鸿蒙实战:全局动画常量管理 — AppAnimations
鸿蒙实战全局动画常量管理 — AppAnimations前言必读图鸿蒙实战全局动画常量管理 — AppAnimations第 63 篇 运行效果截图HarmonyOS NEXT一个成熟的工程化项目绝不会在代码中硬编码魔法数字——animateTo({ duration: 400 })、setTimeout(200)、curve: Curve.FastOutSlowIn这些数字散落在各个页面中会导致维护噩梦和体验不一致。本项目通过AppAnimations常量类统一管理所有动画时长和曲线本文深入分析这套常量体系的设计思想。图AppAnimations 常量体系——集中管理动画时长与缓动曲线保障全局一致性AppAnimations 常量类时长常量曲线常量MICRO: 80ms最小反馈FAST: 200ms快速响应NORMAL: 300ms标准动画SLOW: 500ms强调动画PAGE_TRANSITION: 280ms页面转场Curve.EaseOut减速进入感Curve.Spring弹性回弹Curve.FastOutSlowInMaterial风格一、为什么需要动画常量管理1.1 没有常量管理的问题// ❌ 硬编码散布在各处——维护噩梦// HomePage.ets: animateTo({ duration: 500 })// ReportDetail: setTimeout(200)// CapturePage: animateTo({ duration: 1500 })// TrendPage: animateTo({ duration: 400 })// ...// 如果设计师说「所有入场动画从 500ms 改成 600ms」// 需要搜索所有页面逐行修改——极易遗漏1.2 引入常量管理后的对比// ✅ 使用 AppAnimations 常量// HomePage.ets: animateTo({ duration: AppAnimations.ENTER_DURATION })// ReportDetail: animateTo({ duration: AppAnimations.ENTER_FAST_DURATION })// ReportDetail: animateTo({ duration: AppAnimations.ENTER_DURATION 100 })// 如果设计师说「入场动画从 500ms 改成 600ms」// 只需要修改 AppAnimations.ENTER_DURATION 600 —— 全局生效对比维度硬编码常量管理一致性页面间可能不同全局统一可维护性改一个值需改 N 处改一个常量即可可读性duration: 500含义不明ENTER_DURATION自描述团队协作新成员难判断数字含义常量名即文档二、AppAnimations 常量体系2.1 完整源码// 文件common/theme/AppAnimations.etsexportclassAppAnimations{// 入场动效 /** 页面/元素标准入场 */staticreadonlyENTER_DURATION:number500/** 元素快速入场 */staticreadonlyENTER_FAST_DURATION:number400/** 元素分段入场如关键词逐字弹出 */staticreadonlyENTER_STAGGER_DURATION:number400/** 入场延迟基数关键词等分段动画的起始等待 */staticreadonlyENTER_STAGGER_BASE_DELAY:number900/** 分段间距 */staticreadonlyENTER_STAGGER_INTERVAL:number80/** 卡片等子元素延迟入场 */staticreadonlyENTER_DELAY_CARD:number200// 页面过渡 staticreadonlyPAGE_TRANSITION_DURATION:number350staticreadonlyPAGE_EXIT_DURATION:number300// Canvas 绘制动画 /** 雷达图展开 */staticreadonlyRADAR_DRAW_DURATION:number1000/** 折线图绘制 */staticreadonlyCHART_DRAW_DURATION:number1200/** 匹配环展开 */staticreadonlyRING_DRAW_DURATION:number1200/** 渐变环入场 */staticreadonlyRING_FADE_DURATION:number600// 交互反馈 /** 按钮按下瞬间 */staticreadonlyPRESS_SCALE_DOWN_DURATION:number80/** 按钮弹起恢复 */staticreadonlyPRESS_SCALE_UP_DURATION:number150/** 按钮缩放下限 */staticreadonlyPRESS_SCALE_TARGET:number0.95// 曲线 staticreadonlyCURVE_SMOOTH:CurveCurve.FastOutSlowInstaticreadonlyCURVE_EASE:CurveCurve.EaseOutstaticreadonlyCURVE_LINEAR:CurveCurve.Linear}2.2 常量分类分类常量数量适用的动画类型入场动效6页面元素入场、分段入场、延迟入场页面过渡2PageTransition 进入/退出Canvas 绘制4雷达图、折线图、匹配环、渐变环交互反馈3按钮按下/弹起、缩放目标值曲线3标准平滑曲线、减速曲线、线性曲线三、常量的使用示例3.1 入场动画// TrendPage.ets - 使用入场常量animateTo({duration:AppAnimations.ENTER_DURATION,// 500mscurve:AppAnimations.CURVE_SMOOTH// FastOutSlowIn},(){this.contentOpacity1this.contentOffset0})// 分段关键字入场setTimeout((){for(leti0;ikeywords.length;i){setTimeout((){animateTo({duration:AppAnimations.ENTER_STAGGER_DURATION,// 400mscurve:AppAnimations.CURVE_EASE},(){this.keywordOpacities[i]1})},i*AppAnimations.ENTER_STAGGER_INTERVAL)// 80ms 间隔}},AppAnimations.ENTER_STAGGER_BASE_DELAY)// 900ms 后开始3.2 页面转场// 所有页面的统一转场pageTransition(){PageTransitionEnter({duration:AppAnimations.PAGE_TRANSITION_DURATION,// 350mscurve:AppAnimations.CURVE_SMOOTH}).slide(SlideEffect.Right)PageTransitionExit({duration:AppAnimations.PAGE_EXIT_DURATION,// 300mscurve:AppAnimations.CURVE_SMOOTH}).slide(SlideEffect.Right)}3.3 按钮反馈// 按压缩放统一使用交互反馈常量.onTouch((e:TouchEvent){if(e.typeTouchType.Down){animateTo({duration:AppAnimations.PRESS_SCALE_DOWN_DURATION// 80ms},(){this.btnScaleAppAnimations.PRESS_SCALE_TARGET// 0.95})}else{animateTo({duration:AppAnimations.PRESS_SCALE_UP_DURATION,// 150mscurve:AppAnimations.CURVE_SMOOTH},(){this.btnScale1.0})}})3.4 Canvas 动画// DuetReportPage.ets - Canvas 绘制// 三个并行动画使用不同的时长常量animateTo({duration:800,curve:AppAnimations.CURVE_SMOOTH},(){this.contentOpacity1})animateTo({duration:AppAnimations.RADAR_DRAW_DURATION,// 1000mscurve:AppAnimations.CURVE_SMOOTH},(){this.drawProgress1})animateTo({duration:800,curve:AppAnimations.CURVE_SMOOTH},(){this.barProgress1})四、常量的语义化命名4.1 命名规范// 命名模式[场景]_[属性]// 场景ENTER / PAGE / RADAR / CHART / RING / PRESS// 属性DURATION / DELAY / INTERVAL / TARGET// 场景定义ENTER_xxx → 入场相关 PAGE_xxx → 页面转场相关 xxx_DRAW_xxx → Canvas 绘制相关 PRESS_xxx → 交互反馈相关 CURVE_xxx → 动画曲线4.2 为什么使用static readonly而不是枚举特性static readonly枚举enum类型编译时常量命名常量集合运行时内联为字面量零开销保留枚举对象扩展性可任意分割和引用定义即固定组合使用✅ENTER_DURATION 100❌ 不能运算选择static readonly是因为我们需要在代码中灵活组合使用如ENTER_DURATION 100枚举不支持数值运算。4.3 常量值的选择依据常量值选择理由ENTER_DURATION500Material Design 标准入场时长PAGE_TRANSITION_DURATION350页面转场标准介于 300-400 之间PRESS_SCALE_DOWN_DURATION80按下反应需极快低于 100ms 无感知延迟PRESS_SCALE_UP_DURATION150弹起略慢以产生弹性感RADAR_DRAW_DURATION10001 秒的 Canvas 绘制舒服的观察节奏ENTER_STAGGER_INTERVAL80逐元素弹出的最佳节奏间隔五、与其他常量类的配合AppAnimations 与 AppColors、AppFonts 等常量类一起构成项目的**设计令牌Design Token**体系// 设计令牌系统中的 4 个常量类import{AppColors}from./AppColors// 颜色令牌import{AppFonts,AppRadius,AppSpacing,AppShadow}from./AppFonts// 字体/间距/阴影令牌import{AppAnimations}from./AppAnimations// 动画令牌// 使用示例一个完整的设计令牌组件Column().padding(AppSpacing.M)// 间距令牌.backgroundColor(AppColors.CARD)// 颜色令牌.borderRadius(AppRadius.L)// 圆角令牌.shadow({radius:AppShadow.CARD_RADIUS})// 阴影令牌.opacity(this.opacity)// 动画由 animateTo 驱动// ↑ 动画时长来自 AppAnimations.ENTER_DURATION常量的实际使用面颜色系统AppColors → 所有颜色属性 ├── 背景色、文字色、边框色 │ 字体系统AppFonts → 文字和间距 ├── fontSize / lineHeight / fontFamily ├── AppRadius / AppSpacing / AppShadow │ 动画系统AppAnimations → 所有动画参数 ├── duration / curve ├── 入场、转场、Canvas、交互六、扩展指南6.1 如何添加新的动画常量// 1. 确定场景分类// 属于「入场」还是「交互」还是新建分类// 2. 确定命名// [场景]_[属性] 模式// 3. 添加常量exportclassAppAnimations{// 拖拽交互 /** 拖拽触发阈值px */staticreadonlyDRAG_THRESHOLD:number10/** 拖拽回弹动画时长 */staticreadonlyDRAG_SNAP_DURATION:number200}6.2 常量值调优流程设计师提出 → 评估影响范围 → 修改常量值 → 全局生效 → 复核所有页面 调整要求 确认涉及哪些页面 改一处即可 各页面体验一致6.3 跨模块共享如果存在多模块HAR/HSP可以将AppAnimations放在共享模块中// share-module 的 oh-package.json5 { name: lulu/theme, main: src/main/ets/index.ets } // index.ets 导出所有令牌 export { AppColors } from ./theme/AppColors export { AppFonts, AppRadius, AppSpacing, AppShadow } from ./theme/AppFonts export { AppAnimations } from ./theme/AppAnimations七、FAQ 常见问题Q1为什么有的地方还是硬编码了项目初期快速迭代时部分代码可能未及时迁移到常量。遇到硬编码数字时遵循**「3 次原则」**——同一个值出现 3 次以上就应该提取为常量。Q2常量的值后期还能改吗可以但需要评估影响范围。修改ENTER_DURATION会影响所有入场动画可能需要设计师复核所有页面。建议在专门的「动画调优」阶段统一处理。Q3不同类型动画的时长可以差多少┌──入场400-500ms │ │ ┌──页面转场300-350ms │ │ │ │ ┌──Canvas 绘制1000-1200ms │ │ │ │ │ │ ┌──按压缩放80-150ms │ │ │ │ 0 200 400 600 800 1000 1200 (ms)时长差异体现了不同动画的权重越重要的动画越长越实时的越短。Q4Curve 常量为什么要单独定义项目中统一使用Curve.FastOutSlowIn作为主曲线但如果将来需要替换比如品牌方指定了自定义缓动函数只需要修改CURVE_SMOOTH一处即可。八、最佳实践实践说明遇到数字就提取为常量不要等出现 3 次再提取第一次就使用常量常量名要自描述ENTER_DURATION比ANI_DUR_1好得多按场景分类注释区块注释标明「入场动效」「页面过渡」等static readonly使用这个而非 enum 以获得更好的运算灵活性出口统一通过主题包或共享模块统一导出避免过度设计不要为只用一次的值创建常量文档化每个常量用 JSDoc 注释标明毫秒值九、注意事项与常见问题9.1 开发注意事项在正式开发前建议按以下步骤完成环境准备与前置检查版本确认检查 DevEco Studio 与 SDK 版本确保满足目标 API Level 要求权限声明在module.json5的requestPermissions字段中提前声明所有需要的系统权限设备能力检查调用前验证设备是否支持目标能力相机、NFC、传感器等异步封装所有耗时操作数据库、文件 I/O、网络请求统一使用async/await处理资源释放在组件aboutToDisappear()生命周期钩子中及时释放系统资源防止内存泄漏9.2 常见错误与解决方案常见问题快速排查表问题类型排查方向参考方法应用崩溃查看 hilog 错误日志hilog.error(TAG, ..., e.message)状态丢失检查 AppStorage 键名拼写统一使用常量管理键名动画不流畅避免在 animateTo 回调中执行 I/O动画与数据操作分离总结本文作为动画模块的收尾篇全面讲解了 AppAnimations 全局动画常量管理✅常量管理的必要性避免硬编码、统一体验、降低维护成本✅常量分类体系入场(6) / 页面过渡(2) / Canvas(4) / 交互(3) / 曲线(3)✅使用示例入场动画、页面转场、按钮反馈、Canvas 绘制的标准化调用✅语义化命名[场景]_[属性] 模式、static readonly 设计决策✅设计令牌体系AppAnimations AppColors AppFonts 共同构成完整令牌系统✅扩展指南添加新常量、调优流程、跨模块共享至此第九模块「动画系统」六篇文章全部完成从基础 animateTo 原理到入场编排、页面转场、按压反馈、呼吸脉冲、再到常量管理——覆盖了鸿蒙动画开发的完整知识链。下一篇将进入第十模块「NFC 与碰一碰分享」讲解 NFC Kit、pasteboard 剪贴板分享和二维码扫码分享的实现。 收藏提示如果您觉得本系列对您有帮助欢迎点赞 关注也欢迎在评论区交流鸿蒙动画开发的问题相关资源设计令牌系统AppColors 颜色体系AppFonts 字体体系第58篇 动画基础第59篇 入场动画第62篇 呼吸脉冲第99篇 设计模式回顾ArkUI 主题定制animateTo 显式动画 API