IT评测·应用市场-qidao123.com技术社区

标题: 鸿蒙特效教程03-水波纹动画 [打印本页]

作者: 用多少眼泪才能让你相信    时间: 2025-4-12 16:46
标题: 鸿蒙特效教程03-水波纹动画
鸿蒙特效教程03-水波纹动画效果实现教程

   本教程得当HarmonyOS初学者,通过简单到复杂的步骤,一步步实现美丽的水波纹动画效果。
  开发环境预备


最终效果预览

我们将实现以下功能:


一、创建基础布局

首先,我们需要创建一个基础页面布局。这个布局包含一个占满屏幕的地域,用于展示水波纹动画。
  1. @Entry
  2. @Component
  3. struct WaterRipple {
  4.   build() {
  5.     Column() {
  6.       // 水波纹动画效果区域
  7.       Stack() {
  8.         // 提示文本
  9.         Column() {
  10.           Text('点击屏幕任意位置产生水波纹')
  11.             .fontSize(16)
  12.             .fontColor('#333')
  13.             .margin({ top: 20 })
  14.         }
  15.         .width('100%')
  16.         .height('100%')
  17.         .justifyContent(FlexAlign.Center)
  18.         .alignItems(HorizontalAlign.Center)
  19.       }
  20.       .width('100%')
  21.       .height('100%')
  22.     }
  23.     .width('100%')
  24.     .height('100%')
  25.     .backgroundColor('#EDEDED')
  26.     .expandSafeArea()
  27.   }
  28. }
复制代码
这段代码创建了一个简单的页面,包含以下元素:

Stack组件很紧张,由于我们后续要在这里动态添加水波纹元素。
二、界说水波纹数据布局

在实现动画效果之前,我们需要先界说水波纹的数据布局。每个水波纹都有本身的位置、巨细、颜色等属性:
  1. // 水波纹项目类型定义
  2. interface RippleItem {
  3.   id: number      // 唯一标识
  4.   x: number       // 中心点X坐标
  5.   y: number       // 中心点Y坐标
  6.   size: number    // 当前大小
  7.   maxSize: number // 最大大小
  8.   opacity: number // 透明度
  9.   color: string   // 颜色
  10. }
  11. @Entry
  12. @Component
  13. struct WaterRipple {
  14.   // 水波纹数组,用于存储所有活动的水波纹
  15.   @State ripples: RippleItem[] = []
  16.   
  17.   // 触摸状态
  18.   @State isTouching: boolean = false
  19.   // 波纹生成定时器
  20.   private rippleTimer: number = -1
  21.   // 当前触摸位置
  22.   private touchX: number = 0
  23.   private touchY: number = 0
  24.   // 波纹生成间隔(毫秒)
  25.   private readonly rippleInterval: number = 200
  26.   
  27.   // 其余代码保持不变...
  28. }
复制代码
使用@State装饰器标志ripples数组和isTouching状态,这样当它们的内容发生变革时,UI会自动更新。我们还界说了一些用于跟踪触摸状态和位置的变量。
三、实现波纹绘制与点击事件

接下来,我们需要实现波纹的绘制,并处理基本的点击事件:
  1. @Entry
  2. @Component
  3. struct WaterRipple {
  4.   // 前面定义的属性不变...
  5.   build() {
  6.     Column() {
  7.       // 水波纹动画效果
  8.       Stack() {
  9.         // 水波纹展示区域
  10.         Column() {
  11.           Text('点击屏幕任意位置产生水波纹')
  12.             .fontSize(16)
  13.             .fontColor('#333')
  14.             .margin({ top: 20 })
  15.           Text('触摸并滑动,波纹会跟随手指')
  16.             .fontSize(16)
  17.             .fontColor('#333')
  18.             .margin({ top: 10 })
  19.         }
  20.         .width('100%')
  21.         .height('100%')
  22.         .justifyContent(FlexAlign.Center)
  23.         .alignItems(HorizontalAlign.Center)
  24.         // 绘制所有波纹
  25.         ForEach(this.ripples, (item: RippleItem) => {
  26.           Circle()
  27.             .fill(item.color)
  28.             .width(item.size)
  29.             .height(item.size)
  30.             .opacity(item.opacity)
  31.             .position({ x: item.x - item.size / 2, y: item.y - item.size / 2 })
  32.         })
  33.       }
  34.       .width('100%')
  35.       .height('100%')
  36.       // 触摸事件处理将在后面添加
  37.     }
  38.     // 其余代码保持不变...
  39.   }
  40.   // 创建水波纹的方法(暂未实现)
  41.   createRipple(x: number, y: number) {
  42.     console.info(`点击位置: x=${x}, y=${y}`)
  43.   }
  44. }
