美文网首页JavaScript
JavaScript 运动 04 —— 弹性运动

JavaScript 运动 04 —— 弹性运动

作者: 柏丘君 | 来源:发表于2017-05-22 19:32 被阅读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

    总结

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

    • 物体靠近参考点,做加速运动
    • 物体远离参考点,做减速运动
    • 加速度最好是随着距离做动态变化的
    • 实际情况中的弹性运动是需要停止的,因此需要摩擦系数
    • 弹性运动的停止条件为:速度足够小并且当前位置和目标位置差值的绝对值足够小

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

    完。

    相关文章

      网友评论

        本文标题:JavaScript 运动 04 —— 弹性运动

        本文链接:https://www.haomeiwen.com/subject/yfhixxtx.html