在日常开发中,一些复杂的动画交互效果单纯通过 CSS 来实现往往力不从心。常见的替代方案是使用 GIF 动图,但当动画复杂且对清晰度、流畅度有较高要求时,GIF 的大体积又成了影响用户体验的新瓶颈。
这时,Lottie 动画库便是一个优雅的解决方案。它允许开发者直接加载由 UI 设计师导出的 .json 文件,从而在项目中实现高效、高清且高度可控的复杂动画效果。
本文将以 uniapp 的微信小程序 为例,采用微信小程序官方推荐的 lottie-miniprogram 库进行实践介绍。我们将深入核心流程,探讨实际开发中可能遇到的各种“坑”,并最终将这些经验沉淀为一个可直接复用的 Vue 3 组件。如果你也在寻找一种超越传统 CSS 和 GIF 的动画实现方式,这篇文章或许能为你提供清晰的路径。
开发环境
- Node:
20.13.1
- NPM:
10.5.2
主要依赖包
| 包名 |
版本 |
@dcloudio/uni-app |
3.0.0-4030620241128001 |
vue |
3.4.21 |
lottie-miniprogram |
1.0.12 |
二、核心实现流程
1. 安装依赖
第一步是通过 npm 安装核心库。
npm install --save lottie-miniprogram
2. 绑定 Canvas 组件
Lottie 在小程序中的渲染依赖于 <canvas> 组件。你需要在页面的模板中声明它,并为其指定一个唯一的 id 和 type="2d"。
<canvas id="lottieCanvas" type="2d"></canvas>
随后,在组件的 <script setup> 中,你需要获取这个 canvas 节点的引用,并将其“安装”给 Lottie。
import lottie from "lottie-miniprogram";
import { getCurrentInstance, onMounted } from "vue";
onMounted(() => {
uni
.createSelectorQuery()
.in(getCurrentInstance().proxy)
.select("#lottieCanvas")
.node((res) => {
const canvas = res.node;
lottie.setup(canvas);
// 后续加载动画的代码...
})
.exec();
});
3. 调用 Lottie 接口加载动画
在成功获取并设置 canvas 后,就可以调用 lottie.loadAnimation 来加载并播放动画了。你可以选择直接传入 JSON 对象 (animationData) 或一个远程 JSON 文件的 URL (path)。
lottie.setup(canvas);
state.animation = lottie.loadAnimation({
loop: true,
autoplay: true,
rendererSettings: {
context: canvas.getContext("2d"),
},
animationData: ..., // 或 path: ...
});
重要提示:当页面或组件卸载时,务必手动销毁动画实例以释放资源。
state.animation.destroy();
lottie-miniprogram 官方说明摘要:
- 基于
lottie-web 封装,可根据需要切换底层版本。
- 依赖微信小程序基础库 2.9.0 及以上版本正式开放的
type="2d" canvas。
- 不支持 AE 动画中的
expression 表达式(因为小程序安全策略禁止动态执行 JS)。
三、常见问题分析与解决方案
理论流程看似简单,但实践中总会遇到一些棘手的问题。下面是我们总结的几个典型“坑”及其应对策略。
1. 动画比例不对 / 图像模糊
问题现象:设计师提供的动画尺寸是像素(px)单位,而小程序开发使用的是响应式单位(rpx)。同时,如果不处理设备像素比(DPR),在高清屏上 Canvas 绘制的内容会显得模糊。
根本原因:
- px 与 rpx 的单位转换问题。
- Canvas 的绘图上下文未根据 DPR 进行缩放。
解决方案:
- 在设置 Canvas 节点的
width 和 height 属性时,将设计稿的 rpx 值转换为实际的物理像素(px)。
- 同时,通过
canvas.getContext(“2d”).scale(dpr, dpr) 放大绘图上下文,确保绘制指令在高分辨率设备上生效。
2. CSS 修改 Canvas 位置无效
问题现象:尝试使用 margin, position 等 CSS 属性调整 canvas 位置,但发现完全不起作用。
根本原因:在小程序中,<canvas> 属于原生组件,其层级始终高于 WebView 渲染的普通视图组件,因此不受普通 CSS 样式的影响。
解决方案:
- 放弃使用 CSS 定位,改为在初始化时通过 JavaScript API (
uni.createSelectorQuery) 获取节点信息并计算其位置。
- 更常见的做法是,将 canvas 放置在一个通过 CSS 定位的容器
view 中,然后让 canvas 充满该容器,通过控制容器的位置来间接控制 canvas。
3. 动画 JSON 文件部分效果不生效
问题现象:有些从 After Effects (AE) 导出的 .json 文件,在 AE 或 LottieFiles 预览网站上显示正常,但放入小程序后,渐变、某些特效或图层丢失了。
猜测原因:
- AE 中使用的某些特性或插件效果,Lottie 官方库尚未完全支持。
- 导出
.json 时的 Bodymovin 插件配置或 AE 版本可能导致兼容性问题。
应对建议:
四、组件封装:LottieAnimation
为了提升开发效率,避免在每个页面重复处理上述问题,我们将最佳实践封装成一个可复用的 Vue 3 组件。
✅ 组件设计目标
- 开箱即用:传入宽高(支持 rpx)、动画数据即可显示。
- 清晰显示:自动处理 rpx 转 px 和 DPR 缩放,解决模糊问题。
- 灵活数据源:支持传入本地 JSON 对象或远程 JSON 文件 URL。
- 可控生命周期:支持通过
isShow 控制显示/隐藏,自动管理动画的创建与销毁。
🌟 组件完整代码 (uni-app + Vue3 + TypeScript)
以下是 components/LottieAnimation/LottieAnimation.vue 的完整实现。
<!-- components/LottieAnimation/LottieAnimation.vue -->
<template>
<div
v-if="props.isShow"
class="lottie-container"
:style="{ width: `${props.width}rpx`, height: `${props.height}rpx` }"
>
<canvas
id="lottieCanvas"
type="2d"
:style="{ width: `${props.width}rpx`, height: `${props.height}rpx` }"
></canvas>
</div>
</template>
<script setup lang="ts">
import lottie from "lottie-miniprogram";
import { getCurrentInstance, onMounted, onUnmounted, reactive } from "vue";
const props = withDefaults(defineProps<{
isShow: boolean;
json: Record<string, any> | string;
width: number;
height: number;
}>(), {});
const state = reactive({
animation: null as ReturnType<typeof lottie.loadAnimation> | null,
});
const actions = {
rpxToPx(rpx: number) {
return (rpx / 750) * uni.getWindowInfo().windowWidth;
},
};
onMounted(() => {
const instance = getCurrentInstance();
if (!instance) return;
uni
.createSelectorQuery()
.in(instance.proxy)
.select("#lottieCanvas")
.node((res) => {
const canvas = res.node;
const ctx = canvas.getContext("2d");
const dpr = uni.getSystemInfoSync().pixelRatio;
// 关键步骤:设置Canvas实际宽高并缩放上下文
canvas.width = actions.rpxToPx(props.width) * dpr;
canvas.height = actions.rpxToPx(props.height) * dpr;
ctx.scale(dpr, dpr);
const options = {
loop: true,
autoplay: true,
rendererSettings: { context: ctx },
};
// 支持传入对象或URL字符串
if (typeof props.json === "string") {
options.path = props.json;
} else {
options.animationData = props.json;
}
lottie.setup(canvas);
state.animation = lottie.loadAnimation(options);
})
.exec(() => {
state.animation?.play();
});
});
onUnmounted(() => {
state.animation?.destroy();
});
</script>
<style>
.lottie-container {
display: flex;
justify-content: center;
align-items: center;
}
</style>
五、在页面中使用组件
封装完成后,在页面中的使用方式变得非常简单直观。
<template>
<div class="container">
<LottieAnimation :isShow="true" :json="data" :width="200" :height="200" />
</div>
</template>
<script setup lang="ts">
import LottieAnimation from "@/components/LottieAnimation/LottieAnimation.vue";
// 假设你有一个本地的JSON动画文件
import data from "@/components/LottieAnimation/json/data.json";
</script>
<style>
.container {
width: 100%;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
background: #f5f5f5;
box-sizing: border-box;
padding-bottom: 200rpx;
}
</style>
六、扩展资源与参考
本文涉及的完整示例代码可在代码仓库中查看。
总结
通过上述步骤,我们不仅掌握了在 uniapp 微信小程序 中集成 Lottie 动画的核心技术,还系统性地解决了清晰度、布局兼容性等常见问题,并将这些解决方案封装成了高质量的复用组件。这种从问题出发,以实践为导向,最终沉淀为可复用资产的过程,正是高效前端开发的精髓。希望这份实践总结能为你的项目带来便利,也欢迎在 云栈社区 与其他开发者继续探讨更多前端技术与工程实践。