目录

  1. 动画系统架构

  2. 核心动画类型

  3. 动画工具类实现

  4. 实战应用场景

  5. 性能优化策略

  6. 最佳实践建议


动画系统架构

1. 统一动画管理

项目采用集中式动画管理方案,通过 Animation.ets 工具类统一管理所有动画效果:

// entry/src/main/ets/common/utils/Animation.ets
class AnimationUtil {
  // 转场缩放动画
  public isScaleTran(i: number, uiConfig: UIConfig) { }
  
  // 转场滑动动画
  public isSlide(i: number, uiConfig: UIConfig) { }
  
  // 点击动效
  public isClickEffect(uiConfig: UIConfig) { }
  
  // 图标动效
  public isIconEffect(uiConfig: UIConfig) { }
}
​
export const customAnimationUtil = new AnimationUtil()

2. 动画配置系统

通过 UIConfig 类实现全局动画配置:

export class UIConfig {
  // 动画开关
  isAnimation: boolean = true
  
  // 动画速度(毫秒)
  animationSpeed: number = 300
  
  // 动画速度档位索引
  animationSpeedIndex: number = 1  // 0:快速, 1:正常, 2:慢速, 3:关闭
  
  // 震动反馈
  isVibrate: boolean = true
  
  // 按钮位置索引(影响按钮动画方向)
  buttonPositionIndex: number = 0
}

核心动画类型

1. 转场动画 (TransitionEffect)

1.1 缩放转场动画

实现原理:组件出现时从缩放为0逐渐放大到正常大小,配合透明度变化

public isScaleTran(i: number, uiConfig: UIConfig) {
  return uiConfig.isAnimation ?
    TransitionEffect.OPACITY
      .combine(TransitionEffect.scale({x: 0, y: 0}))
      .animation({ 
        duration: uiConfig.animationSpeed,
        curve: curves.springMotion(),  // 弹性曲线
        delay: i * 10 + 30  // 递增延迟,实现瀑布流效果
      })
    : null
}

使用场景

  • 列表项加载

  • 卡片展示

  • 配置项显示

实际应用

// 配置列表项
ListItem() {
  // ... 内容
}
.transition(customAnimationUtil.isScaleTran(index, this.uiConfig))
1.2 滑动转场动画

从左到右滑入

public isSlide(i: number, uiConfig: UIConfig) {
  return uiConfig.isAnimation ?
    TransitionEffect.OPACITY
      .animation({ 
        duration: uiConfig.animationSpeed, 
        curve: curves.springMotion(),
        delay: i 
      })
      .combine(TransitionEffect.SLIDE)
    : null
}

从右到左滑入

public isSlideR(i: number, uiConfig: UIConfig) {
  return uiConfig.isAnimation ?
    TransitionEffect.OPACITY
      .animation({ 
        duration: uiConfig.animationSpeed, 
        curve: curves.springMotion(),
        delay: i 
      })
      .combine(TransitionEffect.translate({x: 20}))
    : null
}
1.3 非对称转场动画

应用场景:按钮位置切换时的不同进出动画

public isSlideSwitch(uiConfig: UIConfig) {
  return uiConfig.isAnimation ?
    (uiConfig.buttonPositionIndex === 0 ?
      TransitionEffect.asymmetric(
        // 进入动画
        TransitionEffect.OPACITY
          .animation({ duration: uiConfig.animationSpeed, curve: curves.springMotion(), delay: 50 })
          .combine(TransitionEffect.SLIDE_SWITCH),
        // 退出动画
        TransitionEffect.OPACITY
          .animation({ duration: uiConfig.animationSpeed, curve: curves.springMotion(), delay: 50 })
          .combine(TransitionEffect.SLIDE)
      )
    : /* 其他位置的动画 */)
  : null
}

2. 显式动画 (animateTo)

2.1 基本用法

实现原理:通过 animateTo 函数包裹状态变化,实现平滑过渡

