HarmonyOS 鸿蒙Next刷新动画

HarmonyOS 鸿蒙Next刷新动画

Refresh组件如何实现下拉刷新动画

3 回复

【背景知识】

  • Refresh组件是可以进行页面下拉操作并显示刷新动效的容器组件,它可以采用animateTo显式动画来自定义动画效果。
  • Lottie是一个适用于OpenHarmony的动画库,它可以解析json格式的动画,并在移动设备上进行本地渲染,以此来实现动画的播放、暂停等操作。

【解决方案】

方案一:使用Refresh组件自身属性实现,可以参考官网Refresh实现下拉刷新动画示例一

方案二:使用Refresh组件结合Lottie实现。具体步骤如下:

  1. 引入必要的组件和配置:首先,确保项目中已经引入了Lottie动画库。在项目地址中打开终端,输入以下命令安装组件:

    ohpm install [@ohos](/user/ohos)/lottie
    
  2. 引入lottie.json自定义动画文件:json动画文件可以参考json动画文件,在resources/rawfile文件夹下放入动画文件即可,例如在rawfile文件夹下创建common/lottie/animation.json动画文件。

  3. 加载并配置Lottie动画:使用Lottie来配置下拉时的动画效果。例如:

    loadPullDownAnimation() {
      this.animateItem = lottie.loadAnimation({
        container: this.context, // 渲染上下文
        renderer: 'canvas', // canvas渲染模式
        loop: 10, // 是否循环播放,默认true
        autoplay: true, // 是否自动播放,默认true
        name: this.animateName,
        contentMode: 'Contain',
        path: 'common/lottie/animation.json', // json路径
      })
    }
    
  4. 在Refresh组件中使用onStateChangeonRefreshing等事件来配置下拉时不同状态下的动画效果。完整示例代码如下:

    import lottie, { AnimationItem } from '[@ohos](/user/ohos)/lottie'
    
    [@Entry](/user/Entry)
    [@Component](/user/Component)
    struct RefreshExample {
      @State isRefreshing: boolean = false
      @State arr: String[] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
      private mainCanvasRenderingContext: CanvasRenderingContext2D = new CanvasRenderingContext2D()
      private animateItem: AnimationItem | null = null
      private animateName: string = 'pullDownAnimate'
      private setting: RenderingContextSettings = new RenderingContextSettings(true);
      private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.setting);
      private lottieName: string = 'lottie_data';
    
      loadPullDownAnimation() {
        this.animateItem = lottie.loadAnimation({
          container: this.context, // 渲染上下文
          renderer: 'canvas', // canvas渲染模式
          loop: 10, // 是否循环播放,默认true
          autoplay: true, // 是否自动播放,默认true
          name: this.animateName,
          contentMode: 'Contain',
          path: 'common/lottie/animation.json', // json路径
        })
      }
    
      @Builder
      customRefreshComponent() {
        Stack() {
          Row() {
            Canvas(this.context)
              .width('50%')
              .backgroundColor('#aabbcc')
              .onDisAppear(() => {
                lottie.destroy(this.lottieName) // Canvas销毁时顺带销毁lottie动画
              })
              .onReady(() => {
                if (this.mainCanvasRenderingContext) {
                  this.mainCanvasRenderingContext.imageSmoothingEnabled = true;
                  this.mainCanvasRenderingContext.imageSmoothingQuality = 'medium';
                  this.loadPullDownAnimation();
                } else {
                  console.error('mainCanvasRenderingContext is not initialized');
                }
              })
          }
          .alignItems(VerticalAlign.Center)
        }
        .align(Alignment.Center)
        .clip(true)
        // 设置最小高度约束保证自定义组件高度随刷新区域高度变化时自定义组件高度不会低于minHeight
        .constraintSize({ minHeight: 32 })
        .width('100%')
      }
    
      build() {
        Column() {
          Refresh({ refreshing: $$this.isRefreshing, builder: this.customRefreshComponent() }) {
            List() {
              ForEach(this.arr, (item: string) => {
                ListItem() {
                  Text('' + item)
                    .width('70%')
                    .height(80)
                    .fontSize(16)
                    .margin(10)
                    .textAlign(TextAlign.Center)
                    .borderRadius(10)
                    .backgroundColor(0xFFFFFF)
                }
              }, (item: string) => item)
            }
            .onScrollIndex((first: number) => {
              console.info(first.toString())
            })
            .width('100%')
            .height('100%')
            .alignListItem(ListItemAlign.Center)
            .scrollBar(BarState.Off)
          }
          .backgroundColor(0x89CFF0)
          .pullToRefresh(true)
          .refreshOffset(64)
          .onStateChange((refreshStatus: RefreshStatus) => {
            console.info(`Refresh onStatueChange state is ${refreshStatus}`)
            if (refreshStatus === 0) { // 未下拉
              this.animateItem!.destroy()
              this.animateItem = null
            }
            if (refreshStatus === 1) { // 下拉中
              this.loadPullDownAnimation()
            }
            if (refreshStatus === 3) { // 刷新中
              this.animateItem?.play();
            }
            if (refreshStatus === 4) { // 刷新结束
              setTimeout(() => {
                this.animateItem!.destroy()
                this.animateItem = null
              }, 75)
            }
          })
          .onRefreshing(() => {
            setTimeout(() => {
              this.isRefreshing = false
            }, 2000)
            console.info(`onRefreshing test`)
          })
        }
      }
    }
    

