闲来无事。发现自己没有……没有……没有亲手写过 无缝滚动 !!! ZZ 一样的混等。
1 |
|
分析一下
我们这里用 scrollLeft 来实现。分析清楚以后,offsetLeft细节虽然不同,但道理是一样。
假设容器宽度为 W ,内容宽度为 L 。(满足 L > W,我觉得 L <= W 没必要无缝滚动,你非要滚动请自行设计。)
假设容器的scrollLeft值为 x,开始,x = 0;然后 x 不停增加,一直移动到右边 x = L - W(滚不动了,x大于 L-W,也会被设置为 L-W)。
x ∈ [0, L-W] ⊂ N。- Element.scrollLeft,那个rtl我在 Chrome 71.0.3578.98 测试,和描述不符。不兼容吧。我们这里不要理会 rtl。
我们要的效果
为了能让红色块滚出去,我们把它复制一份,蓝色的。 此时 x ∈ [0, 2L-W]。
显然,开始x=0,不断增加 x,内容就向左滚动了,当 x>=L 时,红色块滚了出去,显示的是蓝色块部分内容。关键是,蓝色块和红色块的长相是一样的。如果我们滚出了红色块,进入了蓝色块,我们只要马上跳转到相应的红色块位置即可。
当 x ∈ [L, 2L-W],显示的是蓝色块。 显示范围对应于 [x, x+W-1],由于周期性,它与 [x-L, (x+W-1)-L] 显示完全一样。 这就是无缝而动的本质。
那么这一句跳回去,
1 | if(demo.scrollLeft >= listA.offsetWidth) { |
可以改为
1 | const L = listA.offsetWidth; |
动起来?
- deltaX 多大? 每一次移动,走多少距离合适?
因为 x ∈ [0, 2L-W],显然,1 <= deltaX <= 2L-W。
deltaX=1 看起来就很好用。
再看deltaX=2L-W。你一下滚到头了。步子迈得太大,容易扯蛋。初始 x=0
- 第一次 goLeft 后,x=2L-W,
- 第二次 goLeft 后,x=2L-W,(满足跳转条件,x减去L,即 x=L-W,加个deltaX,3L-2W,过头了,又变成了 2L-W,x=2L-W)
- 第三次 goLeft 后,x=2L-W,同上
- ……
完全没有动画效果,而且我认为这个deltaX无效,为啥?后面每次都加过头了。瞎几把搞。
deltaX上限多少?就是每次加上去都不会过头。
也就是每次 goLeft 都满足表达式 x + deltaX <= 2L-W。
如果用 demo.scrollLeft >= BOUNDARY
,会很复杂。我们就简化一下,还是用原来的,demo.scrollLeft >= listA.offsetWidth
,即 x 大于等于 L 时,就需要减去 L,然后再计算上面的表达式。
假设:
xm-1 < L,
xm >= L,
其中 x0=0,xn = xn-1 + deltaX,
由于 xm 满足跳转条件,此时 x=xm - L,
(xm - L) + deltaX <= 2L-W, 令 xm=2L-W,得 deltaX <= L;
(xm-1 + delataX - L) + deltaX <= 2L-W, 令 xm-1=L-1,得 deltaX <= L - 0.5W + 0.5
同时 deltaX = xm - xm-1 = (2L-W) - (L-1) = L - W + 1
所以 当 deltaX 小于 L-W+1 时,是有效的;而当 deltaX 小于 2L-W 时,是合理的。因为 scrollLeft 有自己的上限。
- 怎么动起来?我们取 deltaX 为个位数就行了,一般 L 都要比 W 大。频率取值大于24(人眼?),小于60(显示器刷新?)即可。 即16ms < deltaT < 42ms。若 deltaT=25,deltaX=4,大概每秒能移动160距离。当 deltaT不变时,deltaX变化就能调整速度,比如
deltaX=10
或者deltaX=Math.round((L-W+1)*Math.random())
;
然后, setInterval(goLeft, deltaT) 就完事了。
向右,向上,向下,斜着滚动,都可以做同样的分析。