美文网首页JavaScriptJavaScript运动
JavaScript 运动 05 —— 碰撞运动

JavaScript 运动 05 —— 碰撞运动

作者: 柏丘君 | 来源:发表于2017-05-22 22:31 被阅读16次

    前面的四篇文章分别实现了匀速运动、缓冲运动和弹性运动,本文继续,实现最后一个常见运动——碰撞运动。

    碰撞运动

    碰撞运动是两个物体接触后速度反向的运动。物体在某个容器内运动,当碰到容器的边缘后,速度反向。
    去掉外层容器中间的分割线,恢复到最初始的布局:

    #par{
        width: 600px;
        height: 300px;
        border: 1px solid #333;
        margin:50px auto;
        position: relative;
        text-align: center;
        padding-top: 10px;
    }
    
    #inner{
        width: 100px;
        height: 100px;
        background: orange;
        position: absolute;
        left: 0;
        top: calc(50% - 50px);
        text-align: center;
        line-height: 100px;
        opacity: 1;
    }
    
    碰撞运动初始布局.png

    碰撞运动在容器内部进行,因此具有两个速度:水平方向的速度 speedX 和垂直方向的速度 speedY。
    修改 animate 函数,实现基本的碰撞效果:

    function animate(ele = null,config = {
        speedX:10,
        speedY:10,
        container:null,
    }){
        // 清除定时器
        clearInterval(ele.timer);
        
        let { speedX, speedY, container } = config; 
        speedX = speedX || 10;
        speedY = speedY || 10;
    
        return new Promise((resolve)=>{
            ele.timer = setInterval(()=>{
                let left = parseInt(getCurrentStyle(ele,"left")) + speedX;
                let top = parseInt(getCurrentStyle(ele,"top")) + speedY;
    
                // 判断运动是否过界
                // 判断左右边界
                if(left<= 0){
                    speedX *= -1;
                    left = 0;
                }else if(left + ele.offsetWidth >= container.clientWidth){
                    speedX *= -1;
                    left = container.clientWidth - ele.offsetWidth;
                }
    
                // 判断上下边界
                if(top <= 0){
                    speedY *= -1;
                    top = 0;
                }else if(top + ele.offsetHeight >= container.clientHeight){
                    speedY *= -1;
                    top = container.clientHeight - ele.offsetHeight;
                }
    
                // 设置速度
                ele.style.left = left + "px";
                ele.style.top = top + "px";
    
    
            },30);
        });
    }
    
    

    调用运动函数:

    ...
    const ele = document.getElementById("inner");
    async function start(){
        await animate(ele,{
            container:document.getElementById("par"),
            speedX:15,
            speedY:20
        });
    }
    ...
    

    效果如图所示:

    简易碰撞运动.gif
    在碰撞运动的过程中,需要对触底进行判断,当小滑块在运动中会触底(或者超出底边时),将小滑块拉回底边位置并设置速度反向

    带有重力的碰撞运动

    和弹性运动的摩擦力一样,我们可以使用一个重力系数来模拟重力:

    function animate(ele = null,config = {
        speedX:10,
        speedY:10,
        gravity:5,
        coeff:0.8,
        container:null,
    }){
        // 清除定时器
        clearInterval(ele.timer);
        
        let { speedX, speedY, container,gravity,coeff } = config; 
        speedX = speedX || 10;
        speedY = speedY || 10;
        gravity = gravity || 5;
        coeff = coeff || 0.8;
    
        return new Promise((resolve)=>{
            ele.timer = setInterval(()=>{
                // 生活常识告诉俺们,每次下落时,垂直速度都比上一次快
                speedY += gravity;
                let left = parseInt(getCurrentStyle(ele,"left")) + speedX;
                let top = parseInt(getCurrentStyle(ele,"top")) + speedY;
    
                // 判断运动是否过界
                // 判断左右边界
                if(left<= 0){
                    speedX *= -coeff;
                    speedY *= coeff;
                    left = 0;
                }else if(left + ele.offsetWidth >= container.clientWidth){
                    speedX *= -coeff;
                    speedY *= coeff;
                    left = container.clientWidth - ele.offsetWidth;
                }
    
                // 判断上下边界
                if(top <= 0){
                    speedY *= -coeff;
                    speedX *= coeff;
                    top = 0;
                }else if(top + ele.offsetHeight >= container.clientHeight){
                    speedY *= -coeff;
                    speedX *= coeff;
                    top = container.clientHeight - ele.offsetHeight;
                }
        
                // 设置速度
                ele.style.left = left + "px";
                ele.style.top = top + "px";
    
    
            },30);
        });
    }
    

    清除定时器

    当碰撞运动的水平速度(speedX)和垂直速度(speedY)都为 0 ,并且小滑块处于容器底部时,碰撞运动停止,此时我们就该清除定时器了。同时,也需要对 speedX 和 speedY 进行浮点数处理,以及重力系数的合法值检验。
    修改 animate 函数:

    function animate(ele = null,config = {
        speedX:0,
        speedY:0,
        gravity:5,
        coeff:0.8,
        container:null,
    }){
        // 清除定时器
        clearInterval(ele.timer);
    
        let { speedX, speedY, container,gravity,coeff } = config; 
        speedX = speedX || 0;
        speedY = speedY || 0;
        gravity = gravity || 5;
    
        // 错误处理
        if(coeff > 1){
            throw new Error("碰撞系数参数错误!");
        }
        coeff = coeff || 0.8;
    
        return new Promise((resolve)=>{
            ele.timer = setInterval(()=>{
                // 生活常识告诉俺们,每次下落时,垂直速度都比上一次快
                speedY += gravity;
                let left = parseInt(getCurrentStyle(ele,"left")) + speedX;
                let top = parseInt(getCurrentStyle(ele,"top")) + speedY;
    
                // 判断运动是否过界
                // 判断左右边界
                if(left<= 0){
                    speedX *= -coeff;
                    speedY *= coeff;
                    left = 0;
                }else if(left + ele.offsetWidth >= container.clientWidth){
                    speedX *= -coeff;
                    speedY *= coeff;
                    left = container.clientWidth - ele.offsetWidth;
                }
    
                // 判断上下边界
                if(top <= 0){
                    speedY *= -coeff;
                    speedX *= coeff;
                    top = 0;
                }else if(top + ele.offsetHeight >= container.clientHeight){
                    speedY *= -coeff;
                    speedX *= coeff;
                    top = container.clientHeight - ele.offsetHeight;
                }
    
                // 处理小数
                if(Math.abs(speedX) < 1){
                    speedX = 0;
                }
    
                if(Math.abs(speedY) < 1){
                    speedY = 0;
                }
    
                // 运动停止条件
                if(!speedX && !speedY && top === container.clientHeight - ele.offsetHeight){
                    clearInterval(ele.timer);
                    resolve()
                }else{
                    // 设置速度
                    ele.style.left = left + "px";
                    ele.style.top = top + "px";
                }
            },30);
        });
    }
    

    调用运动函数:

    const ele = document.getElementById("inner");
    async function start(){
        await animate(ele,{
            container:document.getElementById("par"),
        });
    }
    

    最终效果如图:

    最终效果.gif

    总结

    本文介绍了碰撞运动的实现,下边是一些要点:

    • 碰撞运动拥有水平和垂直两个方向的速度
    • 碰撞运动需要进行上下左右四个边界的检测
    • 带有重力的碰撞运动,每次碰撞之后,都要乘以碰撞系数,以减小速度
    • 带重力的碰撞运动停止条件:水平速度和垂直速度都为0
    • 当水平速度或垂直速度的绝对值趋近于 0 时,进行精度处理,直接将此速度设置为 0
    • 碰撞运动停止条件:水平速度(speedX)和垂直速度(speedY)都为 0,并且物体处于容器底部

    碰撞运动至此就写完了,下篇文章中,将在此基础上,介绍一个基于拖拽的碰撞运动。

    完。

    相关文章

      网友评论

        本文标题:JavaScript 运动 05 —— 碰撞运动

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