做微信小程序时,交互体验往往就差那么一口气:点赞图标干巴巴地切换图片,弹窗生硬地弹出消失,用户总觉得少了点什么。其实官方提供的wx.createAnimation动画API配合animation属性,就能用极少的代码实现平滑的缩放、位移、透明度变化。本文从API的基本用法讲起,逐步实现点赞特效和弹窗动画两个典型场景,并讨论动画方案的选择与性能注意事项。

createAnimation基础用法与核心概念
wx.createAnimation是小程序提供的动画生成器,调用后返回一个Animation动画实例。这个实例本身不直接驱动界面渲染,而是通过链式调用一系列动画方法来描述一段动画,最后把这个实例赋给WXML中某个节点的animation属性,界面才会真正动起来。
创建动画实例时可以传入一个配置对象,常用参数有三个:duration表示单次动画的时长,单位毫秒,默认400;timingFunction表示动画的缓动方式,可选linear、ease、ease-in、ease-out、ease-in-out以及step-start、step-end;delay表示动画延迟执行的毫秒数。下面是最基础的创建方式:
// 在Page的data中声明动画数据
Page({
data: {
likeAnimation: {}
},
onReady() {
// 创建动画实例,时长300毫秒,先快后慢
const animation = wx.createAnimation({
duration: 300,
timingFunction: 'ease-out',
delay: 0
});
animation.scale(1.3).step();
this.setData({ likeAnimation: animation.export() });
}
})这里有个关键点需要特别注意:赋给animation属性的必须是animation.export()导出的动画数据,而不是动画实例本身。export方法会把实例中已经通过step标记的动画步骤打包成WXML可以识别的数据快照。每调用一次export,都会截取当前已经累积的动画步骤。
step方法的作用是把之前的所有动画调用划分成一组,同组内的动画会同时执行。也就是说,写在同一个step之前的scale、rotate、opacity会并行播放;如果想让动画依次执行,就需要多次调用step来分段。理解了这一点,就能解释为什么回弹类动画必须分成两段step来写。
常用动画属性一览
Animation实例支持的方法大致可以分为四类:变换类、透明度类、颜色类和矩阵类。变换类包括translate、translateX、translateY、rotate、rotateX、rotateY、scale、scaleX、scaleY、skew、skewX、skewY,分别控制位移、旋转、缩放和倾斜。透明度类只有opacity一个方法,取值0到1。颜色类包括backgroundColor、color、width、height、top、left等,用于改变布局相关属性。
变换类方法都是相对当前状态叠加的,这是初学者最容易踩的坑。比如先调用translateX(50)再调用translateX(30),最终元素会移动80像素而不是30像素。如果希望基于原始位置动画,可以在动画前调用矩阵重置,或者记住当前状态反向计算。实际开发中更推荐的做法是,每次动画开始前都从初始状态出发设计动画序列,避免叠加产生不可预期的结果。
此外还有matrix、matrix3d方法可以直接设置变换矩阵,适合需要精确控制复合变换的场景,日常开发用得不多。下表整理了点赞特效和弹窗动画中最常用的几个方法:
| 方法 | 作用 | 典型场景 |
|---|---|---|
| scale(num) | 缩放到指定倍数 | 点赞放大、按钮按压效果 |
| opacity(value) | 设置透明度 | 弹窗淡入淡出 |
| translateY(px) | 垂直位移 | 底部弹窗滑入滑出 |
| rotate(deg) | 旋转指定角度 | 加载图标旋转、翻转效果 |
实战一:实现点赞放大回弹特效
点赞特效的核心诉求是:用户点击爱心图标时,图标先放大再回弹到原始大小,同时可以配合旋转增加灵动感。这需要两段动画:第一段放大到1.3倍并顺时针旋转15度,第二段回到1倍并归零角度。两段动画必须分属不同的step,否则会被合并同时执行,看不到回弹效果。
WXML部分非常简单,一个绑定了animation数据的image节点:
<view class="like-wrap" bindtap="onLikeTap">
<image class="like-icon" src="/images/heart.png" animation="{{likeAnimation}}" />
<text>{{likeCount}}</text>
</view>JS部分需要注意动画时长与回弹节奏的配合,第一段短促有力,第二段稍长一些产生弹性观感:
Page({
data: {
likeCount: 120,
liked: false,
likeAnimation: {}
},
onLikeTap() {
if (this.data.liked) return;
const animation = wx.createAnimation({
duration: 150,
timingFunction: 'ease-out'
});
// 第一段:放大并轻微旋转
animation.scale(1.4).rotate(15).step();
// 第二段:回弹,时长设置得长一些
animation.scale(1).rotate(0).step({ duration: 300, timingFunction: 'ease-in-out' });
this.setData({
liked: true,
likeCount: this.data.likeCount + 1,
likeAnimation: animation.export()
});
}
})注意step方法可以接收参数,为该段动画单独指定时长和缓动函数,这样一段动画序列里可以混合不同的节奏。如果还想加上颜色变化,比如点赞后计数文字变红,可以在第一段step里同时调用color('#ff4d6d'),它和缩放会同时执行,观感更丰富。取消点赞时只需要反向播放类似动画,或者简单地把图标切换回灰色即可。
实战二:实现底部滑入的半屏弹窗动画
弹窗动画比点赞稍复杂,因为它涉及显示与隐藏两个方向:打开时从底部滑入并配合背景蒙层淡入,关闭时反向滑出并淡出蒙层。背景蒙层和弹窗主体是两个节点,需要两个独立的动画数据,建议用两个动画实例分别export导出,避免相互干扰。
<view class="mask" animation="{{maskAnimation}}" bindtap="closeDialog"></view>
<view class="dialog" animation="{{dialogAnimation}}" wx:if="{{showDialog}}">
<view class="dialog-title">确认提交</view>
<view class="dialog-content">提交后将无法撤回,请确认信息无误。</view>
<view class="dialog-btns">
<view bindtap="closeDialog">取消</view>
<view bindtap="confirmSubmit">确定</view>
</view>
</view>这里有一个隐藏的坑:wx:if会在关闭时直接销毁节点,动画来不及播放节点就消失了。正确的做法是先播放退出动画,动画结束后再真正隐藏节点。可以利用setTimeout在动画时长后清理状态:
Page({
data: {
showDialog: false,
maskAnimation: {},
dialogAnimation: {}
},
openDialog() {
this.setData({ showDialog: true });
const animation = wx.createAnimation({ duration: 300, timingFunction: 'ease-out' });
// 蒙层淡入
animation.opacity(1).step();
this.setData({ maskAnimation: animation.export() });
// 弹窗从底部滑入,重新创建实例避免相互干扰
const dialogAnim = wx.createAnimation({ duration: 300, timingFunction: 'ease-out' });
dialogAnim.translateY(0).opacity(1).step();
this.setData({ dialogAnimation: dialogAnim.export() });
},
closeDialog() {
const animation = wx.createAnimation({ duration: 250, timingFunction: 'ease-in' });
animation.opacity(0).step();
this.setData({ maskAnimation: animation.export() });
const dialogAnim = wx.createAnimation({ duration: 250, timingFunction: 'ease-in' });
dialogAnim.translateY('100%').opacity(0).step();
this.setData({ dialogAnimation: dialogAnim.export() });
// 等动画播完再销毁节点
setTimeout(() => {
this.setData({ showDialog: false });
}, 260);
}
})CSS中弹窗初始状态要设置为transform: translateY(100%); opacity: 0;,蒙层初始opacity: 0,这样动画从隐藏状态过渡到显示状态才自然。关闭时的260毫秒定时器要略大于动画时长250毫秒,确保动画完整播放。如果弹窗可能被快速重复开关,建议在data中记录一个关闭中的标记,避免定时器叠加导致状态错乱。
createAnimation与其他动画方案的对比与选择
createAnimation并不是小程序里唯一的动画手段,常见的还有三种:CSS3的transition和animation、WXS响应式动画、以及新版Skyline渲染引擎下的worklet动画。它们各有适用边界,选对方案比写好代码更重要。
CSS3动画的优势在于纯样式层实现,不经过逻辑层与渲染层的通信,性能最好,适合状态切换类动画,比如按钮hover、tab下划线滑动。缺点是无法在JS中精细控制动画时序,交互复杂时力不从心。createAnimation的优势则是完全由JS驱动,可以根据接口数据动态计算位移距离、动态决定动画分支,点赞特效和弹窗这类强交互场景正是它的主场。它的原理是JS生成动画描述数据,通过setData传递给渲染层,由原生组件执行,所以性能优于直接用setData频繁改样式,但仍有一次通信开销。
WXS响应式动画主要解决手势跟手类需求,比如下拉刷新、侧滑删除,它运行在视图层,避免了通信延迟,跟手性远超createAnimation。而worklet动画则是Skyline渲染引擎下的新方案,可以在渲染线程直接运行动画函数,适合高频更新的复杂动效。简单总结选择原则:静态状态切换用CSS,离散的交互动画用createAnimation,需要跟手的场景用WXS,追求极致流畅的复杂动效考虑worklet。
最后提醒一点关于动画清理的实践:动画实例是一次性的,每次触发动画都应重新调用wx.createAnimation创建新实例,复用旧实例容易把上一次未完成的动画步骤带入新序列。同时在页面onUnload里清空data中的动画数据,可以避免某些机型上页面关闭时的渲染残留问题。掌握这些细节,点赞特效与弹窗动画就能在小程序里稳定流畅地跑起来了。
微信小程序动画createAnimation点赞特效修改时间:2026-09-01 15:31:18