Html,vue中使用canvas绘制多边形选择选择框,包含拖动,坐标显示

这是我自己在工作中,开发的一个HTML小工具,是一个框选工具,使用canvas进行绘制,可以框选图片中的目标,也可以框选视频中的目标,虽然是vue组件,确实你也可以用在html项目中,因为是使用Typescript写的,可以自己去转换成JavaScript,我没有做更多的拓展。代码有部分代码也此功能无关可以去掉,看自己的需求来。
如果文章有错误,欢迎指正。

效果:
效果1
gif效果:
gif效果

Quadrilateral.vue 多边形选择框文件代码

<template>
  <div id="divCanvas">
    <video id="videoCan" width="1000" height="600" autoplay>
      <source src="../../assets/movie.mp4" type="video/mp4" />
    </video>
    <canvas id="graphCanvas"></canvas>
    <canvas id="personCanvas"></canvas>
    <canvas id="pointCanvas" :style="myStyle"></canvas>
  </div>
</template>

<script lang="ts">
import { defineComponent, onMounted, ref } from "vue";
import PointBean from "./PointBean";
export default defineComponent({
  name: "Quadrilateral",
  props: {
    myStyle: Object,
  },
  setup(_, context) {
    //画布宽高
    const canvasWidth = ref(1000);
    const canvasHeight = ref(600);
    //点的范围
    const pointRange = ref(10);
    //拖动点的范围
    const pointRangeDrag = ref(50);
    // 线条颜色
    const colorVal = ref("rgba(255,0,0,1)");
    // 填充颜色
    const colorValA = ref("rgba(255,0,0,.5)");
    // 坐标文字颜色
    const colorValPoint = ref("rgba(0,255,0,1)");
    // 线条粗细,画笔大小
    const lineWidthVal = ref(4);
    //人的坐标数组
    const personArr = ref([0.13,24,15,23]);

    //用户可以直接在组件上设置的属性
    if (context.attrs.width != undefined) {
      canvasWidth.value = context.attrs.width as number;
    }
    if (context.attrs.height != undefined) {
      canvasHeight.value = context.attrs.height as number;
    }
    if (context.attrs.pointRange != undefined) {
      pointRange.value = context.attrs.pointRange as number;
    }
    if (context.attrs.lineColor != undefined) {
      colorVal.value = context.attrs.lineColor as string;
    }
    if (context.attrs.fillColor != undefined) {
      colorValA.value = context.attrs.fillColor as string;
    }
    if (context.attrs.lineWidth != undefined) {
      lineWidthVal.value = context.attrs.lineWidth as number;
    }
    if (context.attrs.personArr != undefined) {
      personArr.value = context.attrs.personArr as [];
    }

    let canGraph: HTMLCanvasElement;
    let ctxGragh: CanvasRenderingContext2D;
    let canPoint: HTMLCanvasElement;
    let ctxPoint: CanvasRenderingContext2D;
    let canPerson: HTMLCanvasElement;
    let ctxPerson: CanvasRenderingContext2D;

    const isMouseDown = ref(false);
    const isMouseUp = ref(false);
    const isMouseMove = ref(false);
    const isDrag = ref(false);
    //拖动的点
    const dragPosition = ref(-1);

    let pointArr = ref(new Array<PointBean>());
    function clearView() {
      ctxGragh.clearRect(0, 0, canGraph.width, canGraph.height);
      ctxPoint.clearRect(0, 0, canPoint.width, canPoint.height);
      pointArr.value = new Array<PointBean>();
    }
    function canOnClick(e: PointerEvent) {
      // console.log("鼠标点击", e);
      if (pointArr.value.length < 4) {
        let piX: number, piY: number;
        piX = e.offsetX == undefined ? e.clientX : e.offsetX;
        piY = e.offsetY == undefined ? e.clientY : e.offsetY;
        if (pointArr.value.length == 0) {
          pointArr.value.push({ x: piX, y: piY });
        } else {
          //此点是否已经存在数组中
          let isExist = false;
          for (let i = 0; i < pointArr.value.length; i++) {
            if (
              piX >= pointArr.value[i].x - pointRange.value &&
              piX <= pointArr.value[i].x + pointRange.value &&
              piY >= pointArr.value[i].y - pointRange.value &&
              piY <= pointArr.value[i].y + pointRange.value
            ) {
              //判断为同一点,不存储
              console.log("此点已存在");
              isExist = true;
              break;
            }
          }
          if (!isExist) {
            pointArr.value.push({ x: piX, y: piY });
          }
        }
        drawArea();
      }
    }
    function canOnmouseUp(e: MouseEvent) {
      console.log("鼠标抬起", e);
      if (isDrag.value) {
        //这里来减少点
        pointArrMerge();
        drawArea();
        isDrag.value = false;
        dragPosition.value = -1;
      }
    }
    function canOnmouseMove(e: MouseEvent) {
      // console.log("鼠标移动", e);
      ctxPoint.clearRect(0, 0, canPoint.width, canPoint.height);
      let piX: number, piY: number;
      piX = e.offsetX == undefined ? e.clientX : e.offsetX;
      piY = e.offsetY == undefined ? e.clientY : e.offsetY;

      piX = piX <= 0 ? 0 : piX;
      piY = piY <= 0 ? 0 : piY;
      piX = piX >= canPoint.width ? canPoint.width : piX;
      piY = piY >= canPoint.height ? canPoint.height : piY;

      drawMoveArea(piX, piY);

      dot(piX, piY);
      //显示点坐标

      pointShow(piX, piY);

      //判断是否是拖动
      if (isMouseDown.value && isMouseMove.value && !isMouseUp.value) {
        if (isDrag.value) {
          drawDragArea(piX, piY);
        } else {
          dragPoint(piX, piY);
        }
      }
    }
    function canOnmouseDown(e: MouseEvent) {
      console.log("鼠标按下", e);
    }

    //============================
    //删除重复的点
    function pointArrMerge() {
      for (let i = 0; i < pointArr.value.length; i++) {
        for (let j = i + 1; j < pointArr.value.length; j++) {
          if (
            pointArr.value[i].x >= pointArr.value[j].x - pointRange.value &&
            pointArr.value[i].x <= pointArr.value[j].x + pointRange.value &&
            pointArr.value[i].y >= pointArr.value[j].y - pointRange.value &&
            pointArr.value[i].y <= pointArr.value[j].y + pointRange.value
          ) {
            pointArr.value.splice(i, 1);
          }
        }
      }
    }
    //鼠標下畫點
    function dot(x: number, y: number) {
      ctxPoint.clearRect(0, 0, canPoint.width, canPoint.height);
      ctxPoint.beginPath();
      ctxPoint.arc(x <= 0 ? 0 : x, y, lineWidthVal.value, 0, 2 * Math.PI);
      ctxPoint.fillStyle = colorVal.value;
      ctxPoint.fill();
    }
    //画区域
    function drawArea() {
      ctxGragh.clearRect(0, 0, canGraph.width, canGraph.height);
      ctxGragh.beginPath();
      ctxGragh.moveTo(pointArr.value[0].x, pointArr.value[0].y);
      console.log(0, pointArr.value[0].x, pointArr.value[0].y);
      for (let i = 1; i < pointArr.value.length; i++) {
        ctxGragh.lineTo(pointArr.value[i].x, pointArr.value[i].y);
        console.log(i, pointArr.value[i].x, pointArr.value[i].y);
      }
      ctxGragh.strokeStyle = colorVal.value;
      ctxGragh.fillStyle = colorValA.value;
      ctxGragh.lineWidth = lineWidthVal.value;
      ctxGragh.closePath();
      ctxGragh.stroke();
      ctxGragh.fill();
    }
    //画移动的区域
    function drawMoveArea(x: number, y: number) {
      if (pointArr.value.length > 0 && pointArr.value.length < 4) {
        ctxGragh.clearRect(0, 0, canGraph.width, canGraph.height);
        ctxGragh.beginPath();
        ctxGragh.moveTo(pointArr.value[0].x, pointArr.value[0].y);
        for (let i = 1; i < pointArr.value.length; i++) {
          ctxGragh.lineTo(pointArr.value[i].x, pointArr.value[i].y);
        }
        if (x <= 0) {
          x = 0;
        }
        ctxGragh.lineTo(x, y);
        ctxGragh.strokeStyle = colorVal.value;
        ctxGragh.fillStyle = colorValA.value;
        ctxGragh.lineWidth = lineWidthVal.value;
        ctxGragh.closePath();
        ctxGragh.stroke();
        ctxGragh.fill();
      }
    }
    //显示点的坐标
    function pointShow(x: number, y: number) {
      let i = -1;
      let text;
      if (pointArr.value.length > 0) {
        for (i = 0; i < pointArr.value.length; i++) {
          text =
            i + "(" + pointArr.value[i].x + "," + pointArr.value[i].y + ")";
          textCanShow(pointArr.value[i].x, pointArr.value[i].y, text);
        }
        if (i < 4) {
          text = i + "(" + x + "," + y + ")";
          textCanShow(x, y, text);
        }
      } else {
        text = 0 + "(" + x + "," + y + ")";
        textCanShow(x, y, text);
      }
    }
    //显示文字
    function textCanShow(x: number, y: number, text: string) {
      //需要判断4个角,防止文字显示不到
      //左上角
      ctxPoint.beginPath();
      if (x < 50) {
        ctxPoint.textAlign = "left";
      } else if (x > canPoint.width - 50) {
        ctxPoint.textAlign = "right";
      } else {
        ctxPoint.textAlign = "center";
      }

      if (y > canPoint.height - 50) {
        //点在下面,文字在上面
        ctxPoint.textBaseline = "bottom";
      } else {
        ctxPoint.textBaseline = "top";
      }
      ctxPoint.font = "normal 18pt '黑体'";
      ctxPoint.fillStyle = colorValPoint.value;
      ctxPoint.fillText(text, x, y);
      //右上角
      //左下角
      //右下角
    }

    function dragPoint(x: number, y: number) {
      console.log("拖动");
      for (let i = 0; i < pointArr.value.length; i++) {
        if (
          x >= pointArr.value[i].x - pointRangeDrag.value &&
          x <= pointArr.value[i].x + pointRangeDrag.value &&
          y >= pointArr.value[i].y - pointRangeDrag.value &&
          y <= pointArr.value[i].y + pointRangeDrag.value
        ) {
          dragPosition.value = i;
          isDrag.value = true;
          console.log("拖动", i);
        }
      }
    }

    function drawDragArea(x: number, y: number) {
      if (dragPosition.value != -1) {
        pointArr.value.splice(dragPosition.value, 1, { x, y });
        drawArea();
      }
    }
    //有带改进 一个来监听personArr.value 来画
    function drawPersonArr(){
      ctxPerson.clearRect(0,0,canPerson.width,canPerson.height);
      console.log(canPerson.width,canPerson.height);
      if(personArr.value.length>0){
        ctxPerson.beginPath();
        ctxPerson.rect(canPerson.width-120,20,110,(personArr.value.length+1)*21);
         ctxPerson.font = "normal 18pt '黑体'";
          ctxPerson.fillStyle = colorValPoint.value;
          ctxPerson.fillText("个数:"+personArr.value.length,canPerson.width-110,40);
        for(let i=0;i<personArr.value.length;i++){
          // textCanShow(canPerson.width-125,40+i*10,i+":"+personArr.value[i]);
          ctxPerson.fillText(i+":"+personArr.value[i],canPerson.width-110,(40+((i+1)*20)));
        }
        ctxPerson.closePath();
        ctxPerson.stroke();
      }
    }

    const setPersonArr = (arr:[])=>{
      if(arr.length>0){
        personArr.value = arr;
        drawPersonArr();
      }
    }

    onMounted(() => {
      canGraph = document.getElementById("graphCanvas") as HTMLCanvasElement;
      ctxGragh = canGraph.getContext("2d") as CanvasRenderingContext2D;
      canPoint = document.getElementById("pointCanvas") as HTMLCanvasElement;
      ctxPoint = canPoint.getContext("2d") as CanvasRenderingContext2D;
      canPerson = document.getElementById("personCanvas") as HTMLCanvasElement;
      ctxPerson = canPerson.getContext("2d") as CanvasRenderingContext2D;

      canGraph.width = canvasWidth.value;
      canGraph.height = canvasHeight.value;
      canPoint.width = canvasWidth.value;
      canPoint.height = canvasHeight.value;
      canPerson.width = canvasWidth.value;
      canPerson.height = canvasHeight.value;

      ctxGragh.strokeStyle = colorVal.value; //线条颜色
      ctxGragh.lineWidth = lineWidthVal.value; //线条粗细
      ctxPoint.strokeStyle = colorVal.value; //线条颜色
      ctxPoint.lineWidth = lineWidthVal.value; //线条粗细
      ctxPerson.strokeStyle = colorVal.value; //线条颜色
      ctxPerson.lineWidth = lineWidthVal.value; //线条粗细

      canPoint.onmouseup = (e) => {
        // console.log("鼠标抬起", e);
        isMouseUp.value = true;
        isMouseMove.value = false;
        isMouseDown.value = false;
        canOnmouseUp(e);
      };
      canPoint.onmousemove = (e) => {
        isMouseMove.value = true;
        // console.log("鼠标移动",e);
        canOnmouseMove(e);
      };
      canPoint.onmousedown = (e) => {
        // console.log("鼠标按下", e);
        isMouseDown.value = true;
        isMouseUp.value = false;
        canOnmouseDown(e);
      };
      canPoint.onclick = (e) => {
        // console.log("鼠标点击", e);
        canOnClick(e as PointerEvent);
      };
    });

    return {
      clearView,
      pointArr,
      textCanShow,
      setPersonArr,
    };
  },
});
</script>

