javascript运动系列第九篇——碰撞运动

×

目录

[1]碰撞检测 [2]无损碰撞 [3]有损碰撞

前面的话

  碰撞可以分为碰壁和互碰两种形式,上篇介绍了碰壁运动,本文将从浅入深地介绍碰撞运动的互碰形式

碰撞检测

  对于互碰形式的碰撞运动来说,首先要解决的是碰撞检测。对于矩形元素的碰撞检测前面的博文已经详细介绍过,下面主要介绍圆形元素的碰撞检测

  矩形元素的碰撞检测利用九宫格分析法,而圆形元素的碰撞检测则简单很多,判断两个圆形元素的半径之和是否大于两个圆形元素的圆心点坐标之间的距离即可

  由示意图可知,元素一的圆心位置为(x1,y1),半径为r1,元素二的圆心位置为(x2,y2),半径为r2

  两个元素圆心之间的距离len = Math.sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2))

  当len<= r1+r2时,说明两个圆形元素发生碰撞

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
<div id="test1" style="height: 100px;width: 100px;background:pink;position:absolute;top:0;left:0;border-radius: 50%;"></div>
<div id="test2" style="height: 200px;width: 200px;background:orange;position:absolute;top:150px;left:150px;border-radius: 50%;"></div>
<script>
function getCSS(obj,style){
    if(window.getComputedStyle){
        return getComputedStyle(obj)[style];
    }
    return obj.currentStyle[style];
}
function bump(obj,objOther,bgColor){

    /***被碰元素***/
    var r1 = obj.offsetWidth/2;
    var x1 = parseFloat(getCSS(obj,‘left‘)) + r1;
    var y1 = parseFloat(getCSS(obj,‘top‘)) + r1;
    /**侵入元素**/
    var r2 = objOther.offsetWidth/2;
    var x2 = parseFloat(getCSS(objOther,‘left‘)) + r2;
    var y2 = parseFloat(getCSS(objOther,‘top‘)) + r2;
    //碰撞检测
    var len = Math.sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));

    if(len <= r1 + r2){
        obj.style.backgroundColor = ‘red‘;
    }else{
        obj.style.backgroundColor = bgColor;
    }
}

function drag(obj){
    obj.onmousedown = function(e){
        e = e || event;
        //提升当前元素的层级
        obj.style.zIndex = ‘1‘;
        //获取元素距离定位父级的x轴及y轴距离
        var x0 = this.offsetLeft;
        var y0 = this.offsetTop;
        //获取此时鼠标距离视口左上角的x轴及y轴距离
        var x1 = e.clientX;
        var y1 = e.clientY;
        //鼠标按下时,获得此时的页面区域
        var L0 = 0;
        var R0 = document.documentElement.clientWidth;
        var T0 = 0;
        var B0 = document.documentElement.clientHeight;
        //鼠标按下时,获得此时的元素宽高
        var EH = obj.offsetHeight;
        var EW = obj.offsetWidth;
        document.onmousemove = function(e){
            e = e || event;
            //获取此时鼠标距离视口左上角的x轴及y轴距离
            x2 = e.clientX;
            y2 = e.clientY;
            //计算此时元素应该距离视口左上角的x轴及y轴距离
            var X = x0 + (x2 - x1);
            var Y = y0 + (y2 - y1);
            /******范围限定*******/
            //获取鼠标移动时元素四边的瞬时值
            var L = X;
            var R = X + EW;
            var T = Y;
            var B = Y + EH;
            //在将X和Y赋值给left和top之前,进行范围限定
            //只有在范围内时,才进行相应的移动
            //如果脱离左侧范围,则left置L0
            if(L < L0){X = L0;}
            //如果脱离右侧范围,则left置为R0
            if(R > R0){X = R0 - EW;}
            //如果脱离上侧范围,则top置T0
            if(T < T0){Y = T0;}
            //如果脱离下侧范围,则top置为B0
            if(B > B0){Y = B0 - EH;}
            obj.style.left = X + ‘px‘;
            obj.style.top = Y + ‘px‘;
            //运行碰撞检测函数
            bump(test2,test1,‘orange‘)
        }
        document.onmouseup = function(e){
            //降低当前元素的层级
            obj.style.zIndex = ‘0‘;
            //当鼠标抬起时,拖拽结束,则将onmousemove赋值为null即可
            document.onmousemove = null;
            //释放全局捕获
            if(obj.releaseCapture){
                obj.releaseCapture();
            }
        }
        //阻止默认行为
        return false;
        //IE8-浏览器阻止默认行为
        if(obj.setCapture){
            obj.setCapture();
        }
    }
}
drag(test1);
drag(test2);
</script>
</body>
</html>

