我用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属性,以便其减速速度比this.friction属性所允许的自然速度更快.
我很高兴将阻尼区域硬编码为内容顶部/底部的300px.或者,也可以选择一定比例的容器高度.
任何帮助将不胜感激.
解决方法:
可以通过独立于鼠标滚轮速度的惯性运动(带摩擦)将速度衰减到足以仅触摸顶部或底部边缘的值.
假设在当前方向上给出了行进距离x.因此,需要找到速度v0,该速度足以使惯性运动带摩擦地移动该距离.
当前的滚动动力学(vy * =摩擦力)对应于层流.它可以写成微分方程:
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/