一、盒子拖拽
原生JS:拖拽目标元素到页面任意位置
思路
1、onmousedown(鼠标按下事件) :获取鼠标在盒子中的坐标,保证移动后鼠标在盒子的指定位置(x、y)
x = pagex - box.offsetLeft
y = pagey - box.offsetTop
2、onmousemove(鼠标移动事件) :移动后鼠标的位置(x2,y2),减去鼠标在盒子中的坐标,就是盒子移动后的坐标
document.onmousemove
//给box赋值
box.style.left = (x2 – x) + “px”
box.style.top = (y2 - y) + “px”
3、onmouseup(鼠标抬起事件):事件解绑
document.onmousemove = null
注意点
1、变成绝对定位,脱离文档流才可以移动
2、鼠标移动事件绑定在document上,优化性能
二、碰撞检测:矩形—矩形
拖拽两个矩形div进行碰撞检测
逻辑
外接图形判别法,判断任意两个矩形的任意一边是否无间距,从而判断是否碰撞。
算法
1 | rect1.x < rect2.x + rect2.width && |
以上逻辑封装成碰撞检测函数bump(box1,box2),进行复用
三、碰撞检测:圆形—圆形
拖拽两个圆形div进行碰撞检测
逻辑
过判断任意两个圆形的圆心距离是否小于两圆半径之和,若小于则为碰撞。
公式
两点之间的距离公式,两个圆心之间的距离
算法
1 | Math.sqrt(Math.pow(circleA.x - circleB.x, 2) + Math.pow(circleA.y - circleB.y, 2)) |
四、碰撞检测:圆形—矩形
拖拽圆形和矩形div进行碰撞检测
逻辑
通过找出矩形上离圆心最近的点,然后通过判断该点与圆心的距离是否小于圆的半径,若小于则为碰撞。
判断条件
对于 x轴:
如果圆心在矩形的左侧(if(circle.x < rect.x)),那么 closestPoint.x = rect.x
如果圆心在矩形的右侧(else if(circle.x > rect.x + rect.w)),那么 closestPoint.x = rect.x + rect.w
如果圆心在矩形的正上下方(else),那么 closestPoint.x = circle.x
对于 y 轴:
如果圆心在矩形的上方(if(circle.y < rect.y)),那么 closestPoint.y = rect.y。
如果圆心在矩形的下方(else if(circle.y > rect.y + rect.h)),那么 closestPoint.y = rect.y + rect.h。
如果圆心在矩形的正左右两侧(else),那么 closestPoint.y = circle.y
算法
1 | var distance = Math.sqrt(Math.pow(closestPoint.x - circle.x, 2) + Math.pow(closestPoint.y - circle.y, 2)) |
五、碰撞检测:圆形—旋转矩形
拖拽圆形和旋转矩形div进行碰撞检测
逻辑
1、一个物体相对于另一个物体的位置发生了变化,这个物体就在运动。
2、简化模型,变为普通的圆形与矩形碰撞检测
计算过程
目标值:C(c,d)的坐标,即相对旋转后的圆心坐标
1、已知条件:设 A 点旋转前的角度为 δ,则旋转(逆时针)到 C 点后的角度为(δ+β)
2、由于 |AB| 与 |CB| 相等(即长度),且
已知条件: |AB| = y/sin(δ) = x / cos(δ)
已知条件: |CB| = d/sin(δ + β) = c / cos(δ + β)
3、已知条件:半径 r = x / cos(δ) = y / sin(δ) = d / sin(δ + β) = c / cos(δ + β)
4、由以下三角函数两角和差公式:
sin(δ + β) = sin(δ)cos(β) + cos(δ)sin(β)
cos(δ + β) = cos(δ)cos(β) - sin(δ)sin(β)
5、可得出旋转后的坐标:
c = r * cos(δ + β) = r * cos(δ)cos(β) - r * sin(δ)sin(β) = x * cos(β) - y * sin(β)
d = r * sin(δ + β) = r * sin(δ)cos(β) + r * cos(δ)sin(β) = y * cos(β) + x * sin(β)
6、实际坐标公式
x’= cos(β) * (cx – centerX) – sin(β) * (cy – centerY) + centerX
y’= sin(β) * (cx – centerX) + cos(β) * (cy – centerY) + centerY
算法
1 | x’= Math.cos(radian) * (cx - centerX) - Math.sin(radian) * (cy - centerY) + centerX |
六、碰撞检测:像素-像素
拖拽不规则的图形,在像素级别进行碰撞检测
逻辑
1、根据每一像素来判定是否碰撞,性能要求比较高。通常情况下,先对图像所在的矩形进行碰撞检测,当两个矩形碰撞时,再从像素级别进行判断
2、所在矩形发生碰撞后,再在像素级别进行比较,提高性能
3、获取两个精灵图像在相交矩形的像素数据,在相交矩形区域进行遍历判断,若两个图像在同一位置的值都不为0,则发生碰撞
2D游戏中,通常使用矩形、圆形等来代替复杂图形的相交检测。因为这两种形状的碰撞检测速度是最快的。
其中矩形包围盒又可以分为轴对齐包围盒(AABB, Axis Aligned Bounding Box)与转向包围盒(OBB, Oriented Bounding Box)。
AABB与OBB的区别在于,AABB中的矩形的其中一条边和坐标轴平行,OBB的计算复杂度要高于AABB。根据不同的使用场景,可以用不同的方案。
算法
1 | var rect //相交矩形的数据对象,sx, sy, sw, sh |
七、小Demo:颜色选择器
CanvasRenderingContext2D.getImageData() 返回一个 ImageData 对象,用来描述 canvas 区域隐含的像素数据,这个区域通过矩形表示,起始点为(sx, sy)、宽为sw、高为sh。
ctx.getImageData(sx, sy, sw, sh)
参数
sx:将要被提取的图像数据矩形区域的左上角 x 坐标。
sy:将要被提取的图像数据矩形区域的左上角 y 坐标。
sw:将要被提取的图像数据矩形区域的宽度。
sh:将要被提取的图像数据矩形区域的高度。
返回值
一个 ImageData 对象,包含 canvas 给定的矩形图像数据。
ImageData 对象会有三个属性,height、width 和 data。
ImageData.height
使用像素描述 ImageData 的实际高度,这个值其实等于 getImageData() 方法中的参数 sh
ImageData.width
使用像素描述 ImageData 的实际宽度。这个值其实等于 getImageData() 方法中的参数 sw
ImageData.data
一个一维数组,包含以 RGBA 顺序的数据,数据使用 0 至 255(包含)的整数表示
1 | 示例代码: |
引用:
1、javascript动画系列第三篇——碰撞检测
2、用 canvas 的 getImageData 做点有趣的事
3、“等一下,我碰!”——常见的2D碰撞检测
4、使用html+javascript实现游戏常用算法演示
5、工具网站:图片转换Base64
6、GitHub搭建Hexo博客教程
7、圆与旋转矩形的碰撞检测(下篇)
8、碰撞检测的向量实现
9、通过js获取元素css3的transform rotate旋转角度方法