无损碰撞

  假设两个元素的碰撞,对元素的速度并不产生损耗,而只是改变元素速度方向

  假设元素一在与元素二碰撞前的瞬时速度是v,将该速度分解为平行于碰撞方向的速度v1和垂直于碰撞方向的速度v2

  碰撞发生后,碰撞方向的速度v1变成了反向的v1

  将反向的v1分解到水平方向v1x和垂直方向v1y

  将垂直于碰撞方向的速度v2分解到水平方向v2x和垂直方向v2y

    水平方向的速度vx = v2x - v1x
    垂直方向的速度vy = v2y - v1y

  元素二的速度分解形式与元素一类似,就不再赘述

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
<button id="btn1">开始运动</button>
<button id="reset">还原</button>
<div id="test1" style="height: 150px;width: 150px;background:pink;position:absolute;top:50px;left:50px;border-radius: 50%;"></div>
<div id="test2" style="height: 150px;width: 150px;background:orange;position:absolute;top:250px;left:250px;border-radius: 50%;"></div>
<script>
//声明元素的步长值
//步长值默认值为[-25,-20,-15,-10,-5,0,5,10,15,20]中的一个随机数
test1.stepX =  5*Math.floor(Math.random() * 10 - 5);
test1.stepY =  5*Math.floor(Math.random() * 10 - 5);
test2.stepX =  5*Math.floor(Math.random() * 10 - 5);
test2.stepY =  5*Math.floor(Math.random() * 10 - 5);
btn1.onclick = function(){
    collisionMove({
        obj:test1
    })
    collisionMove({
        obj:test2
    })
}
reset.onclick = function(){
    history.go();
}
function collisionMove(json){
    var obj = json.obj;
    var fn = json.fn;
    //声明x、y轴的当前值
    var curX,curY;
    //声明x、y轴方向
    var dirX = json.dirX;
    var dirY = json.dirY;
    dirX = obj.stepX > 0 ? ‘+‘ : ‘-‘;
    dirY = obj.stepY > 0 ? ‘+‘ : ‘-‘;
    //声明offset宽高
    var offsetWidth = obj.offsetWidth;
    var offsetHeight = obj.offsetHeight;
    //声明元素活动区域宽高
    var activeWidth = json.activeWidth;
    var activeHeight = json.activeHeight;
    //元素获取区域宽高默认值为可视区域宽高
    activeWidth = Number(activeWidth) || document.documentElement.clientWidth;
    activeHeight = Number(activeHeight) || document.documentElement.clientHeight;
    //声明left、top样式值
    var left,top;
    //清除定时器
    if(obj.timer){return;}
     //开启定时器
    obj.timer = setInterval(function(){
        //获取x、y轴的当前值
        curX = parseFloat(getCSS(obj,‘left‘));
        curY = parseFloat(getCSS(obj,‘top‘));
        bump(test1,test2);
        //更新left、top值
        left = curX + obj.stepX;
        top = curY + obj.stepY;
        //右侧碰壁前一刻,步长大于剩余距离,且元素向右运动时
        if((left > activeWidth - offsetWidth) && (dirX == ‘+‘)){
            left = activeWidth - offsetWidth;
        }
        //左侧碰壁前一刻,步长大于剩余距离,且元素向左运动时
        if((Math.abs(obj.stepX) > curX) && (dirX == ‘-‘)){
            left = curX;
        }
        //下侧碰壁前一刻,步长大于剩余距离,且元素向下运动时
        if((top > activeHeight - offsetHeight) && (dirY == ‘+‘)){
            top = activeHeight - offsetHeight;
        }
        //上侧碰壁前一刻,步长大于剩余距离,且元素向上运动时
        if((Math.abs(obj.stepY) > curY) && (dirY == ‘-‘)){
            top = curY;
        }
        obj.style.left= left + ‘px‘;
        obj.style.top = top + ‘px‘;
        //左侧或右侧碰撞瞬间
        if(left == activeWidth - offsetWidth || left == curX){
            obj.stepX = -obj.stepX;
        }
        //上侧或下侧碰撞瞬间
        if(top == activeHeight - offsetHeight || top == curY){
            obj.stepY = -obj.stepY;
        }
        //更新运动方向
        dirX = obj.stepX > 0 ? ‘+‘ : ‘-‘;
        dirY = obj.stepY > 0 ? ‘+‘ : ‘-‘;
    },20);
}

