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

如何在动画运行时修改Element.animate()的结束关键帧?

动态调整Element.animate()关键帧实现鼠标速度条平滑过渡

核心问题分析

你当前的问题在于每次调用animate()都会创建新的动画实例,且固定设置结束帧为0px,导致动画无法跟随鼠标速度动态调整目标值。要实现平滑过渡,需要复用同一个动画实例,并在每次速度更新时动态修改其关键帧的结束值。

修复后的完整代码

HTML

<div id="mouseSpeedIndicator"></div>

CSS

div#mouseSpeedIndicator {
  position: fixed;
  bottom: 0px;
  left: 0px;
  height: 33px;
  background-color: green;
  max-width: 100vw; /* 修正为视口宽度,原100vh是视口高度,不符合需求 */
  border-top-right-radius: 10px;
}

JavaScript

// 修复后的鼠标速度追踪器
document.addEventListener('DOMContentLoaded', mausgeschwindigkeitVerfolgen, { once: true });
function mausgeschwindigkeitVerfolgen() {
    // 初始值设为0,避免NaN问题
    let speedX = 0;
    let speedY = 0;
    let posX = 0;
    let posY = 0;
    let speed = 0;

    document.addEventListener("mousemove", function(ev){
        speedX += Math.abs(ev.movementX);
        speedY += Math.abs(ev.movementY);
        speed = 10 * Math.sqrt(ev.movementX ** 2 + ev.movementY ** 2);
        window.mousePosition = { x: posX = ev.clientX, y: posY = ev.clientY };
    }, false);

    setInterval(function(){
        window.mouseSpeed = { x: speedX, y: speedY, t: speed };
        window.mousePosition = { x: posX, y: posY };
        // 重置累计速度,原代码的totalX/totalY未定义,改为speedX/speedY
        speedX = speedY = speed = 0;
    }, 100);

    window.mausgeschwindigkeitVerfolgen = () => ({
        speed: window.mouseSpeed,
        pos: window.mousePosition
    });
    return { speed: window.mouseSpeed, pos: window.mousePosition };
}

// --- 动画实现代码 ---
const indicator = document.querySelector('div#mouseSpeedIndicator');
let animation = null;

// 初始化动画,设置初始关键帧
function initAnimation(targetWidth) {
    animation = indicator.animate(
        [{ width: '0px' }, { width: `${targetWidth}px` }],
        { duration: 200, easing: 'ease-out', fill: 'forwards' }
    );
    animation.pause();
}

// 动态更新动画目标值
function updateAnimation(newWidth) {
    if (!animation) {
        initAnimation(newWidth);
        animation.play();
        return;
    }

    // 获取当前动画的进度对应的实际宽度
    const currentWidth = parseFloat(getComputedStyle(indicator).width);
    
    // 暂停动画,更新关键帧
    animation.pause();
    animation.effect.setKeyframes([
        { width: `${currentWidth}px` },
        { width: `${Math.min(newWidth, window.innerWidth)}px` } // 限制最大宽度不超过视口
    ]);
    // 重置动画进度并播放
    animation.currentTime = 0;
    animation.play();
}

// 每100ms更新一次动画目标
setInterval(() => {
    if (window.mouseSpeed?.t !== undefined) {
        updateAnimation(window.mouseSpeed.t);
    }
}, 100);

关键实现细节

  1. 复用动画实例:只创建一个Animation对象,避免每次更新都生成新动画导致的跳变。
  2. 动态修改关键帧:每次更新速度时,先获取元素当前的实际宽度作为动画起始帧,新的鼠标速度值作为结束帧,调用effect.setKeyframes()更新动画。
  3. 状态控制:暂停动画后修改关键帧,再重置播放进度并继续播放,保证平滑过渡。
  4. 修复原追踪器问题:将初始变量从NaN改为0,修正重置时未定义的变量错误,避免速度计算异常。

补充说明

  • 动画时长设为200ms是为了和100ms的速度更新间隔配合,让过渡更自然,可根据需求调整。
  • 添加了Math.min(newWidth, window.innerWidth)限制最大宽度,避免超出视口范围。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 04:40:28