更新CSS动画参数时实现平滑过渡的方法
问题描述
现有一个id为box的HTML元素:
<div id="box"></div>
初始设置该盒子4秒内向右移动200像素,CSS代码如下:
.anim { animation-name: anim; animation-duration: 4s; animation-timing-function: linear; /* cubic-bezier(0.1, -0.6, 0.2, 0); */ } @keyframes anim { 0% { left: var(--xs); } 100% { left: var(--xe); } }
启动动画的JavaScript代码:
var box = document.getElementById('box'); box.style.setProperty('--xs', '0px'); box.style.setProperty('--xe', '200px'); box.classList.add('anim');
1秒后尝试将最终位置更新为400像素:
setTimeout(() => { box.style.setProperty('--xe', '400px'); },1000)
此时盒子会出现位置跳变,需实现平滑过渡避免“瞬移”效果。
解决方案
问题根源在于CSS动画启动后,关键帧参数的更新会直接基于新值重新计算整个动画进度,导致位置跳变。以下两种方式可实现平滑衔接:
方法一:基于现有动画状态重置动画
通过获取当前动画的实时进度,重新计算起始位置、剩余时长,再重启动画:
var box = document.getElementById('box'); // 初始化动画 box.style.setProperty('--xs', '0px'); box.style.setProperty('--xe', '200px'); box.classList.add('anim'); setTimeout(() => { // 获取当前动画实例与状态 const anim = box.getAnimations()[0]; const elapsedTime = anim.currentTime; const totalDuration = anim.effect.getTiming().duration; const remainingTime = totalDuration - elapsedTime; // 获取盒子当前的实时位置 const currentLeft = parseFloat(getComputedStyle(box).left); // 清除当前动画 anim.cancel(); box.classList.remove('anim'); // 设置新的动画参数 box.style.setProperty('--xs', `${currentLeft}px`); box.style.setProperty('--xe', '400px'); box.style.animationDuration = `${remainingTime}s`; // 重启动画 box.classList.add('anim'); }, 1000);
对应的CSS需补充animation-fill-mode以保持结束状态:
.anim { animation-name: anim; animation-duration: 4s; animation-timing-function: linear; animation-fill-mode: forwards; } @keyframes anim { 0% { left: var(--xs); } 100% { left: var(--xe); } }
方法二:改用CSS过渡(transition)实现
过渡特性支持属性更新时自动平滑过渡,只需计算剩余过渡时长即可保持匀速:
#box { position: absolute; left: 0; transition: left linear; }
var box = document.getElementById('box'); // 初始设置:4秒移动到200px box.style.transitionDuration = '4s'; box.style.left = '200px'; setTimeout(() => { // 计算当前位置与剩余距离 const currentLeft = parseFloat(getComputedStyle(box).left); const remainingDistance = 400 - currentLeft; // 原速度为50px/秒,计算剩余距离所需时长 const remainingTime = (remainingDistance / 50) + 's'; // 更新过渡参数 box.style.transitionDuration = remainingTime; box.style.left = '400px'; }, 1000);
内容的提问来源于stack exchange,提问作者Fifi
相关产品推荐
相关产品推荐

