鸿蒙特效教程03-水波纹动画效果实现教程
本教程得当HarmonyOS初学者,通过简单到复杂的步骤,一步步实现美丽的水波纹动画效果。
开发环境预备
- DevEco Studio 5.0.3
- HarmonyOS Next API 15
最终效果预览
我们将实现以下功能:
- 点击屏幕任意位置,在点击处生成一个水波纹
- 触摸并滑动屏幕,波纹会及时跟随手指位置生成
- 波纹从小到大扩散,同时逐渐消失
- 波纹颜色随机变革,增加视觉多样性
一、创建基础布局
首先,我们需要创建一个基础页面布局。这个布局包含一个占满屏幕的地域,用于展示水波纹动画。
- @Entry
- @Component
- struct WaterRipple {
- build() {
- Column() {
- // 水波纹动画效果区域
- Stack() {
- // 提示文本
- Column() {
- Text('点击屏幕任意位置产生水波纹')
- .fontSize(16)
- .fontColor('#333')
- .margin({ top: 20 })
- }
- .width('100%')
- .height('100%')
- .justifyContent(FlexAlign.Center)
- .alignItems(HorizontalAlign.Center)
- }
- .width('100%')
- .height('100%')
- }
- .width('100%')
- .height('100%')
- .backgroundColor('#EDEDED')
- .expandSafeArea()
- }
- }
复制代码 这段代码创建了一个简单的页面,包含以下元素:
- 最外层是一个占满整个屏幕的Column
- 内部是一个Stack组件,它允许我们将多个元素堆叠在一起
- 在Stack中放置了一个包含提示文本的Column
Stack组件很紧张,由于我们后续要在这里动态添加水波纹元素。
二、界说水波纹数据布局
在实现动画效果之前,我们需要先界说水波纹的数据布局。每个水波纹都有本身的位置、巨细、颜色等属性:
- // 水波纹项目类型定义
- interface RippleItem {
- id: number // 唯一标识
- x: number // 中心点X坐标
- y: number // 中心点Y坐标
- size: number // 当前大小
- maxSize: number // 最大大小
- opacity: number // 透明度
- color: string // 颜色
- }
- @Entry
- @Component
- struct WaterRipple {
- // 水波纹数组,用于存储所有活动的水波纹
- @State ripples: RippleItem[] = []
-
- // 触摸状态
- @State isTouching: boolean = false
- // 波纹生成定时器
- private rippleTimer: number = -1
- // 当前触摸位置
- private touchX: number = 0
- private touchY: number = 0
- // 波纹生成间隔(毫秒)
- private readonly rippleInterval: number = 200
-
- // 其余代码保持不变...
- }
复制代码 使用@State装饰器标志ripples数组和isTouching状态,这样当它们的内容发生变革时,UI会自动更新。我们还界说了一些用于跟踪触摸状态和位置的变量。
三、实现波纹绘制与点击事件
接下来,我们需要实现波纹的绘制,并处理基本的点击事件:
- @Entry
- @Component
- struct WaterRipple {
- // 前面定义的属性不变...
- build() {
- Column() {
- // 水波纹动画效果
- Stack() {
- // 水波纹展示区域
- Column() {
- Text('点击屏幕任意位置产生水波纹')
- .fontSize(16)
- .fontColor('#333')
- .margin({ top: 20 })
- Text('触摸并滑动,波纹会跟随手指')
- .fontSize(16)
- .fontColor('#333')
- .margin({ top: 10 })
- }
- .width('100%')
- .height('100%')
- .justifyContent(FlexAlign.Center)
- .alignItems(HorizontalAlign.Center)
- // 绘制所有波纹
- ForEach(this.ripples, (item: RippleItem) => {
- Circle()
- .fill(item.color)
- .width(item.size)
- .height(item.size)
- .opacity(item.opacity)
- .position({ x: item.x - item.size / 2, y: item.y - item.size / 2 })
- })
- }
- .width('100%')
- .height('100%')
- // 触摸事件处理将在后面添加
- }
- // 其余代码保持不变...
- }
- // 创建水波纹的方法(暂未实现)
- createRipple(x: number, y: number) {
- console.info(`点击位置: x=${x}, y=${y}`)
- }
- }
复制代码 在这一步中,我们添加了以下内容:
- 使用ForEach遍历ripples数组,为每个波纹创建一个Circle组件
- 为提示文本添加了第二行,阐明触摸滑动功能
- 添加了createRipple方法的基本布局(目前只打印坐标)
注意Circle组件的定位方式:由于圆形是以左上角为基准定位的,而我们希望以圆心定位,以是需要从坐标中减去圆形半径(即size/2)。
四、实现水波纹创建逻辑
下一步,我们来实现水波纹的创建逻辑:
- // 创建一个新的水波纹
- createRipple(x: number, y: number) {
- // 创建随机颜色
- const colors = ['#2196F3', '#03A9F4', '#00BCD4', '#4CAF50', '#8BC34A']
- const color = colors[Math.floor(Math.random() * colors.length)]
- // 创建新波纹
- const newRipple: RippleItem = {
- id: Date.now(), // 使用时间戳作为唯一标识
- x: x, // X坐标
- y: y, // Y坐标
- size: 0, // 初始大小为0
- maxSize: 300, // 最大扩散到300像素
- opacity: 0.6, // 初始透明度
- color: color // 随机颜色
- }
- // 添加到波纹数组
- this.ripples.push(newRipple)
- // 启动波纹动画
- this.animateRipple(newRipple)
- }
复制代码 在这个方法中:
- 我们界说了一个颜色数组,每次随机选择一种颜色
- 创建一个新的波纹对象,初始巨细为0,最大扩散尺寸为300像素
- 将新波纹添加到数组中,这会触发UI更新
- 调用animateRipple方法开始动画(下一步实现)
五、实现动画效果
如今,我们来实现波纹的扩散和消失动画:
- // 动画处理波纹的扩散和消失
- animateRipple(ripple: RippleItem) {
- let animationStep = 0
- const totalSteps = 60 // 总动画帧数
- const intervalTime = 16 // 每帧间隔时间(约60fps)
- const sizeStep = ripple.maxSize / totalSteps // 每帧增加的尺寸
- const opacityStep = ripple.opacity / totalSteps // 每帧减少的透明度
- const timer = setInterval(() => {
- animationStep++
- // 更新波纹状态
- const index = this.ripples.findIndex(item => item.id === ripple.id)
- if (index !== -1) {
- // 增加大小
- this.ripples[index].size += sizeStep
- // 降低透明度
- this.ripples[index].opacity -= opacityStep
- // 更新状态触发重绘
- this.ripples = [...this.ripples]
- // 动画结束,移除波纹
- if (animationStep >= totalSteps) {
- clearInterval(timer)
- this.ripples = this.ripples.filter(item => item.id !== ripple.id)
- }
- } else {
- // 波纹已被其他方式移除
- clearInterval(timer)
- }
- }, intervalTime)
- }
复制代码 这个方法使用了setInterval定时器来创建动画,主要逻辑包括:
- 设置动画参数:总步数、帧间隔、每帧尺寸增量和透明度减量
- 每帧更新波纹的巨细和透明度,实现从小到大、从清晰到透明的效果
- 使用[...this.ripples]创建数组的新实例,触发UI更新
- 当动画完成(步数达到总步数)或波纹被移除时,扫除定时器
- 动画结束后从数组中移除该波纹,开释内存
至此,我们已经实现了基本的水波纹效果。下一步将添加触摸滑动功能。
六、实现触摸跟踪功能
最后,我们来实现触摸跟踪功能,让波纹可以或许跟随手指移动:
- .onTouch((event: TouchEvent) => {
- // 获取当前触摸点坐标
- this.touchX = event.touches[0].x
- this.touchY = event.touches[0].y
-
- // 根据触摸状态处理
- switch (event.type) {
- case TouchType.Down:
- // 开始触摸,立即创建一个波纹
- this.isTouching = true
- this.createRipple(this.touchX, this.touchY)
-
- // 启动定时器连续生成波纹
- if (this.rippleTimer === -1) {
- this.rippleTimer = setInterval(() => {
- if (this.isTouching) {
- // 添加小偏移让效果更自然
- const offsetX = Math.random() * 10 - 5
- const offsetY = Math.random() * 10 - 5
- this.createRipple(this.touchX + offsetX, this.touchY + offsetY)
- }
- }, this.rippleInterval)
- }
- break
-
- case TouchType.Move:
- // 移动时保持触摸状态,波纹会在定时器中根据新坐标创建
- this.isTouching = true
- break
-
- case TouchType.Up:
- case TouchType.Cancel:
- // 触摸结束,停止生成波纹
- this.isTouching = false
- if (this.rippleTimer !== -1) {
- clearInterval(this.rippleTimer)
- this.rippleTimer = -1
- }
- break
- }
- })
复制代码 在这段代码中,我们使用onTouch事件监听器来处理触摸事件,主要功能包括:
- 触摸开始(Down)时:
- 记载触摸位置
- 立刻创建一个波纹
- 启动定时器,以固定间隔创建波纹
- 触摸移动(Move)时:
- 更新触摸位置
- 保持触摸状态,定时器会在新位置创建波纹
- 触摸结束(Up)或取消(Cancel)时:
这样实现后,当用户触摸并滑动屏幕时,波纹会及时跟随手指位置生成,创造出一种水流般的视觉效果。
完备代码
下面是最终的完备代码:
- // 水波纹项目类型定义
- interface RippleItem {
- id: number // 唯一标识
- x: number // 中心点X坐标
- y: number // 中心点Y坐标
- size: number // 当前大小
- maxSize: number // 最大大小
- opacity: number // 透明度
- color: string // 颜色
- }
- @Entry
- @Component
- struct WaterRipple {
- // 水波纹数组
- @State ripples: RippleItem[] = []
- // 触摸状态
- @State isTouching: boolean = false
- // 波纹生成定时器
- private rippleTimer: number = -1
- // 当前触摸位置
- private touchX: number = 0
- private touchY: number = 0
- // 波纹生成间隔(毫秒)
- private readonly rippleInterval: number = 200
- build() {
- Column() {
- // 水波纹动画效果
- Stack() {
- // 水波纹展示区域
- Column() {
- Text('点击屏幕任意位置产生水波纹')
- .fontSize(16)
- .fontColor('#333')
- .margin({ top: 20 })
- Text('触摸并滑动,波纹会跟随手指')
- .fontSize(16)
- .fontColor('#333')
- .margin({ top: 10 })
- }
- .width('100%')
- .height('100%')
- .justifyContent(FlexAlign.Center)
- .alignItems(HorizontalAlign.Center)
- // 绘制所有波纹
- ForEach(this.ripples, (item: RippleItem) => {
- Circle()
- .fill(item.color)
- .width(item.size)
- .height(item.size)
- .opacity(item.opacity)
- .position({ x: item.x - item.size / 2, y: item.y - item.size / 2 })
- })
- }
- .width('100%')
- .height('100%')
- .onTouch((event: TouchEvent) => {
- // 获取当前触摸点坐标
- this.touchX = event.touches[0].x
- this.touchY = event.touches[0].y
-
- // 根据触摸状态处理
- switch (event.type) {
- case TouchType.Down:
- // 开始触摸,立即创建一个波纹
- this.isTouching = true
- this.createRipple(this.touchX, this.touchY)
-
- // 启动定时器连续生成波纹
- if (this.rippleTimer === -1) {
- this.rippleTimer = setInterval(() => {
- if (this.isTouching) {
- // 添加小偏移让效果更自然
- const offsetX = Math.random() * 10 - 5
- const offsetY = Math.random() * 10 - 5
- this.createRipple(this.touchX + offsetX, this.touchY + offsetY)
- }
- }, this.rippleInterval)
- }
- break
-
- case TouchType.Move:
- // 移动时保持触摸状态,波纹会在定时器中根据新坐标创建
- this.isTouching = true
- break
-
- case TouchType.Up:
- case TouchType.Cancel:
- // 触摸结束,停止生成波纹
- this.isTouching = false
- if (this.rippleTimer !== -1) {
- clearInterval(this.rippleTimer)
- this.rippleTimer = -1
- }
- break
- }
- })
- }
- .width('100%')
- .height('100%')
- .backgroundColor('#EDEDED')
- .expandSafeArea()
- }
- // 创建一个新的水波纹
- createRipple(x: number, y: number) {
- // 创建随机颜色
- const colors = ['#2196F3', '#03A9F4', '#00BCD4', '#4CAF50', '#8BC34A']
- const color = colors[Math.floor(Math.random() * colors.length)]
- // 创建新波纹
- const newRipple: RippleItem = {
- id: Date.now(),
- x: x,
- y: y,
- size: 0,
- maxSize: 300,
- opacity: 0.6,
- color: color
- }
- // 添加到波纹数组
- this.ripples.push(newRipple)
- // 启动波纹动画
- this.animateRipple(newRipple)
- }
- // 动画处理波纹的扩散和消失
- animateRipple(ripple: RippleItem) {
- let animationStep = 0
- const totalSteps = 60
- const intervalTime = 16 // 约60fps
- const sizeStep = ripple.maxSize / totalSteps
- const opacityStep = ripple.opacity / totalSteps
- const timer = setInterval(() => {
- animationStep++
- // 更新波纹状态
- const index = this.ripples.findIndex(item => item.id === ripple.id)
- if (index !== -1) {
- // 增加大小
- this.ripples[index].size += sizeStep
- // 降低透明度
- this.ripples[index].opacity -= opacityStep
- // 更新状态触发重绘
- this.ripples = [...this.ripples]
- // 动画结束,移除波纹
- if (animationStep >= totalSteps) {
- clearInterval(timer)
- this.ripples = this.ripples.filter(item => item.id !== ripple.id)
- }
- } else {
- // 波纹已被其他方式移除
- clearInterval(timer)
- }
- }, intervalTime)
- }
- }
复制代码 总结
通过这个教程,我们学习了如何一步步实现水波纹动画效果:
- ArkUI 搭建基础布局,创建用于展示水波纹的容器
- @State 界说水波纹数据布局,计划存储和管理波纹的方式
- 实现基本的波纹绘制和触摸事件 onTouch
- 创建水波纹生成逻辑,包括随机颜色 Math.random
- 使用 setInterval 定时器实现波纹扩散和消失的动画效果
- 添加 TouchEvent 触摸跟踪功能,让波纹可以或许跟随手指滑动
这个简单而雅观的水波纹效果可以应用在你的应用中的各种交互场景,比方按钮点击、图片查看、页面切换等。通过调整参数,你还可以创造出不同风格的波纹效果。
希望这个教程对你有所帮助,祝你在鸿蒙应用开发中创造出更多出色的交互体验!
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |