第12篇:Lottie 动画集成
Lottie 是 Airbnb 开源的高性能动画库,可以将 After Effects 导出的 JSON 动画文件直接在移动端渲染。在 HarmonyOS 生态中,Lottie 同样得到了支持。本篇详解"柚兔学伴"项目中 Lottie 动画的四个应用场景及集成要点。

一、Lottie 在 HarmonyOS 中的两种库
项目中使用了两个 Lottie 相关的库:
| 库名 | 作用 |
|---|---|
@ohos/lottie |
核心渲染引擎,负责解析 JSON 并绘制动画 |
@jjr/lottie_component |
ArkUI 声明式封装,提供 Lottie 组件和 LottieController |
@jjr/lottie_component 是对 @ohos/lottie 的 ArkUI 包装,让我们可以像使用原生组件一样在 build() 中声明 Lottie 动画,无需手动管理 Canvas。
导入方式
typescript
import { Lottie, LottieController } from '@jjr/lottie_component';
二、LottieController 动画控制器
每个 Lottie 动画实例对应一个 LottieController,用于控制动画的播放、暂停等行为:
typescript
controller: LottieController = new LottieController()
alarmController: LottieController = new LottieController()
finishController: LottieController = new LottieController()
控制器支持的主要操作:
| 方法 | 说明 |
|---|---|
play() |
播放动画 |
pause() |
暂停动画 |
stop() |
停止并重置动画 |
setProgress(0~1) |
跳转到指定进度 |
在项目中,大部分动画使用 autoPlay: true 自动播放,控制器主要用于在特定时机手动触发播放或停止。
三、四大应用场景
场景1:倒计时闹钟提醒(循环播放)
当番茄钟倒计时归零时,弹出闹钟动画:
typescript
@State alarmVisible: boolean = false
alarmController: LottieController = new LottieController()
@Builder
alarmBuild() {
Column({ space: 15 }) {
Lottie({
controller: this.alarmController,
animationPath: 'lottie/lottie_alarm.json',
autoPlay: true,
loop: true,
})
.width(80)
.height(80)
Text('时间到')
.fontColor($r('app.color.app_primary'))
.fontWeight(FontWeight.Bold)
}.padding(15)
}
IBestDialog({
visible: $alarmVisible,
defaultBuilder: (): void => this.alarmBuild(),
onConfirm: (() => {
this.alarmVisible = false
this.timerController.reset()
})
})
关键配置:
autoPlay: true:弹窗出现时自动播放loop: true:循环播放,直到用户点击确认关闭
这个场景中,Lottie 动画嵌套在 IBestDialog 的 defaultBuilder 中,形成"动画+弹窗"的组合提醒效果。闹钟动画的循环播放增强了紧迫感,用户必须主动确认才能关闭。
场景2:任务完成庆祝(单次播放)
当所有待办事项都完成时,播放庆祝动画:
typescript
@State finishVisible: boolean = false
finishController: LottieController = new LottieController()
@Builder
finishBuild() {
Column({ space: 15 }) {
Lottie({
controller: this.finishController,
animationPath: 'lottie/lottie_done.json',
autoPlay: true,
loop: false,
})
.width(120)
.height(120)
Text('你真棒,完成所有任务,积分+1!')
.fontColor($r('app.color.app_primary'))
.fontWeight(FontWeight.Bold)
}.padding(15)
}
关键配置:
autoPlay: true:弹窗出现时自动播放loop: false:只播放一次,完成感更强
触发逻辑:
typescript
CustomImageToggle({
isOn: item.isCompleted,
onToggleChange: (value) => {
item.isCompleted = value;
this.finishVisible = this.arr.every(item => item.isCompleted === true);
}
});
当最后一个任务被标记完成时,finishVisible 自动设为 true,弹窗弹出并播放庆祝动画。
场景3:汉字查询等待(StrokeView)
在练字功能中,查询汉字详情需要网络请求,等待期间播放 Lottie 动画替代传统 LoadingProgress:
typescript
controller: LottieController = new LottieController()
Column({ space: 10 }) {
if (this.strokeModel.loadingStatus == LoadingStatus.SUCCESS) {
this.buildWordDetail()
} else if (this.strokeModel.loadingStatus == LoadingStatus.FAILED) {
this.buildEmptyView()
} else {
Lottie({
controller: this.controller,
animationPath: 'lottie/lottie_waiting.json',
autoPlay: true,
loop: true,
})
.width(90)
.height(90)
}
}
关键配置:
autoPlay: true+loop: true:持续播放直到数据返回- 根据三种加载状态(加载中/成功/失败)切换不同 UI
这种"三态切换"模式将 Lottie 动画作为加载态的视觉表达,比系统默认的 LoadingProgress 更生动。
场景4:语音录制动画(ChatPage)
在 AI 对话页面,点击录音按钮后显示录制动画:
typescript
controller: LottieController = new LottieController()
@State isShowRecord: boolean = false
Stack() {
// 正常状态:显示录音按钮
Row() {
Button('点击 说话', { stateEffect: true, type: ButtonType.Normal })
.linearGradient({ angle: 90, colors: [[0xFF33FF, 0.0], [0x1C55FF, 1]] })
.borderRadius(30)
.height(50)
.width('100%')
.onClick(async () => {
grantPermission().then(async (isGranted: boolean) => {
if (isGranted) {
this.isShowRecord = true
RecordUtils.getInstance().startRecordingProcess()
} else {
ToastUtil.showToast('录音权限未获取')
}
})
})
}
.visibility(this.isShowRecord ? Visibility.None : Visibility.Visible)
// 录制状态:显示动画 + 控制按钮
Row() {
Image($r('app.media.ic_record_close')).width(30)
.onClick(() => {
this.isShowRecord = false
})
Lottie({
controller: this.controller,
animationPath: 'lottie/lottie_record.json',
autoPlay: true,
loop: true,
})
.width(90)
.height(90)
Image($r('app.media.ic_record_send')).width(30)
.onClick(() => {
this.isShowRecord = false
RecordUtils.getInstance().stopRecordingProcess().then((voicePath: string) => {
// 上传录音并处理
})
})
}
.visibility(this.isShowRecord ? Visibility.Visible : Visibility.None)
}
关键配置:
autoPlay: true+loop: true:录音期间持续播放- 通过
isShowRecord切换正常/录制两种 UI 状态
录音动画布局为三段式:取消(左)+ 动画(中)+ 发送(右),Lottie 动画占据中心位置,视觉上传达"正在录音"的实时感。
四、动画资源文件管理
项目中的 Lottie JSON 文件统一存放在 rawfile/lottie/ 目录下:
resources/rawfile/lottie/
├── lottie_alarm.json # 闹钟提醒动画
├── lottie_done.json # 完成庆祝动画
├── lottie_waiting.json # 等待加载动画
└── lottie_record.json # 录制中动画
引用时使用相对路径:
typescript
animationPath: 'lottie/lottie_alarm.json'
Lottie 组件会自动从 rawfile 目录加载对应文件,无需手动读取。
五、Lottie + IBestDialog 组合模式
项目中两次使用了"Lottie 动画嵌入自定义弹窗"的组合:
typescript
IBestDialog({
visible: $$someVisible,
defaultBuilder: (): void => someBuild(), // Builder 中包含 Lottie 动画
onConfirm: (() => {
this.someVisible = false
})
})
这种组合的优势:
- 视觉冲击力:纯文字弹窗容易忽略,动画弹窗吸引力强
- 情绪传达:闹钟动画传达"紧迫",庆祝动画传达"喜悦"
- 无需额外资源:Lottie JSON 文件体积小(通常 10-50KB),远低于 GIF 或视频
六、Lottie 组件核心参数
| 参数 | 类型 | 说明 |
|---|---|---|
controller |
LottieController | 动画控制器 |
animationPath |
string | rawfile 中的 JSON 路径 |
autoPlay |
boolean | 是否自动播放 |
loop |
boolean | 是否循环播放 |
autoPlay 和 loop 的组合策略:
| autoPlay | loop | 适用场景 |
|---|---|---|
| true | true | 等待、录制、提醒等持续性动画 |
| true | false | 庆祝、完成等一次性动画 |
| false | true/false | 需要手动触发的动画 |
七、业务逻辑与动画的联动
项目中动画的触发与业务逻辑紧密耦合:
倒计时归零 → alarmVisible = true → 闹钟动画循环播放 → 用户确认 → 动画消失 + 计时器重置
任务全部完成 → finishVisible = true → 庆祝动画单次播放 → 用户确认 → 动画消失 + 计时器重置
网络请求中 → loadingStatus != SUCCESS → 等待动画循环播放 → 数据返回 → 动画替换为内容
开始录音 → isShowRecord = true → 录制动画循环播放 → 发送/取消 → 动画消失
核心原则:动画是状态的视觉表达,而非独立存在。每个 Lottie 动画都有对应的状态变量控制其可见性,状态变化驱动动画的出现与消失。
八、小结
| 要点 | 说明 |
|---|---|
| 库选择 | @jjr/lottie_component 提供声明式 API,比直接使用 @ohos/lottie 更简洁 |
| autoPlay | 弹窗/条件渲染中的动画建议设为 true,出现即播放 |
| loop 策略 | 持续性状态用 true,一次性事件用 false |
| 资源管理 | JSON 文件放 rawfile/lottie/,用相对路径引用 |
| 状态联动 | 动画可见性由业务状态驱动,避免脱离上下文的动画 |