写点什么

鸿蒙 Next 实现仿抖音点赞动画功能

作者:auhgnixgnahz
  • 2025-06-25
    北京
  • 本文字数:2740 字

    阅读完需:约 9 分钟

实现目标: 抖音小视频播放时,双击屏幕会在手指点击位置出现一个小红心,由大变小,然后放大渐变消失,并且点击速度快,可以出现多个小红心动画,每次出现的小红心会有一个小角度的旋转。


实现思路:


1.双击屏幕,在手指点击位置会出现小心心,因此需要监听双击手势,并且获取到点击坐标


2.快速点击,会产生多个小心心的动画,因此需要动态添加 view


3.动画过程,大概是出现后先缩小后放大逐渐消失


实现结果:



实现过程:


1.在需要展示动画的父布局中,添加占位组件 ContentSlot()用于动态添加点赞动画 2.之前一篇介绍了动态添加布局,需要定一个布局,和一个参数对象


Column(){      ContentSlot(this.content)    }    .height('100%')    .width('100%')    .gesture(      TapGesture({ count: 2 })        .onAction((event: GestureEvent) => {          let buildNode = new BuilderNode<[ParamsInterface]>(this.getUIContext());          let redowAngle=(Math.random()*2-1)*50;          buildNode.build(wrapBuilder<[ParamsInterface]>(buildHeartView), {            x: event.fingerList[0].localX,            y: event.fingerList[0].localY,            opacity:1,            scale:1.3,            duration:300,            angle:redowAngle,            finish:()=>{              buildNode.update({                x: event.fingerList[0].localX,                y: event.fingerList[0].localY,                scale:1,                opacity:1,                duration:300,                angle:redowAngle,              })              setTimeout(()=>{                buildNode.update({                  x: event.fingerList[0].localX,                  y: event.fingerList[0].localY,                  scale:3,                  opacity:0,                  duration:500,                  angle:redowAngle,                })              },300)              setTimeout(()=>{                this.content.removeFrameNode(buildNode.getFrameNode())              },1000)            }          }, { nestingBuilderSupported: true });          this.content.addFrameNode(buildNode.getFrameNode());        })    )interface ParamsInterface {  x:number  y:number  finish:() => void  opacity:number  scale:number  duration:number  angle:number}@Builderfunction buildHeartView(params: ParamsInterface) {  Image($r('app.media.heart')).width(40).height(40)    .position({      x: params.x,      y: params.y,    })    .opacity(params.opacity)    .scale({x:params.scale,y:params.scale})    .animation({      duration: params.duration, //动画持续时间,单位为毫秒      curve: Curve.Linear, //动画曲线      iterations: 1, //动画播放次数      playMode: PlayMode.Normal, //动画播放模式 正向    })    .onAppear( params.finish)}
复制代码


4.使用动画,需要对 Image 的属性进行修改,但是 buildHeartView 不支持内部定义参数,所以需要修改 params 的值,这时需要用到 BuilderNode 的 update 方法更新参数。这里不能直接在 buildHeartView 内里修改参数,否侧会报错。例如在 onAppear 中修改透明度的值,会闪退:



实现优化:


这里我们需要展示 2 个动画,需要在主布局中维护想要调整的属性值,后期如果修改或者复用,一点也不方便,如何将动画的控制让 Image 自己控制呢? 主布局中我只要触发动画就行。实现也很简单,只要再定义一个全局的 Component,动画相关的事情让这个子组件去实现,buildHeartView 中引用这个全局 Component。动态节点只需要负责展示定位和角度就可以了。最后加一个动画结束的回调用于移除节点。


最终源码:


import { BuilderNode, NodeContent } from '@kit.ArkUI';
@Entry@ComponentV2struct HeartTest{ content: NodeContent = new NodeContent(); build() { Column(){ ContentSlot(this.content) } .height('100%') .width('100%') .gesture( TapGesture({ count: 2 }) .onAction((event: GestureEvent) => { let buildNode = new BuilderNode<[ParamsInterface]>(this.getUIContext()); buildNode.build(wrapBuilder<[ParamsInterface]>(buildHeartView), { x: event.fingerList[0].localX, y: event.fingerList[0].localY, finish:()=>{ this.content.removeFrameNode(buildNode.getFrameNode()) } }, { nestingBuilderSupported: true }); this.content.addFrameNode(buildNode.getFrameNode());
}) ) }}interface ParamsInterface { x:number y:number finish:() => void}
@Builderfunction buildHeartView(params: ParamsInterface) { HeartImageAnimation({ finish:params.finish}).position({ x: params.x, y: params.y, }) .rotate({angle:(Math.random()*2-1)*50})}@ComponentV2struct HeartImageAnimation{ @Local opacityNor:number=1 @Local scanOption:ScaleOptions={ x:1.3, y:1.3 } @BuilderParam finish:() => void build() { Image($r('app.media.heart')).width(40).height(40) .opacity(this.opacityNor) .scale(this.scanOption).onAppear(()=>{ this.getUIContext().animateTo({ duration: 300, //动画持续时间,单位为毫秒 curve: Curve.EaseOut, //动画曲线 iterations: 1, //动画播放次数 playMode: PlayMode.Normal, //动画播放模式 正向 onFinish: () => { //动画播放完成回调 this.getUIContext().animateTo({ duration: 500, //动画持续时间,单位为毫秒 curve: Curve.EaseOut, //动画曲线 iterations: 1, //动画播放次数 playMode: PlayMode.Normal, //动画播放模式 正向 onFinish: () => { //动画播放完成回调 this.finish } },()=>{ this.scanOption = { x: 3, y: 3, } this.opacityNor = 0 }) } },()=>{ this.scanOption = { x: 1, y: 1, } }) }) }}
复制代码


发布于: 刚刚阅读数: 3
用户头像

auhgnixgnahz

关注

还未添加个人签名 2018-07-10 加入

欢迎关注:HarmonyOS开发笔记

评论

发布
暂无评论
鸿蒙Next实现仿抖音点赞动画功能_鸿蒙Next_auhgnixgnahz_InfoQ写作社区