RefreshLoadLayout.ets 5.39 KB
import lottie, { AnimationItem } from '@ohos/lottie';
import { LoadStatus, RefreshLayoutBean } from './RefreshLayoutBean';

/**
 * Custom layout to show refresh or load.
 * TODO 待优化
 */
@Component
export default struct CustomLayout {
  // 设置刷新view高度
  static readonly REFRESH_HEIGHT: number = 90;
  @ObjectLink @Watch('onOffsetChange') refreshBean: RefreshLayoutBean;
  private mainRenderingSettings: RenderingContextSettings = new RenderingContextSettings(true)
  private mainCanvasRenderingContext: CanvasRenderingContext2D =
    new CanvasRenderingContext2D(this.mainRenderingSettings)
  private mainRenderingSettings2: RenderingContextSettings = new RenderingContextSettings(true)
  private mainCanvasRenderingContext2: CanvasRenderingContext2D =
    new CanvasRenderingContext2D(this.mainRenderingSettings2)
  private animateItem: AnimationItem | null = null;
  private animateItem2: AnimationItem | null = null;
  private animateName: string = "refresh";
  private animateName2: string = "refreshing";
  @State private layoutHeight: number = 0;
  @State private textVisible: boolean = false

  build() {
    Stack({ alignContent: Alignment.Center }) {
      Canvas(this.mainCanvasRenderingContext)
        .width(60)
        .height(60)
        .backgroundColor(Color.Transparent)
        .onReady(() => {
          // 可在此生命回调周期中加载动画,可以保证动画尺寸正确
          //抗锯齿的设置
          this.mainCanvasRenderingContext.imageSmoothingEnabled = true;
          this.mainCanvasRenderingContext.imageSmoothingQuality = 'medium'
        })
        .onDisAppear(() => {
          lottie.destroy(this.animateName);
        })
        .visibility(this.refreshBean.loadStatus === LoadStatus.PRELOAD ? Visibility.Visible : Visibility.Hidden)

      Canvas(this.mainCanvasRenderingContext2)
        .width(60)
        .height(60)
        .backgroundColor(Color.Transparent)
        .onReady(() => {
          // 可在此生命回调周期中加载动画,可以保证动画尺寸正确
          //抗锯齿的设置
          this.mainCanvasRenderingContext2.imageSmoothingEnabled = true;
          this.mainCanvasRenderingContext2.imageSmoothingQuality = 'medium'
        })
        .onDisAppear(() => {
          lottie.destroy(this.animateName2);
        })
        .visibility(this.refreshBean.loadStatus === LoadStatus.LOADING ? Visibility.Visible : Visibility.Hidden)

      Text('已更新至最新')
        .fontSize(14)
        .textAlign(TextAlign.Center)
        .fontColor('#676767')
        .backgroundColor('#f6f6f6')
        .borderRadius(20)
        .padding({
          left: 19,
          right: 19,
          top: 10,
          bottom: 10
        })
        .visibility(this.textVisible ? Visibility.Visible : Visibility.Hidden)

    }
    .clip(true)
    .width('100%')
    .height(this.layoutHeight)
  }

  animate1(offset: number) {
    if (this.animateItem == null) {
      this.animateItem = lottie.loadAnimation({
        container: this.mainCanvasRenderingContext,
        renderer: 'canvas', // canvas 渲染模式
        loop: 1,
        autoplay: true,
        name: this.animateName,
        path: "lottie/refresh_step1.json", // 路径加载动画只支持entry/src/main/ets 文件夹下的相对路径
      })
      this.animateItem.goToAndStop(1)
    }
    let total = CustomLayout.REFRESH_HEIGHT
    let progress = offset * 100 / total
    this.animateItem?.goToAndStop(this.getFramesByProgress(progress), true);
  }

  animate2() {
    if (this.animateItem2 == null) {
      this.animateItem2 = lottie.loadAnimation({
        container: this.mainCanvasRenderingContext2,
        renderer: 'canvas', // canvas 渲染模式
        loop: 10,
        autoplay: true,
        name: this.animateName2,
        path: "lottie/refresh_step2.json", // 路径加载动画只支持entry/src/main/ets 文件夹下的相对路径
      })
    }
  }

  getFramesByProgress(progress: number): number {
    if (this.animateItem == null) {
      return 1;
    }
    let progressTmp = progress
    let total = this.animateItem.totalFrames;
    let frame = Math.floor(total * progressTmp / 100);
    if (frame >= total - 1) {
      frame = total - 1
    }
    return frame;
  }

  onOffsetChange() {
    if (!this.refreshBean.isVisible) {
      return
    }
    if (this.refreshBean.loadStatus === LoadStatus.PRELOAD) {
      // 下拉刷新
      this.animate1(this.refreshBean.offset)
    } else if (this.refreshBean.loadStatus == LoadStatus.LOADING) {
      // 正在刷新
      this.animate2()
    } else {
      // 刷新完成
      lottie.destroy()
    }
    let maxH = CustomLayout.REFRESH_HEIGHT
    let tmpHeight = this.refreshBean.offset > maxH ? maxH : this.refreshBean.offset
    if (this.refreshBean.loadStatus === LoadStatus.LOADED) {
      this.textVisible = true
      if (tmpHeight <= 0) {
        setTimeout(() => {
          // 延时设置0,让“已更新到最新”展示
          this.textVisible = false
          this.setHeight0WithAnimation()
        }, 800)
      }
    } else {
      // 直接设置高度
      this.layoutHeight = tmpHeight
    }
  }

  /**
   * 下拉刷新UI,高度慢慢变为0
   */
  setHeight0WithAnimation() {
    // this.layoutHeight = 0
    animateTo({
      duration: 500, // 动画时长
      curve: Curve.Linear, // 动画曲线
      iterations: 1, // 播放次数
      playMode: PlayMode.Normal, // 动画模式
    }, () => {
      this.layoutHeight = 0
    })
  }
}