我们时常会遇到这样的需求:让物体平滑地移动至某处。一般来说,匀速地移动总是显得过于死板,不够 fancy,因此有变速移动的需求,即「缓动」。
这个问题的本质可以转化到对运动规律的设计上。我们都知道几种基本运动规律:匀速运动、匀加速运动、变加速运动的运动性质,要做的只是实现它们罢了。不过物理课上对它们的研究主要还是停留在连续域上,计算机世界是离散的,因此我们需要对这些运动规律做一个离散域的 “移植”。
昨天我闲来无事,做了一个小的缓动跟随 Demo 如下。你可以试着以不同速度用鼠标拖动我的头像,观察炮姐头像的运动规律(手机上可能不好使)。
当我的移动速度比较慢时,炮姐会以相对平均的速度慢慢追上我;当我的速度突然变快,我和炮姐之间的距离会被拉开,炮姐就会加快步伐追我,最后慢慢地停在我的边上。
这种运动规律与 “离开多少追上多少” 那种规律不同,在那种规律下,两次界面重绘之间我和炮姐拉开了多少距离,在重绘时就会立马被补上,是不是显得没什么人情味?
观察上面那个 Demo 中的 JS,核心代码是:
function followMe(){
var me=$("#me").offset();
var paojie=$("#paojie").offset();
if(distance(me,paojie)>=100){
var moveX=(me.left-paojie.left)*0.002;
var moveY=(me.top-paojie.top)*0.002;
$("#paojie").css("left",paojie.left+moveX+"px");
$("#paojie").css("top",paojie.top+moveY+"px");
requestAnimationFrame(followMe);
}
}
其中 distance()
函数用于计算我和炮姐之间的曼哈顿距离:
function distance(x,y){
return Math.abs(x.left-y.left)+Math.abs(x.top-y.top);
}
即:每两次重绘(即每两次调用 requestAnimationFrame()
)之间,炮姐在每根轴上的移动距离是我和炮姐的间隔 × 0.002,直至我和炮姐的距离小于 100 像素,函数返回不再追及。随着我和炮姐之间的距离逐渐变短,炮姐每次移动的距离也变小,实现了 “减速” 的效果,有兴趣的话可以研究一下这到底属于哪一种运动规律。0.002
这个参数决定了追逐的速度,越大越快,你可以自己修改这个值,也可以试试在每根轴上采用不同的追逐速度。
说到这里我想到一件事。在工业设计领域,遇到直线与圆角连接时一般要保证连接点两边二阶导数连续才能算 “平滑”。例如 iPhoneX 的显示界面圆角,并不是圆弧与直线连接,而是特别设计的弧,使得它在与直线的连接处足够平滑。不过这是题外话了。