// 切换代理组
animateTo({ 
  duration: this.uiConfig.animationSpeed 
}, () => {
  this.currentProxyIndex = targetIndex
})
2.2 配合 Swiper 组件
// 同步 Swiper 切换动画
animateTo({ duration: this.uiConfig.animationSpeed }, () => {
  this.currentProxyIndex = targetIndex
})
this.SwiperController.changeIndex(
  targetIndex, 
  this.uiConfig.animationSpeed != 0 ? true : false
)
2.3 卡片编辑动画
// 点击卡片显示/隐藏
.onClick(() => {
  animateTo({ 
    duration: this.uiConfig.animationSpeed, 
    curve: curves.springMotion() 
  }, () => {
    this.theCurrentHomeCard.splice(i, 1, isShow)
  })
})

3. 属性动画

3.1 宽度/高度动画

圆形按钮展开为胶囊按钮

Column() {
  Row() {
    SymbolGlyph(this.Switch ? this.Icon1 : this.Icon2)
      .fontSize(24)
    
    Text(this.SubText)
      .fontSize(20)
      .fontColor(this.Switch ? Color.White : Color.Transparent)
  }
}
.width(this.Switch ? 
  $r('app.integer.vp_proxy_started_button_width') :  // 展开宽度
  $r('app.integer.vp_proxy_not_start_button_width')  // 收缩宽度
)
.height($r('app.integer.vp_proxy_not_start_button_height'))
.animation({ 
  duration: this.uiConfig.animationSpeed,
  curve: curves.springMotion() 
})
3.2 旋转动画

无限循环旋转

startAnim() {
  this.rotateAngle = 0
  animateTo({
    duration: this.uiConfig.animationSpeed,
    curve: Curve.Linear,
    iterations: -1,  // 无限循环
    playMode: PlayMode.Alternate  // 往返播放
  }, () => {
    this.rotateAngle = 1
  })
}
​
// 应用旋转
.rotate({ 
  angle: this.uiConfig.isAnimation ? this.rotateAngle : null 
})
3.3 缩放动画

标签页图标缩放

.scale(this.tabsSelect.swiperPage == n.swiperPage ? 
  {x: this.tabsIconScale, y: this.tabsIconScale} : 
  {x: 1, y: 1}
)
.onTouch((event: TouchEvent) => {
  animateTo({
    duration: this.uiConfig.animationSpeed,
    curve: Curve.Ease,
  }, () => {
    if (event.type == TouchType.Down) {
      this.tabsIconScale = 0.8  // 按下缩小
    } else if (event.type == TouchType.Up) {
      this.tabsIconScale = 1    // 松开恢复
    }
  })
})

4. 交互动效

4.1 点击动效 (ClickEffect)

实现原理:点击时组件缩小,松开后恢复

public isClickEffect(uiConfig: UIConfig) {
  return uiConfig.isAnimation ? 
    { 
      level: ClickEffectLevel.MIDDLE,  // 中等强度
      scale: 0.95  // 缩放到95%
    } 
    : null
}
​
// 使用
.clickEffect(customAnimationUtil.isClickEffect(this.uiConfig))
4.2 图标动效 (SymbolEffect)

实现原理:系统图标的内置动画效果

public isIconEffect(uiConfig: UIConfig) {
  return uiConfig.isAnimation ? 
    SymbolEffectStrategy.SCALE  // 缩放效果
    : null
}
​
// 使用
SymbolGlyph($r('sys.symbol.play'))
  .effectStrategy(customAnimationUtil.isIconEffect(this.uiConfig))
4.3 悬停效果 (HoverEffect)
.hoverEffect(HoverEffect.Scale)  // 悬停时缩放

5. 手势动画

5.1 Swiper 滑动动画
Swiper(this.SwiperController) {
  // 内容
}
.indicator(false)
.loop(false)
.duration(this.uiConfig.animationSpeed)
.curve(Curve.EaseInOut)
.onGestureSwipe((index: number, extraInfo: SwiperAnimationEvent) => {
  animateTo({ duration: this.uiConfig.animationSpeed }, () => {
    // 处理滑动逻辑
  })
})
.onAnimationStart((index: number, targetIndex: number) => {
  animateTo({ duration: this.uiConfig.animationSpeed }, () => {
    this.currentProxyIndex = targetIndex
  })
})
5.2 触摸反馈动画
.onTouch((event: TouchEvent) => {
  animateTo({
    duration: this.uiConfig.animationSpeed,
    curve: Curve.Ease,
  }, () => {
    if (event.type == TouchType.Down) {
      this.switchTranslateX(i)
      this.tabsSelect.swiperPage = n.swiperPage
      this.tabsIconScale = 0.8
    } else if (event.type == TouchType.Up) {
      this.tabsIconScale = 1
    }
  })
})

动画工具类实现

完整工具类代码

import { UIConfig } from "../../entryability/AppState"
import { curves } from "@kit.ArkUI"
​
let effectEvent: TransitionEffect | null
let clickEffect: ClickEffect | null
let iconEffect: SymbolEffectStrategy | null
​
class AnimationUtil {
  
  // 1. 转场缩放动画(标准延迟)
  public isScaleTran(i: number, uiConfig: UIConfig) {
    effectEvent = uiConfig.isAnimation ?
      TransitionEffect.OPACITY
        .combine(TransitionEffect.scale({x: 0, y: 0}))
        .animation({ 
          duration: uiConfig.animationSpeed,
          curve: curves.springMotion(),
          delay: i * 10 + 30 
        })
      : null
    return effectEvent
  }
​
  // 2. 转场缩放动画(快速延迟)
  public isScaleTranFast(i: number, uiConfig: UIConfig) {
    effectEvent = uiConfig.isAnimation ?
      TransitionEffect.OPACITY
        .combine(TransitionEffect.scale({x: 0, y: 0}))
        .animation({ 
          duration: uiConfig.animationSpeed,
          curve: curves.springMotion(),
          delay: i * 5 + 30 
        })
      : null
    return effectEvent
  }
​
​
  // 3. 转场透明度动画
  public isOpacity(uiConfig: UIConfig) {
    effectEvent = uiConfig.isAnimation ?
      TransitionEffect.OPACITY
        .animation({ 
          duration: uiConfig.animationSpeed,
          curve: Curve.Ease,
          delay: 0 
        })
      : null
    return effectEvent
  }
​
  // 4. 转场滑动动画(左到右)
  public isSlide(i: number, uiConfig: UIConfig) {
    effectEvent = uiConfig.isAnimation ?
      TransitionEffect.OPACITY
        .animation({ 
          duration: uiConfig.animationSpeed, 
          curve: curves.springMotion(),
          delay: i 
        })
        .combine(TransitionEffect.SLIDE)
      : null
    return effectEvent
  }
​
  // 5. 转场滑动动画(右到左)
  public isSlideR(i: number, uiConfig: UIConfig) {
    effectEvent = uiConfig.isAnimation ?
      TransitionEffect.OPACITY
        .animation({ 
          duration: uiConfig.animationSpeed, 
          curve: curves.springMotion(),
          delay: i 
        })
        .combine(TransitionEffect.translate({x: 20}))
      : null
    return effectEvent
  }
​
  // 6. 转场放大动画
  public isSlideScaleBig(i: number, uiConfig: UIConfig) {
    effectEvent = uiConfig.isAnimation ?
      TransitionEffect.OPACITY
        .animation({ 
          duration: uiConfig.animationSpeed, 
          curve: curves.springMotion(),
          delay: i 
        })
        .combine(TransitionEffect.scale({x: 0, y: 0}))
      : null
    return effectEvent
  }
​
  // 7. 转场缩小动画(非对称)
  public isSlideScaleSmall(i: number, uiConfig: UIConfig) {
    effectEvent = uiConfig.isAnimation ?
      TransitionEffect.asymmetric(
        // 进入:从大到小
        TransitionEffect.OPACITY
          .animation({ 
            duration: uiConfig.animationSpeed, 
            curve: curves.springMotion(),
            delay: i 
          })
          .combine(TransitionEffect.scale({x: 2, y: 2})),
        // 退出:向上移动
        TransitionEffect.OPACITY
          .animation({ 
            duration: uiConfig.animationSpeed, 
            curve: curves.springMotion(),
            delay: i 
          })
          .combine(TransitionEffect.translate({y: -5}))
      )
      : null
    return effectEvent
  }
​
  // 8. 按钮滑动切换动画
  public isSlideSwitch(uiConfig: UIConfig) {
    effectEvent = uiConfig.isAnimation ?
      (uiConfig.buttonPositionIndex === 0 ?
        TransitionEffect.asymmetric(
          TransitionEffect.OPACITY
            .animation({ 
              duration: uiConfig.animationSpeed, 
              curve: curves.springMotion(), 
              delay: 50 
            })
            .combine(TransitionEffect.SLIDE_SWITCH),
          TransitionEffect.OPACITY
            .animation({ 
              duration: uiConfig.animationSpeed, 
              curve: curves.springMotion(), 
              delay: 50 
            })
            .combine(TransitionEffect.SLIDE)
        )
      : uiConfig.buttonPositionIndex === 2 ?
        TransitionEffect.asymmetric(
          TransitionEffect.OPACITY
            .animation({ 
              duration: uiConfig.animationSpeed, 
              curve: curves.springMotion(), 
              delay: 50 
            })
            .combine(TransitionEffect.SLIDE),
          TransitionEffect.OPACITY
            .animation({ 
              duration: uiConfig.animationSpeed, 
              curve: curves.springMotion(), 
              delay: 50 
            })
            .combine(TransitionEffect.SLIDE_SWITCH)
        )
      : TransitionEffect.OPACITY
          .animation({ 
            duration: uiConfig.animationSpeed, 
            curve: curves.springMotion(), 
            delay: 50 
          })
          .combine(TransitionEffect.SLIDE_SWITCH))
    : null
    return effectEvent
  }
​
​
  // 9. 底部标签栏转场动画
  public isTranslate(uiConfig: UIConfig) {
    effectEvent = uiConfig.isAnimation ? 
      TransitionEffect.OPACITY
        .animation({ 
          duration: uiConfig.animationSpeed, 
          delay: 0 
        })
        .combine(TransitionEffect.translate({ y: 50 })) 
      : null
    return effectEvent
  }
​
  // 10. 点击动效
  public isClickEffect(uiConfig: UIConfig) {
    clickEffect = uiConfig.isAnimation ? 
      { 
        level: ClickEffectLevel.MIDDLE,
        scale: 0.95
      } 
      : null
    return clickEffect
  }
​
  // 11. 图标动效
  public isIconEffect(uiConfig: UIConfig) {
    iconEffect = uiConfig.isAnimation ? 
      SymbolEffectStrategy.SCALE 
      : null
    return iconEffect
  }
​
  // 12. 图标转场缩放旋转动画
  public isScaleTranIcon(i: number, uiConfig: UIConfig) {
    effectEvent = uiConfig.isAnimation ?
      TransitionEffect.scale({x: 0, y: 0})
        .combine(TransitionEffect.rotate({angle: 360}))
        .animation({ 
          duration: uiConfig.animationSpeed,
          curve: curves.springMotion(),
          delay: i * 10 + 50 
        })
      : null
    return effectEvent
  }
}
​
export const customAnimationUtil = new AnimationUtil()

