JavaScript 运动 04 —— 弹性运动
2017-05-22 本文已影响21人
柏丘君
弹性运动
弹性运动是物体在某个参考点两侧进行运动。
弹性运动.gif加速运动和减速运动
加速运动是速度逐渐增加的运动,减速运动是速度逐渐减小的运动。根据物理规律,我们知道:
- 当物体靠近参考点时,做加速运动
- 当物体原理参考点时,做减速运动
简单的弹性运动
根据上面的规律,我们来实现一个简单的弹性运动,首先修改一下页面布局,使之更适应于弹性运动:
...
#par::after{
content: "";
display: block;
width: 1px;
height: 310px;
position: absolute;
left: 50%;
top: 0;
background: red;
z-index: -1;
}
...
修改后的布局是这个样子的:
弹性运动场景.png小滑块将在红线的两侧进行弹性运动。
修改 animate 函数,实现一个最简单的弹性运动:
function animate(ele = null,config = {
accel:10,
attrs:{},
}){
// 清除定时器
clearInterval(ele.timer);
const attrs = config.attrs;
const accel = config.accel||10;
let speed = 0;
return new Promise((resolve)=>{
ele.timer = setInterval(()=>{
for(const attr in attrs){
// 设置目标距离
let target = Number.parseInt(attrs[attr]);
// 获取当前的样式
let currentStyle = (attr === "opacity")?(Number.parseInt(Number.parseFloat(getCurrentStyle(ele,attr))*100)):Number.parseInt(getCurrentStyle(ele,attr));
// 如果改变的样式是 opacity,target乘以100
if(attr === "opacity"){
target = Number.parseInt(Number.parseFloat(attrs[attr])*100);
}
// 获取速度
if(currentStyle < target){
speed += accel;
}else{
speed -= accel;
}
// 根据当前样式动态改变物体的样式
ele.style[attr] = (attr === "opacity")?( currentStyle + speed)/100:(currentStyle + speed) + "px";
}
},30);
});
}
调用运动函数:
const ele = document.getElementById("inner");
async function start(){
await animate(ele,{
accel:10,
attrs:{
left:"250px",
}
});
}
看下效果:
这里的 accel 表示加速度,当小滑块靠近红线时,速度越来越大,当小滑块远离红线时,速度越来越小。
更真实的加速度
上面的加速度采用手动设置的方式,虽然可以设定不同的值,但仍然不太真实。具体说来,加速度应该是不断变化的,在距离参考点最远的地方,加速度最大,在距离参考点最近的地方,加速度最小。于是我们将加速度调整为函数内部计算:
function animate(ele = null,config = {
scale:20,
coeff:0.95,
attrs:{
},
}){
// 清除定时器
clearInterval(ele.timer);
const attrs = config.attrs;
const scale = config.scale||20;
const coeff = config.coeff||0.95;
let speed = 0;
return new Promise((resolve)=>{
ele.timer = setInterval(()=>{
for(const attr in attrs){
// 设置目标距离
let target = Number.parseInt(attrs[attr]);
// 获取当前的样式
let currentStyle = (attr === "opacity")?(Number.parseInt(Number.parseFloat(getCurrentStyle(ele,attr))*100)):Number.parseInt(getCurrentStyle(ele,attr));
// 如果改变的样式是 opacity,target乘以100
if(attr === "opacity"){
target = Number.parseInt(Number.parseFloat(attrs[attr])*100);
}
// 获取速度
speed += (target - currentStyle)/scale;
// 根据当前样式动态改变物体的样式
ele.style[attr] = (attr === "opacity")?( currentStyle + speed)/100:(currentStyle + speed) + "px";
}
},30);
});
}
scale 是缩放系数,由使用者设置。
加入摩擦力
上面的弹性运动中,速度是没有损失的,于是小滑块就一直在红线两侧往复运动。有时候我们想让弹性运动逐渐停下来,于是就需要加入一个摩擦系数。
修改 animate 函数:
function animate(ele = null,config = {
scale:5,
coeff:0.7,
attrs:{
},
}){
// 清除定时器
clearInterval(ele.timer);
const attrs = config.attrs;
const scale = config.scale||5;
const coeff = config.coeff||0.7;
let speed = 0;
return new Promise((resolve)=>{
ele.timer = setInterval(()=>{
for(const attr in attrs){
// 设置目标距离
let target = Number.parseInt(attrs[attr]);
// 获取当前的样式
let currentStyle = (attr === "opacity")?(Number.parseInt(Number.parseFloat(getCurrentStyle(ele,attr))*100)):Number.parseInt(getCurrentStyle(ele,attr));
// 如果改变的样式是 opacity,target乘以100
if(attr === "opacity"){
target = Number.parseInt(Number.parseFloat(attrs[attr])*100);
}
// 获取速度
speed += (target - currentStyle)/scale;
speed *= coeff;
// 根据当前样式动态改变物体的样式
ele.style[attr] = (attr === "opacity")?( currentStyle + speed)/100:(currentStyle + speed) + "px";
}
},30);
});
}
调用动画函数:
const ele = document.getElementById("inner");
async function start(){
await animate(ele,{
attrs:{
left:"300px",
}
});
}
查看效果:
加入摩擦系数.gif弹性运动停止条件
和其他运动一样,弹性运动也需要在某一时刻停止,弹性运动的停止条件为:速度足够小并且目标点和当前位置差值的绝对值足够小。现在我们来修改 animate 函数,使弹性运动可以停止,同时增加摩擦因子判断:摩擦因子不能大于 1 。
修改 animate 函数:
function animate(ele = null,config = {
scale:5,
coeff:0.7,
attrs:{
},
}){
// 清除定时器
clearInterval(ele.timer);
const attrs = config.attrs;
const scale = config.scale||5;
const coeff = config.coeff||0.7;
if(coeff > 1){
throw new Error("错误的摩擦因数值");
}
let speed = 0;
return new Promise((resolve)=>{
ele.timer = setInterval(()=>{
for(const attr in attrs){
// 设置目标距离
let target = Number.parseInt(attrs[attr]);
// 获取当前的样式
let currentStyle = (attr === "opacity")?(Number.parseInt(Number.parseFloat(getCurrentStyle(ele,attr))*100)):Number.parseInt(getCurrentStyle(ele,attr));
// 如果改变的样式是 opacity,target乘以100
if(attr === "opacity"){
target = Number.parseInt(Number.parseFloat(attrs[attr])*100);
}
// 获取速度
speed += (target - currentStyle)/scale;
speed *= coeff;
if(Math.abs(speed)<1 && Math.abs(target - currentStyle)<1){
clearInterval(ele.timer);
// 清除定时器后,将元素移动到目标点
ele.style[attr] = (attr === "opacity")?target / 100: target + "px";
resolve();
}else{
ele.style[attr] = (attr === "opacity")?( currentStyle + speed)/100:(currentStyle + speed) + "px";
}
}
},30);
});
}
调用运动函数:
const ele = document.getElementById("inner");
async function start(){
await animate(ele,{
coeff:0.9,
attrs:{
left:"300px",
}
});
}
最终效果:
弹性运动最终效果.gif
调整 config 中的 scale 和 coeff 属性值,可以设置不同的动态效果。
多值弹性运动
基于现有的运动框架,该运动同样支持多值运动和链式调用。下面是一个多值运动的 demo:
const ele = document.getElementById("inner");
async function start(){
await animate(ele,{
coeff:0.9,
attrs:{
width:"200px",
height:"200px",
lineHeight:"200px",
}
});
}
效果图如下:
弹性运动多值运动.gif总结
至此,弹性运动就算说完了,总结一下有以下几点需要注意的地方:
- 物体靠近参考点,做加速运动
- 物体远离参考点,做减速运动
- 加速度最好是随着距离做动态变化的
- 实际情况中的弹性运动是需要停止的,因此需要摩擦系数
- 弹性运动的停止条件为:速度足够小并且当前位置和目标位置差值的绝对值足够小
在下一篇文章,我们将会实现常用运动中的最后一种运动形式:碰撞运动。
完。