复制代码
在这一步中,我们添加了以下内容:
注意Circle组件的定位方式:由于圆形是以左上角为基准定位的,而我们希望以圆心定位,以是需要从坐标中减去圆形半径(即size/2)。
四、实现水波纹创建逻辑

下一步,我们来实现水波纹的创建逻辑:
  1. // 创建一个新的水波纹
  2. createRipple(x: number, y: number) {
  3.   // 创建随机颜色
  4.   const colors = ['#2196F3', '#03A9F4', '#00BCD4', '#4CAF50', '#8BC34A']
  5.   const color = colors[Math.floor(Math.random() * colors.length)]
  6.   // 创建新波纹
  7.   const newRipple: RippleItem = {
  8.     id: Date.now(),  // 使用时间戳作为唯一标识
  9.     x: x,            // X坐标
  10.     y: y,            // Y坐标
  11.     size: 0,         // 初始大小为0
  12.     maxSize: 300,    // 最大扩散到300像素
  13.     opacity: 0.6,    // 初始透明度
  14.     color: color     // 随机颜色
  15.   }
  16.   // 添加到波纹数组
  17.   this.ripples.push(newRipple)
  18.   // 启动波纹动画
  19.   this.animateRipple(newRipple)
  20. }
复制代码
在这个方法中:
五、实现动画效果

如今,我们来实现波纹的扩散和消失动画:
  1. // 动画处理波纹的扩散和消失
  2. animateRipple(ripple: RippleItem) {
  3.   let animationStep = 0
  4.   const totalSteps = 60        // 总动画帧数
  5.   const intervalTime = 16       // 每帧间隔时间(约60fps)
  6.   const sizeStep = ripple.maxSize / totalSteps  // 每帧增加的尺寸
  7.   const opacityStep = ripple.opacity / totalSteps  // 每帧减少的透明度
  8.   const timer = setInterval(() => {
  9.     animationStep++
  10.     // 更新波纹状态
  11.     const index = this.ripples.findIndex(item => item.id === ripple.id)
  12.     if (index !== -1) {
  13.       // 增加大小
  14.       this.ripples[index].size += sizeStep
  15.       // 降低透明度
  16.       this.ripples[index].opacity -= opacityStep
  17.       // 更新状态触发重绘
  18.       this.ripples = [...this.ripples]
  19.       // 动画结束,移除波纹
  20.       if (animationStep >= totalSteps) {
  21.         clearInterval(timer)
  22.         this.ripples = this.ripples.filter(item => item.id !== ripple.id)
  23.       }
  24.     } else {
  25.       // 波纹已被其他方式移除
  26.       clearInterval(timer)
  27.     }
  28.   }, intervalTime)
  29. }
复制代码
这个方法使用了setInterval定时器来创建动画,主要逻辑包括:
至此,我们已经实现了基本的水波纹效果。下一步将添加触摸滑动功能。
六、实现触摸跟踪功能

