我们经常在电商网站上买东西,当我们将心仪的商品加入购物车时,可能碰到下面的画面。
注意小球的运动,请自主忽略画技;
小球的运动轨迹呈现抛物线,且小球的体积由大变小在由小变大;对于一个编程人员,不探究其原理都可能睡不着,所以就研究一下其原理;
首先小球的运动轨迹是一个抛物线,则我们可以使用抛物线方程来计算水平方向和垂直方向的关系;
求抛物线的方程首先的需要至少最少三个坐标点,当然三个坐标点你肯定知道了或者你可以设定,开始与结束的坐标点是一定的,第三个坐标点是小球运动的最高点;
再者小球的体积变化和小球水平运动也可以使用方程来表示:
y轴表示小球的缩放变化,小球的变化是通过transform: scale(xxx)来表示,如scale变化从1 –> 0.1 –> 1;
注意:
小球在屏幕运动的最小单位是1px,所以你计算的时候需要把运动结果的取整;
<main class="main flex-center">
<section class="rectangle">
<section class="wrapper max-size">
<span class="ball">span>
section>
section>
main>
html, body, .main {
width: 100%;
height: 100%;
overflow: hidden;
background: #000;
}
.main {
position: relative;
.rectangle {
position: absolute;
width: 50%;
height: 400px;
.wrapper {
position: relative;
.ball {
position: absolute;
left: 11rem;
top: 380px;
display: inline-block;
width: 20px;
height: 20px;
border-radius: 50%;
background: #fff;
}
&:before, &:after {
position: absolute;
background: #f00;
content: '';
display: block;
}
&:before {
left: -1px;
bottom: px2rem(-20);
width: 1px;
height: 100%;
}
&:after {
left: px2rem(-20);
bottom: -1px;
width: 100%;
height: 1px;
}
}
}
}
class BallAnimation {
constructor() {
this.ball = document.querySelector('.rectangle .ball');
this.startPosition = {
x: this.ball.offsetLeft,
y: this.ball.offsetTop
};
this.endPosition = {
x: 37,
y: 389
};
this.timer = null;
this.duaration = 800;
this.timerTime = 60;
this.diffDis = Math.floor(this.endPosition.x - this.startPosition.x);
this.direct = this.diffDis > 0? 1 : -1;
this.counts = this.duaration / this.timerTime;
this.speed = Math.floor(this.diffDis / this.counts);
TB.print('constructor function: ', {
startPosition: this.startPosition,
speed: this.speed
});
}
init() {
this.start();
}
getBallPositionY(x) {
return (-90/7569) * x * x + (22320/7569) * x -746783/7569;
}
getBallScale(x) {
let middle = Math.floor(Math.abs(this.endPosition.x - this.startPosition.x));
if (x < middle){
return (-9/970) * x + (1303/970);
} else {
return (9/770) * x - (1129/770);
}
}
setBallStyle(x = 0) {
this.ball.style.left = x + 'px';
this.ball.style.top = this.endPosition.y - Math.floor(this.getBallPositionY(x)) + 'px';
this.ball.style.transform = 'scale(' + parseFloat(this.getBallScale(x), 2) + ')';
}
start() {
let surplusDistance = Math.floor(Math.abs(this.endPosition.x - this.startPosition.x) % Math.abs(this.speed));
this.endPosition.x = this.endPosition.x + this.direct * -1 * surplusDistance;
this.timer = setInterval(() => {
let _left;
if (this.ball.offsetLeft == this.endPosition.x) {
_left = this.ball.offsetLeft + (this.direct * surplusDistance);
this.setBallStyle(_left);
clearInterval(this.timer);
this.timer = null;
} else {
_left = this.ball.offsetLeft + this.speed;
this.setBallStyle(_left);
}
}, this.timerTime);
}
}
let ballAnimate = new BallAnimation();
ballAnimate.init();
而垂直方向和小球体积变化都与水平的方向都相同,所以只需要关心水平方向的运动;
当然我们都清楚任何运动都是通过定时器来实现,定时器的每次变化其实水平方向的运动一部分,但是通过js中的speed变量可知,运动到最后可知不可能完美的到达终点,实际上会超出一些,所以我们将最后不足的一部分距离单独拿出来,最后将该距离作为speed,则这样的话就可以100%到达终点;
再来复习一下高中的求方程吧:
抛物线方程: a*x*x + b*x + c = y;
坐标点:(37, 10) (124, 100) (211, 10);
(37 * 37)a + 37b + c = 10;
(124 * 124)a + 124b + c = 100;
(211 * 211)a + 211b + c = 10;
(211*211 - 37*37)a + (211 - 37)b = 0; => 43152a + 174b = 0;
(124*124 - 37*37)a + (124 - 37)b = 90; => 14007a + 87b = 90; => 28014a + 174b = 180;
15138a = -180;
a = (-90/7569);
87b = 90 + 14007 * (90/7569); => (90* 7569 + 14007 * 90)/7569;
b = 22320/7569;
c = (10*7569 + 37*37*90 - 37*22320)/7569;
c = (-746783/7569);
直线方程
(37, 1) (134, 0.1) (211, 1)
37a + b = 1;
134a + b = 0.1;
211a + b = 1;
97a = -0.9;
a = -9/970;
b = 1303/970;
77a = 0.9;
a = 9 /770;
b = -1129/770;