关于canvas画布上绘制多个(单个也一样)不规则多边形,用户点击这张画布时,判断点击的是哪个多边形

首先来看看canvas效果图

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;
    },
效果

在这里插入图片描述