最后,我们来实现触摸跟踪功能,让波纹可以或许跟随手指移动:
  1. .onTouch((event: TouchEvent) => {
  2.   // 获取当前触摸点坐标
  3.   this.touchX = event.touches[0].x
  4.   this.touchY = event.touches[0].y
  5.   
  6.   // 根据触摸状态处理
  7.   switch (event.type) {
  8.     case TouchType.Down:
  9.       // 开始触摸,立即创建一个波纹
  10.       this.isTouching = true
  11.       this.createRipple(this.touchX, this.touchY)
  12.       
  13.       // 启动定时器连续生成波纹
  14.       if (this.rippleTimer === -1) {
  15.         this.rippleTimer = setInterval(() => {
  16.           if (this.isTouching) {
  17.             // 添加小偏移让效果更自然
  18.             const offsetX = Math.random() * 10 - 5
  19.             const offsetY = Math.random() * 10 - 5
  20.             this.createRipple(this.touchX + offsetX, this.touchY + offsetY)
  21.           }
  22.         }, this.rippleInterval)
  23.       }
  24.       break
  25.       
  26.     case TouchType.Move:
  27.       // 移动时保持触摸状态,波纹会在定时器中根据新坐标创建
  28.       this.isTouching = true
  29.       break
  30.       
  31.     case TouchType.Up:
  32.     case TouchType.Cancel:
  33.       // 触摸结束,停止生成波纹
  34.       this.isTouching = false
  35.       if (this.rippleTimer !== -1) {
  36.         clearInterval(this.rippleTimer)
  37.         this.rippleTimer = -1
  38.       }
  39.       break
  40.   }
  41. })
复制代码
在这段代码中,我们使用onTouch事件监听器来处理触摸事件,主要功能包括:
这样实现后,当用户触摸并滑动屏幕时,波纹会及时跟随手指位置生成,创造出一种水流般的视觉效果。
完备代码

