首先来看看canvas效果图
上面是我在canvas上绘制了多个不规则多边形,如果不是很了解怎么绘制,可以看我上个博客canvas画图
盒子结构
<div class="imgBox" ref="canvas">
<canvas id="myCanvas" width="660px" height="260px" @click="getWorkShop"></canvas>
</div>
样式
.imgBox {
width: 100%;
height: 100%;
// width: 660px;
// height: 260px;
background-color: #c1c8df;
cursor: pointer;
#myCanvas {
width: 100%;
height: 100%;
}
}
这个页面是适配的,imgBox盒子的大小比canvas画布大,canvas是适配上的,这个大小适配方法会影响后面点击画布时有影响,需要在写方法时,判断转化一下
原理 - 光线投射法
原理:
1、从点P出发,任意引一条射线(模拟光线)。
2、该条射线与多边形A的边相交时,若射线从边的左侧贯穿记录leftCount加1,若射线从边的右侧贯穿记录rightCount加1。
3、若leftCount-rightCount等于0表示在图形外部,若不等于0表示图形内部。
点击画布方法
getWorkShop($el, e) {
// 画布上是有一张背景图的,且是响应式的,页面的展示的画布与实际样式的画布尺寸不一致,需要转换一下,例如页面时1300 * 500,但是我的画布实际是660 * 260,然后需要比例转换一下
let cImgW = this.$refs.canvas.clientWidth; // 画布的渲染的宽度
let cImgH = this.$refs.canvas.clientHeight; // 画布的渲染高度
let offsetX = $el.offsetX; // 鼠标点击的横坐标
let offsetY = $el.offsetY; // 鼠标点击的纵坐标
// 按照比例实际上用户点击的位置,把鼠标点击的位置转换成660 * 260的相应位置
let userPointX = parseInt((660 * offsetX) / cImgW);
let userPointY = parseInt((260 * offsetY) / cImgH);
// this.pointData 是数组格式,切里面的每个对象里还有对象
let pointDataCopy = JSON.parse(JSON.stringify(this.pointData));
// 判断画布上有无图形
if (pointDataCopy.length > 0) {
let dot = {
x: userPointX,
y: userPointY,
};
pointDataCopy.map((item) => {
item.maxArray = [];
item.maxArray.push(item.point1);
item.maxArray.push(item.point2);
item.maxArray.push(item.point4);
item.maxArray.push(item.point3);
item.maxArray.push(item.point1);
// maxArray 是多边形点的集合,坐标格式是{x, y}
let flag = this.judge(dot, item.maxArray);
// if (flag) {
// console.log(item.name);
// }
});
}
},
/**
* @param dot {x,y} 需要判断的点
* @param coordinates [{x,y},{x,y}....] 多边形点坐标的数组,为保证图形能够闭合,起点和终点必须相等。这里有个要求,就是点坐标的数组集合的点必须是顺时针或逆时针,如果顺序不一样,无法判断
* 比如三角形需要四个点表示,第一个点和最后一个点必须相同。
* @param noneZeroMode 对不规则图形进行判断
*/
judge(dot, coordinates, noneZeroMode) {
// 默认启动none zero mode
noneZeroMode = noneZeroMode || 1;
var x = dot.x,
y = dot.y;
var crossNum = 0;
// 点在线段的左侧数目
var leftCount = 0;
// 点在线段的右侧数目
var rightCount = 0;
for (var i = 0; i < coordinates.length - 1; i++) {
var start = coordinates[i];
var end = coordinates[i + 1];
// 起点、终点斜率不存在的情况
if (start.x === end.x) {
// 因为射线向右水平,此处说明不相交
if (x > start.x) continue;
// 从左侧贯穿
if (end.y > start.y && y >= start.y && y <= end.y) {
leftCount++;
crossNum++;
}
// 从右侧贯穿
if (end.y < start.y && y >= end.y && y <= start.y) {
rightCount++;
crossNum++;
}
continue;
}
// 斜率存在的情况,计算斜率
var k = (end.y - start.y) / (end.x - start.x);
// 交点的x坐标
var x0 = (y - start.y) / k + start.x;
// 因为射线向右水平,此处说明不相交
if (x > x0) continue;
if (end.x > start.x && x0 >= start.x && x0 <= end.x) {
crossNum++;
if (k >= 0) leftCount++;
else rightCount++;
}
if (end.x < start.x && x0 >= end.x && x0 <= start.x) {
crossNum++;
if (k >= 0) rightCount++;
else leftCount++;
}
}
return noneZeroMode === 1
? leftCount - rightCount !== 0
: crossNum % 2 === 1;
},
效果