共计 1992 个字符,预计需要花费 5 分钟才能阅读完成。
这一章,咱们来说鼠标如何选择变换后的图形。
首先给大家举个栗子:在 2029 年末世之战的时候,终结者想干掉人类领袖大壮,可是大壮太强,而且其实力需要复杂运算才能知晓。所以终结者就想回到 1997 年,在大壮实力弱小、且已知的情况下将其干掉。这样根据物质不易法则,2029 年末世之战中的人类领袖大壮也就不会存在。
接下来给大家解密终结者穿梭时间的方法
终结者需要知道数据:
- 1997 年大壮的初始属性,比如构成大壮轮廓的顶点集合;
- 大壮从 1997 到 2029 的变换信息,比如其大壮移动了多少、旋转了多少、长大了多少。
根据物质不易法则:物质不变,空间不变;空间不变,时间不变。
将物质不易法则逆推,依旧成立:物质改变,空间改变;空间改变,时间改变。
所以终结者想要回到 1997 年,只要根据大壮的变换规则逆向变换自己的位置就可以回到 1997 年。
比如:
- 从 1997 年到 2029 年,大壮沿 x 轴移动了 100,沿 y 轴移动了 200,旋转了 90 度,变大了 2 倍。
- 终结者 (鼠标点位) 就要沿 x 轴移动了 -100,沿 y 轴移动了 -200,旋转 -90 度,点位到圆心点的距离缩小 2 倍。
注意:终结者的变换顺序要和大壮的变换顺序一致;终结者改变的只是点位,点没有尺寸,其点位变换本质是在目标对象所在的 canvas 画布的坐标系的位移。只有如此,当终结者穿梭到 1997 年的时候,才可以精准定位大壮。
图示:
接下来我们就在代码里走一下这个原理:
先画了一颗爱心,其所在的 canvas 画布坐标系在 x、y 方向分别位移了(300,400)
const poly=new Poly({position:new Vector2(300,400),
stroke:true,
close:true,
crtPath:function(ctx){ctx.beginPath();
ctx.moveTo(0,0);
ctx.bezierCurveTo(-200,-50,-180,-300,0,-200);
ctx.bezierCurveTo(180,-300,200,-50,0,0);
}
});
poly.draw(ctx);
若我的鼠标想要选择这颗爱心,那它的位置就要基于爱心的变换信息反向变换:x、y 方向分别位移(-300,-400)。代码如下:
const mousePos=getMousePos(event);
poly.crtPath(ctx);
const [nx,ny]=[
mousePos.x-poly.position.x,
mousePos.y-poly.position.y
];
const bool=ctx.isPointInPath(nx,ny);
图形变换中的位移说完了,那它的旋转、缩放也是同样道理,就是让鼠标位置基于图形的变换信息反向变换。
下面我直接将所有变换的方法封装到了获取鼠标点位的方法里,即 getMousePos(event,poly),event 是事件,poly 是图形。
代码如下:
const getMousePos=function(event,obj=null){
// 获取鼠标位置
const {clientX,clientY}=event;
// 获取 canvas 边界位置
const {top,left}=canvas.getBoundingClientRect();
// 计算鼠标在 canvas 中的位置
const x=clientX-left;
const y=clientY-top;
const mousePos=new Vector2(x,y);
if(obj){const {position,scale,rotation}=obj;
mousePos.sub(position);
mousePos.rotate(-rotation);
mousePos.divide(scale);
}
return mousePos;
};
mousePos 是一个 Vector2 对象,其中封装了关于向量的常用方法。如:
export default class Vector2{constructor(x=0,y=0){
this.x=x;
this.y=y;
}
// 减法
sub(v){
this.x -= v.x;
this.y -= v.y;
return this;
}
// 基于原点旋转
rotate(angle){const c = Math.cos( angle), s = Math.sin(angle);
const {x,y}=this;
this.x = x * c - y * s;
this.y = x * s + y * c;
return this;
}
// 向量除法
divide (v) {
this.x /= v.x;
this.y /= v.y;
return this;
}
//...
}
好啦,关于变换后的图形选择我们就说到这。
其实图形图形选择的方法是有很多的,下一章我再跟大家说一个图形选择的方法:图形选择 - 网格选择
注:物质不易是我从修仙小说上课看的,没有科学依据,只为辅助大家理解代码。
源码地址