使用方法

// 1. 转场动画
.transition(customAnimationUtil.isScaleTran(index, this.uiConfig))
​
// 2. 点击动效
.clickEffect(customAnimationUtil.isClickEffect(this.uiConfig))
​
// 3. 图标动效
.effectStrategy(customAnimationUtil.isIconEffect(this.uiConfig))

实战应用场景

场景1:列表项递增动画

效果:列表项依次从小到大出现,形成瀑布流效果

List() {
  ForEach(this.configList, (item: ConfigData, index: number) => {
    ListItem() {
      // 列表项内容
    }
    .transition(customAnimationUtil.isScaleTran(index, this.uiConfig))
  })
}

关键点

  • 使用 index 作为延迟参数

  • 延迟计算公式:i * 10 + 30 毫秒

  • 配合 curves.springMotion() 实现弹性效果

场景2:卡片展开/收起动画

效果:卡片点击后平滑展开或收起

Image($r('app.media.card_image'))
  .width(w)
  .height(160)
  .transition(customAnimationUtil.isScaleTran(i, this.uiConfig))
  .onClick(() => {
    animateTo({ 
      duration: this.uiConfig.animationSpeed, 
      curve: curves.springMotion() 
    }, () => {
      this.theCurrentHomeCard.splice(i, 1, !this.theCurrentHomeCard[i])
    })
  })