<style scoped>
#divCanvas {
  position: relative;
}
#graphCanvas ,#personCanvas{
  position: absolute;
  left: 0;
  top: 0;
}
#pointCanvas {
  position: absolute;
  left: 0;
  top: 0;
  border: 1px solid red;
  cursor: crosshair;
}
#videoCan {
  position: absolute;
  left: 0;
  top: 0;
}
</style>

PointBean.ts 数据类代码

export default class PointBean{
    x:number;
    y:number;

    constructor(x:number,y:number){
        this.x = x;
        this.y = y;
    }
}

使用代码

<template>
  <div>
    <h1>def</h1>
    <Quadrilateral
      :myStyle="{ border: '1px solid red' }"
      ref="RefChilde"
    ></Quadrilateral>
    <button @click="ddd">asdsd</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref,  onMounted } from "vue";
import Quadrilateral from "../utils/Quadrilateral.vue";

export default defineComponent({
  name: "def",
  components: { Quadrilateral },
  setup() {
    let RefChilde = ref();
    // RefQuadrilateral.value.setPersonArr([1,2,3,4])
    function ddd() {
      RefChilde.value.setPersonArr([1, 2, 3, 4, 5, "asd"]);
    }
    onMounted(()=>{
     RefChilde.value.setPersonArr([1,2,3,4]);
    });
    return {
      RefChilde,
      ddd,
    };
  },
});
</script>

<style>
</style>

  • 4
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值