【总结】

方案一、二都是通过Refresh组件实现下拉刷新动画,但实现方法略有不同,区别如下表格:

方案 特点
方案一 通过Refresh组件的通用属性来实现动画效果。
方案二 通过引入动画库来实现自定义动画效果。

常见场景如下:

  • 工具类应用或功能型应用,如手机的电话簿。
  • 社交媒体应用或视频播放应用,如抖音。

更多关于HarmonyOS 鸿蒙Next刷新动画的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


HarmonyOS Next的刷新动画采用ArkTS语言开发,基于声明式UI架构实现。动画系统通过属性动画和转场动画组件驱动,支持弹性曲线、插值计算等动态效果。系统内置了PageTransitionEnter和PageTransitionExit等转场接口,可配置平移、缩放、透明度变化的组合效果。刷新动画的帧率与系统渲染管线协同,确保60fps以上的流畅度。开发者可通过修改动画参数和曲线函数调整动画节奏,所有动画执行均在ArkUI引擎内完成。

在HarmonyOS Next中,可通过Refresh组件实现下拉刷新动画。以下是核心实现步骤:

  1. 使用Refresh组件
    在布局中引入Refresh组件,作为列表或滚动内容的容器:

    Refresh({ refreshing: this.isRefreshing }) {
      List({ space: 10 }) {
        // 列表内容
      }
    }
    .onStateChange((isRefreshing: boolean) => {
      // 监听刷新状态变化
    })
    
  2. 控制刷新状态
    通过refreshing参数控制动画的显示与隐藏。触发刷新时设为true,数据加载完成后设为false

    [@State](/user/State) isRefreshing: boolean = false;
    
    // 触发刷新
    loadData() {
      this.isRefreshing = true;
      // 模拟数据加载
      setTimeout(() => {
        this.isRefreshing = false;
      }, 2000);
    }
    
  3. 自定义动画样式
    可通过progressIndicator属性自定义下拉指示器,结合Canvas绘制动画或使用Lottie加载复杂动效:

    Refresh({ 
      refreshing: this.isRefreshing,
      progressIndicator: this.customIndicator 
    })
    
  4. 监听手势事件
    利用onStateChange回调处理下拉过程中的状态变化(如拖动距离、释放判定),实现更流畅的交互反馈。

注意:需确保Refresh组件嵌套在可滚动容器内,且遵循单向数据流更新状态。

回到顶部