HarmonyOS律愈实战07:HealCanvas五音粒子动画

📅 2026/7/5 14:24:48
HarmonyOS律愈实战07:HealCanvas五音粒子动画
HarmonyOS Canvas 实战根据五音生成疗愈粒子动画1. 动画要解决什么问题疗愈类 App 如果只有按钮和列表很难形成沉浸感。“律愈”的做法是在疗愈页加入 Canvas 动画不同五音对应不同色相、粒子数量和呼吸圆环让音频播放有视觉反馈。2. 组件接口HealCanvas接收两个参数Componentexportstruct HealCanvas{PropWatch(onToneChanged)toneKey:ToneKeygong;Propanimate:booleantrue;}toneKey当前五音。animate是否播放动画。当音色变化时组件会重新生成粒子onToneChanged():void{this.reseedParticles();}3. 粒子模型每个粒子记录位置、速度、透明度、色相等信息interfaceParticle{x:number;y:number;size:number;speedX:number;speedY:number;opacity:number;hue:number;isWave:boolean;}isWave用于决定是否绘制扩散波纹。4. 五音映射到色相项目用hueForTone()把五音映射到 HSL 色相functionhueForTone(tone:ToneKey):number{switch(tone){casejiao:return140;casezhi:return15;casegong:return38;caseshang:return220;caseyu:return210;default:return38;}}这种写法的好处是绘制粒子时可以用 HSL 轻松做同色系变化fillStylehsla(${p.hue},60%,70%,${p.opacity});5. 根据音色重新播种粒子不同音色的粒子数量不同privatereseedParticles():void{if(this.wPx0||this.hPx0){return;}constmetawuYinMetaOf(this.toneKey);constcountmeta.keyyu?120:meta.keyzhi?100:meta.keyjiao?80:meta.keyshang?70:60;constlist:Particle[][];consthuehueForTone(this.toneKey);for(leti0;icount;i){list.push({x:Math.random()*this.wPx,y:Math.random()*this.hPx,size:Math.random()*2.51,speedX:(Math.random()-0.5)*0.5,speedY:-Math.random()*0.8-0.2,opacity:Math.random()*0.50.1,hue:hue(Math.random()*10-5),isWave:Math.random()0.72});}this.particleslist;}这里不是所有音色都画同样粒子而是让视觉强弱跟音色有关。6. 动画循环动画使用定时器privatestartLoop():void{this.stopLoop();if(!this.animate){return;}this.timerIdsetInterval(():void{this.drawFrame();},36)asnumber;}36ms 大约是 27 FPS适合背景氛围动画。如果追求更细腻可以调小间隔如果担心功耗可以调大间隔。7. 绘制一帧每帧绘制分三步半透明底色覆盖。径向渐变营造光晕。更新并绘制粒子和呼吸圆环。privatedrawFrame():void{constctxthis.ctx;constmetawuYinMetaOf(this.toneKey);ctx.fillStylergba(245,234,214,0.18);ctx.fillRect(0,0,this.wPx,this.hPx);constgradctx.createRadialGradient(this.wPx/2,this.hPx/2,0,this.wPx/2,this.hPx/2,this.wPx*0.72);grad.addColorStop(0,meta.glow);grad.addColorStop(1,rgba(245,234,214,0));ctx.fillStylegrad;ctx.fillRect(0,0,this.wPx,this.hPx);}呼吸圆环通过正弦函数实现缩放constbreatheMath.sin(now*0.0008)*0.151;ctx.arc(cx,cy,radius*breathe,0,Math.PI*2);8. 生命周期处理Canvas 组件必须处理尺寸变化和消失Canvas(this.ctx).width(100%).height(100%).onReady((){this.reseedParticles();}).onDisAppear((){this.stopLoop();}).onAreaChange((oldVal:Area,newVal:Area){this.wPxNumber(newVal.width);this.hPxNumber(newVal.height);if(this.wPx0this.hPx0){this.reseedParticles();if(this.timerId0this.animate){this.startLoop();}}});onDisAppear()停止定时器非常关键否则页面切换后动画仍可能在后台跑。9. 动画流程图Canvas onAreaChange记录宽高reseedParticlesstartLoopdrawFrame更新粒子位置绘制光晕和圆环toneKey 改变组件消失stopLoop10. 小结这个 Canvas 组件的价值在于它没有把动画写成装饰而是和五音数据模型联动。音色变化会影响颜色、光晕、粒子密度和文字展示。如果你正在做 HarmonyOS 应用Canvas 不一定只用于游戏。像音乐、冥想、运动、阅读这类产品也可以用轻量 Canvas 背景提升沉浸感。9. 粒子动画为什么使用半透明覆盖drawFrame() 每帧没有完全清屏而是用半透明背景覆盖s ctx.fillStyle rgba(245,234,214,0.18); ctx.fillRect(0, 0, this.wPx, this.hPx);这会留下轻微拖影让粒子运动更柔和。疗愈类场景不适合太硬的运动轨迹半透明覆盖正好能制造缓慢扩散的感觉。10. 色彩来自五音元数据画布光晕读取 meta.glows const meta wuYinMetaOf(this.toneKey); const grad ctx.createRadialGradient( this.wPx / 2, this.hPx / 2, 0, this.wPx / 2, this.hPx / 2, this.wPx * 0.72 ); grad.addColorStop(0, meta.glow); grad.addColorStop(1, rgba(245,234,214,0));这说明视觉主题不是写死在 Canvas 里而是从数据模型继承。以后改某个音色的主色首页、卡片、Canvas 都能同步调整。11. 粒子越界回收粒子向上飘出画布后不删除重建而是移动到底部s if (p.y -20) { p.y this.hPx 20; p.x Math.random() * this.wPx; }这种复用比频繁创建对象更稳定适合长时间播放的背景动画。12. Canvas 生命周期清理组件消失时停止循环s .onDisAppear(() { this.stopLoop(); })这段看起来不起眼但非常关键。疗愈页切到其他 Tab 后如果定时器继续跑会浪费资源也可能造成页面状态异常。13. 性能优化建议粒子数量根据设备档位调整后台或页面不可见时停止动画避免每帧创建大量对象文字绘制可以降低频率或拆出静态层帧率不必追求 60 FPS氛围动画 24-30 FPS 足够