function getCSS(obj,style){
    if(window.getComputedStyle){
        return getComputedStyle(obj)[style];
    }
    return obj.currentStyle[style];
}
//碰撞检测函数
function bump(obj,objOther){
    /***动态元素***/
    obj.r = obj.offsetWidth/2;
    obj.x0 = parseFloat(getCSS(obj,‘left‘)) + obj.r;
    obj.y0 = parseFloat(getCSS(obj,‘top‘)) + obj.r;
    /**静态元素**/
    objOther.r = objOther.offsetWidth/2;
    objOther.x0 = parseFloat(getCSS(objOther,‘left‘)) + objOther.r;
    objOther.y0 = parseFloat(getCSS(objOther,‘top‘)) + objOther.r;
    //圆心之间的距离
    var len = Math.sqrt((obj.x0-objOther.x0)*(obj.x0-objOther.x0) + (obj.y0-objOther.y0)*(obj.y0-objOther.y0));
    //发生碰撞
    if(len <= obj.r + objOther.r){
    //碰撞方向与水平负方向的夹角a
        var a = Math.atan(Math.abs((obj.y0-objOther.y0)/(obj.x0-objOther.x0)));
        stepChange(test1,test2,a);
        stepChange(test2,test1,a);
    }
}
//碰撞时,步长变化函数
function stepChange(obj,objOther,a){
    //步长合并
    obj.step = Math.sqrt(obj.stepX*obj.stepX + obj.stepY*obj.stepY);
    //假设总步长方向与x轴方向的夹角为b
    obj.b = Math.atan(Math.abs(obj.stepY/obj.stepX));
    //假设总步长方向与碰撞方向的夹角为c
    obj.c = Math.abs(a - obj.b);
    //步长分解
    //碰撞方向
    obj.step1 = obj.step*Math.cos(obj.c);
    //垂直方向
    obj.step2 = obj.step*Math.sin(obj.c);
    //按照运动元素(侵入元素)的起始运动方向对步长进行重新分解
    //左上
    if(obj.x0 <= objOther.x0 && obj.y0 <= objOther.y0){
        obj.stepX = -obj.step1*Math.cos(a) + obj.step2*Math.sin(a)
        obj.stepY = -obj.step1*Math.sin(a) - obj.step2*Math.cos(a)
    }
    //左下
    if(obj.x0 < objOther.x0 && obj.y0 > objOther.y0){
        obj.stepX = -obj.step1*Math.cos(a) + obj.step2*Math.sin(a)
        obj.stepY = obj.step1*Math.sin(a) + obj.step2*Math.cos(a)
    }
    //右上
    if(obj.x0 > objOther.x0 && obj.y0 < objOther.y0){
        obj.stepX = obj.step1*Math.cos(a) - obj.step2*Math.sin(a)
        obj.stepY = -obj.step1*Math.sin(a) - obj.step2*Math.cos(a)
    }
    //右下
    if(obj.x0 > objOther.x0 && obj.y0 > objOther.y0){
        obj.stepX = obj.step1*Math.cos(a) - obj.step2*Math.sin(a)
        obj.stepY = obj.step1*Math.sin(a) + obj.step2*Math.cos(a)
    }
}
</script>
</body>
</html>

有损碰撞

  匀速有损碰撞是在无损碰撞的基础上,每次碰撞都有一定的速度损耗,在碰撞或碰壁的瞬间乘以损耗因子即可

  变速有损碰撞类似于击打台球,元素运动时,速度就一直在减小,碰撞或碰壁时,除了速度方向改变外,速度也有所损耗,当速度减小到0时,停止运动。由于代码相似,就不再重复,源码见此

时间: 2024-11-03 01:26:43

javascript运动系列第九篇——碰撞运动的相关文章

深入理解javascript作用域系列第二篇——词法作用域和动态作用域

× 目录 [1]词法 [2]动态 前面的话 大多数时候,我们对作用域产生混乱的主要原因是分不清楚应该按照函数位置的嵌套顺序,还是按照函数的调用顺序进行变量查找.再加上this机制的干扰,使得变量查找极易出错.这实际上是由两种作用域工作模型导致的,作用域分为词法作用域和动态作用域,分清这两种作用域模型就能够对变量查找过程有清晰的认识.本文是深入理解javascript作用域系列第二篇——词法作用域和动态作用域 词法作用域 第一篇介绍过,编译器的第一个工作阶段叫作分词,就是把由字符组成的字符串分解成

深入理解javascript作用域系列第一篇——内部原理

× 目录 [1]编译 [2]执行 [3]查询[4]嵌套[5]异常[6]原理 前面的话 javascript拥有一套设计良好的规则来存储变量,并且之后可以方便地找到这些变量,这套规则被称为作用域.作用域貌似简单,实则复杂,由于作用域与this机制非常容易混淆,使得理解作用域的原理更为重要.本文是深入理解javascript作用域系列的第一篇——内部原理 内部原理分成编译.执行.查询.嵌套和异常五个部分进行介绍,最后以一个实例过程对原理进行完整说明 编译 以var a = 2;为例,说明javasc