下面是最终的完备代码:
  1. // 水波纹项目类型定义
  2. interface RippleItem {
  3.   id: number // 唯一标识
  4.   x: number // 中心点X坐标
  5.   y: number // 中心点Y坐标
  6.   size: number // 当前大小
  7.   maxSize: number // 最大大小
  8.   opacity: number // 透明度
  9.   color: string // 颜色
  10. }
  11. @Entry
  12. @Component
  13. struct WaterRipple {
  14.   // 水波纹数组
  15.   @State ripples: RippleItem[] = []
  16.   // 触摸状态
  17.   @State isTouching: boolean = false
  18.   // 波纹生成定时器
  19.   private rippleTimer: number = -1
  20.   // 当前触摸位置
  21.   private touchX: number = 0
  22.   private touchY: number = 0
  23.   // 波纹生成间隔(毫秒)
  24.   private readonly rippleInterval: number = 200
  25.   build() {
  26.     Column() {
  27.       // 水波纹动画效果
  28.       Stack() {
  29.         // 水波纹展示区域
  30.         Column() {
  31.           Text('点击屏幕任意位置产生水波纹')
  32.             .fontSize(16)
  33.             .fontColor('#333')
  34.             .margin({ top: 20 })
  35.           Text('触摸并滑动,波纹会跟随手指')
  36.             .fontSize(16)
  37.             .fontColor('#333')
  38.             .margin({ top: 10 })
  39.         }
  40.         .width('100%')
  41.         .height('100%')
  42.         .justifyContent(FlexAlign.Center)
  43.         .alignItems(HorizontalAlign.Center)
  44.         // 绘制所有波纹
  45.         ForEach(this.ripples, (item: RippleItem) => {
  46.           Circle()
  47.             .fill(item.color)
  48.             .width(item.size)
  49.             .height(item.size)
  50.             .opacity(item.opacity)
  51.             .position({ x: item.x - item.size / 2, y: item.y - item.size / 2 })
  52.         })
  53.       }
  54.       .width('100%')
  55.       .height('100%')
  56.       .onTouch((event: TouchEvent) => {
  57.         // 获取当前触摸点坐标
  58.         this.touchX = event.touches[0].x
  59.         this.touchY = event.touches[0].y
  60.         
  61.         // 根据触摸状态处理
  62.         switch (event.type) {
  63.           case TouchType.Down:
  64.             // 开始触摸,立即创建一个波纹
  65.             this.isTouching = true
  66.             this.createRipple(this.touchX, this.touchY)
  67.             
  68.             // 启动定时器连续生成波纹
  69.             if (this.rippleTimer === -1) {
  70.               this.rippleTimer = setInterval(() => {
  71.                 if (this.isTouching) {
  72.                   // 添加小偏移让效果更自然
  73.                   const offsetX = Math.random() * 10 - 5
  74.                   const offsetY = Math.random() * 10 - 5
  75.                   this.createRipple(this.touchX + offsetX, this.touchY + offsetY)
  76.                 }
  77.               }, this.rippleInterval)
  78.             }
  79.             break
  80.             
  81.           case TouchType.Move:
  82.             // 移动时保持触摸状态,波纹会在定时器中根据新坐标创建
  83.             this.isTouching = true
  84.             break
  85.             
  86.           case TouchType.Up:
  87.           case TouchType.Cancel:
  88.             // 触摸结束,停止生成波纹
  89.             this.isTouching = false
  90.             if (this.rippleTimer !== -1) {
  91.               clearInterval(this.rippleTimer)
  92.               this.rippleTimer = -1
  93.             }
  94.             break
  95.         }
  96.       })
  97.     }
  98.     .width('100%')
  99.     .height('100%')
  100.     .backgroundColor('#EDEDED')
  101.     .expandSafeArea()
  102.   }
  103.   // 创建一个新的水波纹
  104.   createRipple(x: number, y: number) {
  105.     // 创建随机颜色
  106.     const colors = ['#2196F3', '#03A9F4', '#00BCD4', '#4CAF50', '#8BC34A']
  107.     const color = colors[Math.floor(Math.random() * colors.length)]
  108.     // 创建新波纹
  109.     const newRipple: RippleItem = {
  110.       id: Date.now(),
  111.       x: x,
  112.       y: y,
  113.       size: 0,
  114.       maxSize: 300,
  115.       opacity: 0.6,
  116.       color: color
  117.     }
  118.     // 添加到波纹数组
  119.     this.ripples.push(newRipple)
  120.     // 启动波纹动画
  121.     this.animateRipple(newRipple)
  122.   }
  123.   // 动画处理波纹的扩散和消失
  124.   animateRipple(ripple: RippleItem) {
  125.     let animationStep = 0
  126.     const totalSteps = 60
  127.     const intervalTime = 16 // 约60fps
  128.     const sizeStep = ripple.maxSize / totalSteps
  129.     const opacityStep = ripple.opacity / totalSteps
  130.     const timer = setInterval(() => {
  131.       animationStep++
  132.       // 更新波纹状态
  133.       const index = this.ripples.findIndex(item => item.id === ripple.id)
  134.       if (index !== -1) {
  135.         // 增加大小
  136.         this.ripples[index].size += sizeStep
  137.         // 降低透明度
  138.         this.ripples[index].opacity -= opacityStep
  139.         // 更新状态触发重绘
  140.         this.ripples = [...this.ripples]
  141.         // 动画结束,移除波纹
  142.         if (animationStep >= totalSteps) {
  143.           clearInterval(timer)
  144.           this.ripples = this.ripples.filter(item => item.id !== ripple.id)
  145.         }
  146.       } else {
  147.         // 波纹已被其他方式移除
  148.         clearInterval(timer)
  149.       }
  150.     }, intervalTime)
  151.   }
  152. }
复制代码
总结

通过这个教程,我们学习了如何一步步实现水波纹动画效果:
这个简单而雅观的水波纹效果可以应用在你的应用中的各种交互场景,比方按钮点击、图片查看、页面切换等。通过调整参数,你还可以创造出不同风格的波纹效果。
希望这个教程对你有所帮助,祝你在鸿蒙应用开发中创造出更多出色的交互体验!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 IT评测·应用市场-qidao123.com技术社区 (https://dis.qidao123.com/) Powered by Discuz! X3.4