You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Javascript中使用@keyframes实现CSS动画中途JS接管效果

CSS @keyframes 动画运行到90%时由JS接管的实现方案

核心实现思路

要保证动画全程连贯无卡顿,需满足两个要求:精准捕获90%进度的时间节点,接管时直接继承元素当前的实际动画状态,避免视觉跳变。

第一步:调整CSS配置

先补全@keyframes的完整动画路径,设置好动画总时长,添加forwards属性保证动画结束前保持当前状态:

@keyframes spin {
  0% {
    transform: rotate(0deg);
  }
  50% {
    transform: rotate(1080deg);
  }
  100% {
    /* 先补全CSS端的终点状态,用于进度计算,实际到90%就会被JS接管 */
    transform: rotate(2160deg);
  }
}

/* 动画绑定的元素示例 */
#spin-container {
  width: 100px;
  height: 100px;
  background: #2563eb;
  /* 动画配置:总时长10s、线性运动、保持最后一帧状态,可根据需求调整 */
  animation: spin 10s linear forwards;
}

第二步:JS逻辑实现

通过animationstart事件获取动画准确启动时间,用requestAnimationFrame轮询进度,到90%时接管动画:

const container = document.getElementById('spin-container');
// 和CSS配置保持一致的总动画时长,单位毫秒
const TOTAL_DURATION = 10 * 1000;
// 90%进度对应的接管时间点
const HANDOVER_POINT = TOTAL_DURATION * 0.9;
let animationStartTime = null;
let isJsControl = false;

// 监听动画启动事件,获取精准启动时间
container.addEventListener('animationstart', (e) => {
  if (e.animationName === 'spin') {
    animationStartTime = performance.now();
    requestAnimationFrame(checkProgress);
  }
});

function checkProgress() {
  if (isJsControl) return;
  const runTime = performance.now() - animationStartTime;

  // 到达接管时间点
  if (runTime >= HANDOVER_POINT) {
    // 取消CSS动画,避免和JS控制冲突
    container.style.animation = 'none';
    // 读取元素当前实际的旋转角度,保证衔接无跳变
    const matrix = new DOMMatrix(getComputedStyle(container).transform);
    let currentDeg = Math.atan2(matrix.b, matrix.a) * 180 / Math.PI;
    // 处理角度为负值的情况
    currentDeg = currentDeg < 0 ? currentDeg + 360 : currentDeg;
    // 补全多圈旋转的总角度,避免只取到0-360区间的数值
    const expectedDeg = 2160 * 0.9;
    currentDeg += Math.floor(expectedDeg / 360) * 360;

    isJsControl = true;
    // 后续JS自定义动画逻辑,可替换为你的业务逻辑
    function runJsAnimation() {
      currentDeg += 1.5; // 自定义旋转速度
      container.style.transform = `rotate(${currentDeg}deg)`;
      requestAnimationFrame(runJsAnimation);
    }
    runJsAnimation();
    return;
  }
  requestAnimationFrame(checkProgress);
}

注意事项

  • 如果动画使用非匀速的缓动函数,不要依赖预计算的角度值,直接以getComputedStyle读取到的实际变换矩阵计算的角度为准,避免进度计算偏差
  • 不要用setTimeout做进度监听,主线程阻塞会导致时间偏差,requestAnimationFrame和动画刷新频率同步,进度判断更准确
  • 接管时必须先取消CSS动画,否则CSS的动画规则会和JS设置的transform属性冲突,出现元素抖动的情况

内容的提问来源于stack exchange,提问作者DavidJambonski

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 13:39:02