背景
过年的项目中遇到一个问题让我百思不得其解,明明我的帧率保持在60帧,为何我的动画却一直抖动?
我的场景是一个匀速直线运动的物体。
先上一个 Demo
在这个 Demo 中,小姐姐是按照 x 轴 10px/s,y 轴 30 px/s 进行移动的,不过她的移动是明显伴随着抖动的。
这到底是怎么了呢?
解决
如果小姐姐的y轴速度是10px/s,我们的帧率是60f/s,计算一下:
10 / 60 = 1/6 (px/f)
实际上,的实际速度是每 6 帧才会移动 1px,这当然会有抖动,小姐姐走一步停一会,当然会抖喽~
我索性把小姐姐的移动速度调快,调成 100px/s,发现,还是会抖动,以为高高兴兴能解决了这个问题,发现还是没那么简单。
既然我们能算,那我们就算一算
100 / 60 = 10/6 (px/f) = 1.666666....(px/f)
写了个for循环,看看一秒中每一帧小姐姐都在什么位置
for(let i = 0; i < 60; i ++) {
console.log(i*10/6)
}
输出结果是这样的:
0 1.66 3.33 4.99 6.66 8.33 9.99 11.66 13.33 14.99 16.66 18.33 19.99 21.66 23.33 24.99 26.66 28.33 29.99 31.66
33.33 34.99 36.66 38.33 39.99 41.66 43.33 44.99 46.66 48.33 49.99 51.66 53.33 54.99 56.66 58.33 59.99 61.66 63.33 64.99
66.66 68.33 69.99 71.66 73.33 74.99 76.66 78.33 79.99 81.66 83.33 84.99 86.66 88.33 89.99 91.66 93.33 94.99 96.66 98.33
那么作为小数,Canvas 将如何定位呢?
我们来写一个 Demo
使用 Chrome 打开,作为一个像素眼,我发现,小姐姐定位在 50.6px 的时候,其实就已经被渲染到 51px 的位置。
所以在Chrome中,应该使用了 Banker's rounding 来处理小数的问题。
所以真正的位置其实是
0 2 3 5 7 8 10 12 13 15 17 18 20 22 23 25 27 28 30 32
33 35 37 38 40 42 43 45 47 48 50 52 53 55 57 58 60 62 63 65
67 68 70 72 73 75 77 78 80 82 83 85 87 88 90 92 93 95 97 98
从数值来看,每帧移动的距离可能是 1px 也可能是 2px,小姐姐可能是在边跳芭蕾边走路喽~
既然这样,60 帧的帧率下,设置 60px/s 就可以解决问题了,尝试了一下,真的可以!
总结
前端动画/游戏开发 requestAnimationFrame 之 锁帧 这篇文章介绍过,在项目中我们可能对动画进行锁帧,帧率可能是 60 或者 30,如果我们想保证渲染不抖动,在匀速直线运动中,我们尽量保证我们设置的速度要是帧率的倍数,或者保证平均每帧移动的像素点是一样的。