javascript动画系列第一篇——模拟拖拽

× 目录 [1]原理介绍 [2]代码实现 [3]代码优化[4]拖拽冲突[5]IE兼容 前面的话 从本文开始,介绍javascript动画系列.javascript本身是具有原生拖放功能的,但是由于兼容性问题,以及功能实现的方式,用的不是很广泛.javascript动画广泛使用的还是模拟拖拽.本文将详细介绍该内容 原理介绍 模拟拖拽最终效果和在桌面上移动文件夹的效果类似 鼠标按下时,拖拽开始.鼠标移动时,被拖拽元素跟着鼠标一起移动.鼠标抬起时,拖拽结束 所以,拖拽的重点是确定被拖拽元素是如何移动的

javascript运动系列第一篇——运动函数

× 目录 [1]简单运动 [2]定时器管理 [3]分享到效果[4]移入移出[5]运动函数[6]透明度[7]多值[8]多物体 前面的话 除了拖拽以外,运动也是javascript动画的一个基本操作.通过CSS属性transition和animation可以实现运动.但是,要进行更精细地操作,javascript运动是必不可少的.本文将详细介绍javascript运动 简单运动 让一个元素在页面中运动起来很简单,设置定时器,改变定位元素的left或top值即可 <button id="btn&

深入理解javascript对象系列第一篇——初识对象

× 目录 [1]定义 [2]创建 [3]组成[4]引用 前面的话 javascript中的难点是函数.对象和继承,前面已经介绍过函数系列.从本系列开始介绍对象部分,本文是该系列的第一篇——初识对象 对象定义 javascript的基本数据类型包括undefined.null.boolean.string.number和object.对象和其他基本类型值不同的是,对象是一种复合值:它将许多值(原始值或者其他对象)聚合在一起,可通过名字访问这些值 于是,对象也可看做是属性的无序集合,每个属性都是一个

深入理解javascript对象系列第二篇——属性操作

× 目录 [1]查询 [2]设置 [3]删除[4]继承 前面的话 对于对象来说,属性操作是绕不开的话题.类似于“增删改查”的基本操作,属性操作分为属性查询.属性设置.属性删除,还包括属性继承.本文是对象系列的第二篇——属性操作 属性查询 属性查询一般有两种方法,包括点运算符和方括号运算符 var o = { p: 'Hello World' }; o.p // "Hello World" o['p'] // "Hello World" [注意]变量中可以存在中文,因

javascript动画系列第二篇——磁性吸附

× 目录 [1]范围限定 [2]拖拽范围 [3]磁性吸附 前面的话 上一篇,我们介绍了元素拖拽的实现.但在实际应用中,常常需要为拖拽的元素限定范围.而通过限定范围,再增加一些辅助的措施,就可以实现磁性吸附的效果 范围限定 如果我们限定元素只可以在可视范围内移动,那么就需要对其进行范围限定 首先,先要搞清楚是可视区域限定被拖拽元素 左侧范围L0 = 0 右侧范围R0 = document.documentElement.clientWidth 上侧范围T0 = 0 下侧范围B0 = documen

【Windows编程】系列第九篇:剪贴板使用

 上一篇我们学习了常见的通用对话框,本篇来了解剪贴板的使用,它常用于复制粘贴功能. 剪贴板是Windows最早就加入的功能,由于该功能非常实用,我们几乎每天都会使用到.通过剪贴板,我们就可以将数据从一个应用程序传递到另一个应用程序,是一种简单的进程间通信. 许多文档处理软件都有复制.剪切.粘贴功能,这些都是用Windows剪贴板实现的,当然我们也可以在我们的程序中实现自己的剪贴板功能,本篇我们就来实现自己的剪贴板.使用剪贴板时,都是先把源数据先传到剪贴板上,再在需要的时候从剪贴板传输到目的处

深入理解ajax系列第九篇——jQuery中的ajax

前面的话 jQuery提供了一些日常开发中需要的快捷操作,例如load.ajax.get和post等,使用jQuery开发ajax将变得极其简单.这样开发人员就可以将程序开发集中在业务和用户体验上,而不需要理会那么繁琐的XMLHTTPRequest对象.jQuery对ajax操作进行了封装,在jQuery中$.ajax()属性最底层的方法,第2层是load().$.get()和$.post()方法,第3层是$.getScript()和$.getJSON()方法.下面将详细介绍jQuery中的aj