场景3:按钮状态切换动画

效果:圆形按钮展开为胶囊按钮,显示计时器文本

Column() {
  Row() {
    SymbolGlyph(this.Switch ? this.Icon1 : this.Icon2)
      .fontSize(24)
      .effectStrategy(customAnimationUtil.isIconEffect(this.uiConfig))
    
    Text(this.SubText)
      .fontSize(20)
      .fontColor(this.Switch ? Color.White : Color.Transparent)
  }
}
.width(this.Switch ? 
  $r('app.integer.vp_proxy_started_button_width') : 
  $r('app.integer.vp_proxy_not_start_button_width')
)
.animation({ 
  duration: this.uiConfig.animationSpeed,
  curve: curves.springMotion() 
})
.transition(customAnimationUtil.isSlideSwitch(this.uiConfig))
.clickEffect(customAnimationUtil.isClickEffect(this.uiConfig))

场景4:Swiper 页面切换动画

效果:页面滑动切换时同步更新索引和UI状态

Swiper(this.SwiperController) {
  ForEach(this.theProxyGroups, (group: ProxyGroup) => {
    // 页面内容
  })
}
.indicator(false)
.loop(false)
.duration(this.uiConfig.animationSpeed)
.curve(Curve.EaseInOut)
.onGestureSwipe((index: number, extraInfo: SwiperAnimationEvent) => {
  animateTo({ duration: this.uiConfig.animationSpeed }, () => {
    // 处理手势滑动逻辑
  })
})
.onAnimationStart((index: number, targetIndex: number) => {
  animateTo({ duration: this.uiConfig.animationSpeed }, () => {
    this.ListScroller.scrollToIndex(targetIndex)
    this.currentProxyIndex = targetIndex
  })
})

场景5:标签页切换动画

效果:标签页图标缩放 + 指示器平移

// 标签页图标
SymbolGlyph(n.icon)
  .fontSize(24)
  .scale(this.tabsSelect.swiperPage == n.swiperPage ? 
    {x: this.tabsIconScale, y: this.tabsIconScale} : 
    {x: 1, y: 1}
  )
  .onTouch((event: TouchEvent) => {
    animateTo({
      duration: this.uiConfig.animationSpeed,
      curve: Curve.Ease,
    }, () => {
      if (event.type == TouchType.Down) {
        this.switchTranslateX(i)
        this.tabsSelect.swiperPage = n.swiperPage
        this.tabsIconScale = 0.8
      } else if (event.type == TouchType.Up) {
        this.tabsIconScale = 1
      }
    })
  })
​
// Swiper 切换时同步动画
.onChange((index: number) => {
  animateTo({
    duration: this.uiConfig.animationSpeed,
    curve: curves.interpolatingSpring(10, 1, 228, 30),
  }, () => {
    this.tabsSelect.swiperPage = targetIndex
    this.switchTranslateX(targetIndex)
  })
})

场景6:配置项删除动画

效果:删除按钮出现 + 列表项移除动画

if (this.isShowDeleteButton) {
  Image($r('sys.symbol.xmark'))
    .width(HomeCardDeleteButtonSize(this.widthBp, this.heightBp))
    .height(HomeCardDeleteButtonSize(this.widthBp, this.heightBp))
    .transition(customAnimationUtil.isSlideScaleSmall(0, this.uiConfig))
    .onClick(() => {
      animateTo({ 
        duration: this.uiConfig.animationSpeed, 
        curve: curves.springMotion() 
      }, () => {
        this.theCurrentHomeCard.splice(i, 1, false)
      })
    })
}

性能优化策略

1. 动画开关控制

实现方案:通过全局配置控制动画开关

export class UIConfig {
  isAnimation: boolean = true  // 动画总开关
  animationSpeed: number = 300  // 动画速度
  animationSpeedIndex: number = 1  // 速度档位
}
​
// 动画速度映射
const ANIMATION_SPEEDS = [150, 300, 500, 0]  // 快速、正常、慢速、关闭

优势

  • 用户可根据设备性能选择动画档位

  • 低端设备可关闭动画提升流畅度

  • 节省电量

