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

如何基于时长、起止位置计算JavaScript滚动的acceleration值?

问题

我用background-image和background-position-x实现滚动功能,点击Roll按钮触发roll()函数。功能里有起始位置startPosition和结束位置endPosition,roll.style.backgroundPosition的X值由currentPosition变量控制。

想要实现先快后慢的滚动效果,直到currentPosition等于endPosition,每次滚动时长固定在3-4秒左右。

但调整acceleration值时遇到问题:

  • 调大acceleration,currentPosition可能变成负数并无限循环;
  • 调小acceleration,如果速度没降下来,会因为currentPosition提前到达endPosition而提前停止滚动。

而且每次执行时startPosition和endPosition是随机的,请问怎么基于duration、startPosition和endPosition精确计算acceleration的取值?

附带当前代码:

function roll() {
    let startPosition = 1000;
    let endPosition = 5000;
    let currentPosition = startPosition;
    let initialSpeed = 10;
    let duration = 4000;
    let startTime = 0;
    const acceleration = 0.1;
    const roll = document.querySelector('.rotation')

    function updatePosition() {
        startTime += 5
        initialSpeed -= acceleration
        
        initialSpeed = Math.max(initialSpeed, 1);

        currentPosition += initialSpeed;
        if (currentPosition > endPosition) {
            currentPosition = endPosition;
        }

        
        roll.style.backgroundPosition = `${currentPosition}px center`;

        
        if (currentPosition < endPosition) {
            requestAnimationFrame(updatePosition);
        }
    }

    updatePosition();
}
解决方案

你的核心问题是用线性减速的方式控制滚动,但未结合总距离和总时长计算参数,导致要么提前停止要么循环。推荐用缓动函数实现先快后慢的效果,既无需手动计算加速度,还能精准控制时长,适配任意随机的起始/结束位置。

思路说明

  1. 放弃手动维护initialSpeed和acceleration,改用时间比例计算当前位置:
    • 计算滚动总距离:totalDistance = endPosition - startPosition
    • 记录滚动开始的时间戳,每次更新时计算已流逝时间占总时长的比例t(范围0~1)
  2. 用缓动函数(比如easeOutQuad)把线性时间比例转换成非线性位置比例,实现先快后慢的效果
  3. 根据位置比例计算当前currentPosition,直接赋值给backgroundPosition

修改后的代码

function roll() {
    const startPosition = 1000;
    const endPosition = 5000;
    const duration = 4000; // 固定4秒,可改成3000~4000的随机值
    const roll = document.querySelector('.rotation');
    const startTime = performance.now();
    const totalDistance = endPosition - startPosition;

    // 缓动函数:easeOutQuad,实现先快后慢的效果
    function easeOutQuad(t) {
        return t * (2 - t);
    }

    function updatePosition() {
        const elapsed = performance.now() - startTime;
        // 计算时间比例,超过1就取1(防止超时)
        const progress = Math.min(elapsed / duration, 1);
        // 用缓动函数计算位置比例,再算出当前位置
        const currentPosition = startPosition + totalDistance * easeOutQuad(progress);
        
        roll.style.backgroundPosition = `${currentPosition}px center`;

        if (progress < 1) {
            requestAnimationFrame(updatePosition);
        } else {
            // 确保最终位置精准落在endPosition
            roll.style.backgroundPosition = `${endPosition}px center`;
        }
    }

    updatePosition();
}

关键优势

  • 完全不需要计算加速度,适配任意startPosition和endPosition的随机组合
  • 滚动时长严格控制在duration设定的时间内,不会提前结束或超时
  • 缓动函数可以替换成其他类型(比如easeOutCubic),调整减速曲线,满足不同需求
  • 不会出现currentPosition负数或无限循环的问题

如果坚持用加速度计算(不推荐)

如果一定要用线性减速的方式,需要基于总距离和时长推导公式:
假设初始速度为v0,加速度为a(减速时a为正,速度逐渐减小),总时长为T,总距离为S。
线性减速到速度为0的公式:
S = v0*T - 0.5*a*T²
v0 = a*T(因为最终速度为0,v0 - a*T = 0)
代入得:S = 0.5*a*T² → a = 2*S / T²
但要注意:

  • 时间单位要统一(比如T用秒,S用像素,a的单位是像素/秒²)
  • 如果用requestAnimationFrame,需要把时间转换成帧间隔(通常约16ms)来计算每帧的速度变化
  • 这种方式不如缓动函数灵活,且容易出现边界问题(比如总距离太小导致初始速度过大)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 04:13:10