自定义弹窗的动画快慢,并不是改一个全局参数就能统一生效的。微信小程序里的 modal 通常不是用原生 modal 组件完成,而是通过 <view> 节点配合 wx:if、hidden 或类名切换来实现。动画时长由 duration 承担,但它分布在 wx.createAnimation 的 step 方法以及 CSS 的 transition-duration、animation-duration 中。只有把这些位置的值同时理顺,才能让自定义 modal 出现和消失的节奏符合预期。

一、自定义 modal 的动画实现与 duration 参数位置
微信小程序自带 modal 组件虽然可以直接弹出,但样式可定制性差,动画时长也不容易单独调整。因此多数项目会自己用 <view> 覆盖层和内容面板来搭建弹窗。自定义 modal 的动画通常有两种实现路径:一是通过 wx.createAnimation 生成动画数据并绑定到节点;二是通过 CSS 的 transition 或 animation 属性,在类名切换时触发过渡效果。
duration 参数在这两条路径中并不是同一个入口。在 JS 路径下,它既可以写在 wx.createAnimation 的构造参数中,也可以写在某个 step 方法的参数里,单位都是毫秒。在 CSS 路径下,它对应 transition-duration 或者 animations 简写中的时间值。理解这些位置的区别,是正确设置自定义 modal 动画快慢的前提。
二、wx.createAnimation 中 duration 的配置细节
wx.createAnimation 接收一个配置对象,可以预设 duration、timingFunction、delay 等参数。如果不传 duration,默认值为 400 毫秒。这个默认值在弹窗动画里往往偏慢,所以很多时候需要显式覆盖。除了构造参数,animation.step() 方法也能接收一个配置对象,里面同样支持 duration 和 timingFunction。当 step 中设置了 duration 时,它会覆盖构造参数中的默认值。
多个 step 可以串联成一段分段动画。比如让弹窗先从下方 40rpx 处透明状态开始,再过渡到完全可见,就可以先设置一个时长为 0 的初始状态,再执行真正的显示动画。下面是一个用 wx.createAnimation 控制自定义 modal 弹出的示例:
Page({
data: {
animationData: {}
},
showModal() {
const animation = wx.createAnimation({
duration: 0
});
animation.translateY(40).opacity(0).step({
duration: 0
});
animation.translateY(0).opacity(1).step({
duration: 300,
timingFunction: 'ease-out'
});
this.setData({
animationData: animation.export()
});
}
});这段代码中的第一个 step 把 duration 设为 0,目的是让弹窗先瞬间处于透明且向下偏移的状态,避免初始帧闪烁。第二个 step 将 duration 设为 300 毫秒,并指定缓动函数 ease-out,这样弹窗会在 0.3 秒内平滑上移并变得不透明。如果先后有多个 step,这一段完整动画的总时长等于各步 duration 之和,因此可以灵活组合出分段节奏。
动画数据生成后,需要通过 WXML 绑定到对应的 <view> 节点上。例如:
<view class="modal-mask" animation="{{animationData}}">
<view class="modal-box">
<text>自定义弹窗内容</text>
</view>
</view>这里 animation 是微信小程序节点上的内置属性,用来接收 wx.createAnimation 导出的动画数据。如果绑定的动画数据发生变化,节点就会按照新的数据重新执行动画。因此隐藏弹窗时,也可以再生成一段指向透明状态的动画数据,用 setData 更新即可。
三、CSS transition 和 animation 的时长控制
CSS 方式更适合节点常驻、通过类名切换来控制显示状态的场景。如果自定义 modal 使用 wx:if 直接控制节点挂载,节点在创建后立刻添加过渡类,浏览器或小程序渲染层往往来不及处理初始状态,导致过渡失效。为了让 CSS 过渡稳定生效,通常会让弹窗节点始终存在,通过 visibility 和 opacity 共同控制可见性。
下面是一个使用类名切换和 transition 控制弹窗动画的 WXML 结构:
<view class="modal-root {{show ? 'modal-root--visible' : ''}}">
<view class="modal-panel">
<text>用类名切换控制动画时长</text>
</view>
</view>对应的 WXSS 可以这样写:
.modal-root {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
visibility: hidden;
opacity: 0;
transition: opacity 300ms ease, visibility 300ms ease;
pointer-events: none;
}
.modal-root--visible {
visibility: visible;
opacity: 1;
pointer-events: auto;
}这里的 transition 中写了两个 300ms,它们就是控制时长的关键。为何要对 visibility 也设置过渡时长?因为如果只对 opacity 做过渡,隐藏动画结束后元素虽然透明了,但 visibility 仍然保持 visible,会挡住下层点击事件。让 visibility 与 opacity 同步过渡,能保证淡出动画结束后再彻底移除交互层。若希望隐藏动画比显示动画更快,可以在隐藏类中单独指定更短的过渡时间。
除了 transition,还可以使用 CSS animation 的简写形式直接控制进入和离开动画时长。例如:
.modal-root {
animation: modal-fade-out 200ms ease-in forwards;
}
.modal-root--visible {
animation: modal-fade-in 300ms ease-out forwards;
}
@keyframes modal-fade-in {
from {
opacity: 0;
transform: translateY(40rpx);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes modal-fade-out {
from {
opacity: 1;
}
to {
opacity: 0;
}
}在上述代码中,200ms 和 300ms 分别对应隐藏和显示动画的 duration。进入动画略长、退出动画略短,符合移动端交互习惯。动画中优先使用 opacity 和 transform,可以避免触发大量重排,使弹窗动画在低端机型上也保持流畅。
四、动画时长设置与交互节奏建议
自定义 modal 的 duration 并不是越长越柔和,也不是越短越高效。弹窗动画主要起两个作用:一是引导用户注意力,二是给内容切换一个缓冲。如果时间过短,弹出会像闪烁一样突然出现;如果时间过长,用户会感觉操作被拖慢。从移动端实践经验看,显示动画建议设置在 200 到 300 毫秒之间,隐藏动画可以稍短,通常 150 到 250 毫秒即可。
除了具体数值,缓动函数也要与 duration 搭配。同样的 300 毫秒,使用 ease-out 会让动画先快后慢,适合弹窗进入;使用 ease-in 则会让动画先慢后快,适合弹窗关闭。若只加大 duration 而不调整缓动函数,动画中段可能显得拖沓。比如 500 毫秒配合 linear,在移动端会明显感觉反应迟缓。
- 使用
wx.createAnimation时,务必把时长写在step参数中,而不是写在export之后。 - 多个动画状态通过
setData更新时,避免短时间内覆盖同一条animationData,否则动画会中断。 - 如果采用 CSS transition,弹窗节点不要直接使用
wx:if删除,否则退出动画无法正常播放。 - 显示和隐藏的时长应分开定义,不要为了省事统一使用 300 毫秒,退出动画略短体验更好。
- 真机调试时要重点观察低端 Android 设备上的动画表现,必要时把时长整体缩短 50 到 100 毫秒。
控制自定义 modal 动画快慢,本质上是把每一段状态切换的时间成本和视觉预期对齐。无论是 JS 动画实例还是 CSS 过渡,duration 都只是一个基础参数,真正影响体验的是它与缓动函数、动画方向以及节点渲染方式之间的配合。把显示和隐藏路径拆开设置,并在真机上实际感受节奏,通常能得到比固定使用默认值更好的效果。