2. 条件性应用动画

实现方案:根据配置决定是否应用动画

.transition(
  uiConfig.isAnimation ? 
    customAnimationUtil.isScaleTran(index, uiConfig) : 
    null
)

优势

  • 避免不必要的动画计算

  • 减少渲染负担

3. 延迟优化

标准延迟:适用于普通列表

delay: i * 10 + 30  // 每项延迟10ms

快速延迟:适用于大量元素

delay: i * 5 + 30  // 每项延迟5ms

固定延迟:适用于单个元素

delay: 50  // 固定延迟

4. 动画曲线选择

弹性曲线 (springMotion):适用于大部分场景

curve: curves.springMotion()
  • 自然的弹性效果

  • 符合物理运动规律

  • 用户体验最佳

线性曲线 (Linear):适用于循环动画

curve: Curve.Linear
  • 匀速运动

  • 适合旋转、进度条等

缓动曲线 (Ease):适用于简单过渡

curve: Curve.Ease
  • 先加速后减速

  • 适合透明度、位移等

插值弹簧曲线:适用于精细控制

curve: curves.interpolatingSpring(10, 1, 228, 30)
  • 参数:速度、阻尼、质量、初始速度

  • 可精确控制弹性效果

5. 避免过度动画

问题:同时触发过多动画导致卡顿

解决方案

  1. 限制同时播放的动画数量

  2. 使用虚拟滚动减少渲染元素

  3. 大列表使用快速延迟模式

// 使用快速延迟
.transition(customAnimationUtil.isScaleTranFast(index, this.uiConfig))

6. 动画状态管理

问题:频繁切换动画状态导致性能问题

解决方案:使用标志位控制动画触发

export class isON {
  toggleAnim: boolean = false  // 动画触发标志
}
​
// 使用
.transition(this.isON.toggleAnim ? null : customAnimationUtil.isScaleTran(index, this.uiConfig))

最佳实践建议

1. 动画设计原则

1.1 一致性原则
  • 同类型交互使用相同动画效果

  • 统一动画时长和曲线

  • 保持视觉语言一致

1.2 性能优先原则
  • 优先使用 GPU 加速的属性(transform、opacity)

  • 避免动画 width、height 等触发重排的属性

  • 使用 scale 代替 width/height 动画

1.3 用户体验原则
  • 动画时长控制在 200-400ms

  • 提供动画开关选项

  • 避免过度动画干扰用户操作

2. 代码组织建议

2.1 集中管理动画配置
// 统一的动画工具类
export const customAnimationUtil = new AnimationUtil()
​
// 统一的配置类
@StorageLink('uiConfig') uiConfig: UIConfig = new UIConfig()
2.2 使用语义化命名
// 好的命名
isScaleTran()      // 缩放转场
isSlideSwitch()    // 滑动切换
isClickEffect()    // 点击效果
​
// 避免
anim1()
effect()
trans()
2.3 注释说明动画效果
// 转场缩放动画,i为延迟,在list或foreach中可以将i设为index,达到递增的动画效果
public isScaleTran(i: number, uiConfig: UIConfig) {
  // 实现代码
}

3. 常见动画组合

3.1 列表项出现动画
TransitionEffect.OPACITY
  .combine(TransitionEffect.scale({x: 0, y: 0}))
  .animation({ 
    duration: 300,
    curve: curves.springMotion(),
    delay: index * 10 + 30 
  })
3.2 页面切换动画
TransitionEffect.asymmetric(
  TransitionEffect.move(TransitionEdge.END),
  TransitionEffect.move(TransitionEdge.START)
)
.animation({ duration: 300, curve: Curve.Ease })
.combine(TransitionEffect.OPACITY)
3.3 按钮交互动画
// 点击缩放
.clickEffect({ level: ClickEffectLevel.MIDDLE, scale: 0.95 })
​
// 悬停效果
.hoverEffect(HoverEffect.Scale)
​
// 图标动效
.effectStrategy(SymbolEffectStrategy.SCALE)

4. 动画调试技巧

