当我的惯性滚动算法接近顶部或底部时,如何减速?

时间:2015-10-21 23:13:43

标签: javascript animation physics

我在javascript中为鼠标轮编写了一个小惯性滚动算法。

它完全符合我的需要,但有一部分缺失,我似乎无法达到预期的行为。

当用户滚动到容器的末尾时,无论是顶部还是底部。我想动力自然减速到停止。目前它只是当它碰到任何一个边缘时立即停止,无论它目前的速度如何。

我没有在这里发布一堆代码,而是创建了一个小的jsfiddle来说明:

https://jsfiddle.net/o8xLw68L/8/

这是我当前代码的简化版本。如果您取消注释行111如果从div的顶部向下滚动一点,然后快速合理地向上滑动鼠标滚轮,您可以看到我正在寻找的行为。你会看到动量在0位置自然缓慢。

Inertial.prototype.smoothWheel = function(amt) {
     this.targetY += amt;

     //uncomment this line to see the decelleration almost work against the top edge of the container
     //this.targetY = Math.max(0, this.targetY);

     this.vy += (this.targetY - this.oldY) * this.stepAmt;
     this.oldY = this.targetY;
}

这种方法的问题在于它只会在鼠标滚轮发出脉冲时抑制产生的this.vy属性,因此并不总是正常工作,因为用户可能会从容器中的较低位置滚动,并且速度更快,但没有任何持续的鼠标轮脉冲。 (这很难说清楚,jsFiddle应该更清楚)

解决方案可能需要以某种方式抑制this.vy属性,因为我们将sclose放到容器的顶部或底部,因此它比自然this.friction属性允许的速度更快

当您到达内容的顶部/底部的300px时,我很高兴要将阻尼区域硬编码。或者,容器高度的百分比也会起作用。

非常感谢任何帮助。

1 个答案:

答案 0 :(得分:3)

可以将速度抑制到足以仅通过惯性运动(摩擦力)触及顶部或底部边缘的值,而与鼠标轮速度无关。

假设在当前方向上给出了行程x的距离。因此,需要找到速度v0,该速度足以通过惯性运动与摩擦一起行进该距离。

当前滚动动态(vy *= friction)对应于层流流动。它可以写成微分方程:

dv = - (1 - friction) * v * dt; // velocity change dv in time interval dt
dv / v = - (1 - friction) * dt;
// integrating lhs and rhs with initial conditions (v = v0 at t = 0)
ln(v/v0) = - (1 - friction) * t;
v = v0 * exp(- (1 - friction) * t);

因此,随着时间的推移,速度从v0呈指数衰减到零。

旅行距离:

dx = v * dt = v0 * exp(- (1 - friction) * t) * dt;
// integrating, initial conditions x = 0 at t = 0
x = v0 / (1 - friction) * (1 - exp(- (1 - friction) * t))

因此,可以在无限时间内以起始速度v0行进以下距离:

x = v0 / (1 - friction);

基于留给边缘的距离,可以约束速度:

Inertial.prototype.boundVelocity = function () {
    var dist = 0;

    if (this.dir == 1)
        dist = this.scrollerPos;
    else if (this.dir == -1)
        dist = this.contentHeight - this.scrollerHeight - this.scrollerPos;

    var maxv = dist * (1 - this.friction) + 1;

    if (Math.abs(this.vy) > maxv) {
        console.log('reduce velocity ' + this.vy + ' to ' + (-maxv * this.dir) + ', dist: ' + dist);
        this.vy = -maxv * this.dir;
    }
}

maxv+1)有一些小的非零最小值,以确保尽管存在离散误差,但始终会检测边缘。当smoothWheel()中的速度可以增加时调用该函数,并且在render()中调用该函数只是为了避免数值误差累积。

可运行的示例:https://jsfiddle.net/c675bkn9/