动画执行一次后消失的根本原因是终态样式被重置,必须同时满足:①@keyframes含100%/to帧并写全终态属性;②显式设置animation-fill-mode: forwards;③避开真机WebView对transform合并、canvas未声明type="2d"等渲染陷阱。

uni-app H5端在移动端 CSS 动画执行一次后消失,根本不是动画没跑,而是动画结束时样式被重置为初始值 —— 解决方案必须同时满足三个条件:显式终态、正确 fill-mode、避开真机渲染陷阱。
animation-fill-mode: forwards 为什么在 H5 真机上不生效
uni-app 编译到 H5 后,在 iOS Safari 或安卓 WebView 中,animation-fill-mode: forwards 常常“看起来没用”,实际是它拿到了错误的终态。关键原因有:
- @keyframes 里缺
100%或to帧:只写from和50%,浏览器会用元素原始transform值补全终态,而原始值可能是none,导致位移/缩放丢失 - 简写
animation漏掉forwards:例如animation: slideIn 0.3s等价于animation-fill-mode: none,必须写成animation: slideIn 0.3s forwards - 真机 WebView 对
transform合并支持差:动画中只写transform: translateX(100px),但元素已有transform: scale(0.95),结束时不会保留 scale,直接归零
uni-app H5 动画只播一次就“闪回”的真实场景
在 uni-app 的 .vue 文件中,常见写法如下,但极易出问题:
div {
animation: fadeIn 0.4s;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
这段代码在 Chrome DevTools 里看着正常,但部署到 iOS 真机后,动画播完瞬间变透明 —— 因为:
立即学习“前端免费学习笔记(深入)”;
- 没设
animation-iteration-count: 1(虽默认是 1,但 H5 环境下可能被全局样式或编译器覆盖) - 没设
animation-fill-mode: forwards,或被animation简写覆盖 - 更隐蔽的是:父容器用了
overflow: hidden+position: relative,而动画元素在终点刚好超出裁剪区,视觉上“消失”
移动端 H5 必须加的兜底措施
uni-app 的 H5 构建目标是兼容各种 WebView,不能只依赖 CSS 行为。推荐组合策略:
- 在
@keyframes的100%帧中,把所有要保留的属性一次性写全:transform: translateX(200px) scale(1) rotate(0);,不要依赖继承 - 给动画元素显式声明终态兜底样式,比如
opacity: 1、background-color: #fff,防止某段动画提前结束导致透明暴露 - 避免仅靠
display: none控制显隐:动画前用visibility: hidden; opacity: 0;,动画后用 JS 在animationend事件里补一句element.style.visibility = 'visible'; - uni-app 中可配合
uni.createSelectorQuery()做真机检测,对 iOS 加 class 降级处理(如改用left/top替代transform)
为什么 uni-app 的 JSON 动画(Lottie)在 H5 真机也“消失”
这不是 CSS 动画问题,但常被混淆。uni-app 使用 Lottie-web 渲染 JSON 动画时,在 H5 真机上消失,主因是:
-
<canvas>元素未声明type="2d"属性,某些安卓 WebView 会静默忽略渲染 - 尺寸单位用了
px而非rpx,导致 canvas 实际宽高为 0(尤其在横屏切换后) - Lottie 实例未在
onReady生命周期中初始化,而是在onLoad就调用loadAnimation(),此时 DOM 可能未挂载完成 - 未监听
loop和autoplay的真机兼容性:iOS Safari 下需手动调用anim.play(),不能只靠配置
最稳妥做法:用 setTimeout(() => anim.play(), 16) 延迟启动,确保 canvas 已渲染且上下文可用。


