4.1 动画速度调试
// 开发时使用慢速动画便于观察
const DEBUG_MODE = true
const animationSpeed = DEBUG_MODE ? 1000 : 300
4.2 动画开关调试
// 快速切换动画开关测试性能
@StorageLink('uiConfig') uiConfig: UIConfig = new UIConfig()
​
// 在设置页提供开关
Toggle({ type: ToggleType.Switch, isOn: this.uiConfig.isAnimation })
  .onChange((isOn: boolean) => {
    this.uiConfig.isAnimation = isOn
  })
4.3 日志输出
animateTo({ 
  duration: this.uiConfig.animationSpeed,
  onFinish: () => {
    console.info('动画完成')
  }
}, () => {
  console.info('动画开始')
  this.currentIndex = targetIndex
})

5. 动画性能监控

5.1 帧率监控
// 使用 HiTrace 监控动画性能
import { hiTraceMeter } from '@kit.PerformanceAnalysisKit'
​
hiTraceMeter.startTrace('animation_performance', 1)
animateTo({ duration: 300 }, () => {
  // 动画逻辑
})
hiTraceMeter.finishTrace('animation_performance', 1)
5.2 避免动画卡顿

问题场景

  • 大量元素同时动画

  • 复杂布局计算

  • 频繁状态更新

解决方案

// 1. 分批动画
ForEach(this.list, (item, index) => {
  ListItem() { }
    .transition(customAnimationUtil.isScaleTran(
      Math.min(index, 10),  // 限制最大延迟
      this.uiConfig
    ))
})
​
// 2. 使用 LazyForEach
LazyForEach(this.dataSource, (item, index) => {
  ListItem() { }
    .transition(customAnimationUtil.isScaleTranFast(index, this.uiConfig))
})
​
// 3. 条件性渲染
if (this.isVisible) {
  Component()
    .transition(customAnimationUtil.isOpacity(this.uiConfig))
}

动画效果速查表

动画类型 方法名 适用场景 延迟参数
缩放转场 isScaleTran() 列表项、卡片 index * 10 + 30
快速缩放转场 isScaleTranFast() 大量元素 index * 5 + 30
透明度转场 isOpacity() 简单显隐 0
左滑入 isSlide() 页面切换 固定值
右滑入 isSlideR() 侧边栏 固定值
放大转场 isSlideScaleBig() 强调元素 固定值
缩小转场 isSlideScaleSmall() 删除动画 0
按钮切换 isSlideSwitch() 按钮状态 50
底部栏 isTranslate() 标签栏 0
点击效果 isClickEffect() 所有可点击元素 -
图标效果 isIconEffect() 系统图标 -
图标转场 isScaleTranIcon() 设置项图标 index * 10 + 50

动画曲线对比

曲线类型 特点 适用场景 代码
springMotion 弹性效果 大部分场景 curves.springMotion()
Linear 匀速运动 循环动画 Curve.Linear
Ease 缓入缓出 简单过渡 Curve.Ease
EaseIn 缓入 元素消失 Curve.EaseIn
EaseOut 缓出 元素出现 Curve.EaseOut
EaseInOut 缓入缓出 页面切换 Curve.EaseInOut
interpolatingSpring 自定义弹簧 精细控制 curves.interpolatingSpring(10, 1, 228, 30)

总结

核心要点

  1. 统一管理:使用工具类集中管理所有动画效果

  2. 性能优先:提供动画开关,支持多档速度

  3. 用户体验:使用弹性曲线,控制合理时长

  4. 代码复用:封装通用动画,减少重复代码

  5. 灵活配置:支持全局配置和局部定制

实现步骤

  1. 创建 Animation.ets 工具类

  2. 定义 UIConfig 配置类

  3. 在组件中引入并使用

  4. 提供用户设置界面

  5. 测试和优化性能

推荐配置

// 推荐的动画配置
export class UIConfig {
  isAnimation: boolean = true           // 默认开启
  animationSpeed: number = 300          // 300ms 最佳
  animationSpeedIndex: number = 1       // 正常速度
}
​
// 推荐的动画曲线
curve: curves.springMotion()            // 弹性效果最佳
​
// 推荐的延迟策略
delay: index * 10 + 30                  // 标准列表
delay: index * 5 + 30                   // 大量元素
delay: 50                               // 单个元素

参考资源

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