JavaScript

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

总结

至此,弹性运动就算说完了,总结一下有以下几点需要注意的地方:

在下一篇文章,我们将会实现常用运动中的最后一种运动形式:碰撞运动。

完。

上一篇下一篇

猜你喜欢

热点阅读