您的当前位置:首页>全部文章>文章详情

【前端】使用canvas做一个可绘制矩形的画布(带有移动,缩放,删除)

CrazyPanda发表于:2024-04-07 16:08:20浏览:345次TAG:

效果如下

00bc04c6c82d4c3493db2e2b1b415789.gif

下面直接看代码吧,可以直接复制运行

<!DOCTYPE html>
<html>
 
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    * {
      padding: 0;
      margin: 0;
    }
 
    html,
    body {
      height: 100vh;
      width: 100vw;
      background-color: #ececec;
    }
 
    body {
      display: flex;
      justify-content: center;
      align-items: center;
    }
 
    /* 图像容器 */
    .image_box {
      position: relative;
      height: 600px;
      width: 600px;
    }
 
    img,
    .canvas_dom {
      height: 100%;
      width: 100%;
      position: absolute;
    }
 
    .canvas_dom {
      background-color: rgba(0, 0, 0, .2);
      cursor: crosshair;
    }
  </style>
</head>
 
<body>
 
  <div>
    <!-- canvas 必须指定宽高,不然绘制出来的矩形会模糊和错误。 -->
    <canvas id="canvasDom" width="600" height="600">
      no canvas! Your Browser is not support canvas. Please change your browser.
    </canvas>
  </div>
</body>
 
</html>
<script>
  // canvas对象
  let canvasDom = document.getElementById('canvasDom')
  let ctx2d = canvasDom.getContext("2d");
  // 保存矩形数据
  let recArrs = []
  // 记录当前的信息
  let curObj = {
    isRightClick: false, // 鼠标右键按下标识
    radious: 4,          // 范围误差值
    recSize: 5,         // 移动小框的大小
    index: -1,          // 当前矩形框的index
    side: 0,            // 边界值
    resize: false, // 是否拖拽大小
    draw: false, // 是否画图
    drag: false, // 是否拖动
    x: 0, // 画图的起始x
    y: 0, // 画图的起始y
    startX: 0, // x轴开始位置
    startY: 0, // y轴开始位置
  }
 
  // 获取canvas的宽高
  const canvasHeight = canvasDom.offsetHeight
  const canvasWidth = canvasDom.offsetWidth
  console.log('高度--', canvasHeight);
  console.log('宽度--', canvasWidth);
 
  // 注册事件
  canvasDom.addEventListener('mousemove', moveFunction)
  canvasDom.addEventListener('mousedown', downFunction)
  canvasDom.addEventListener('mouseup', upFunction)
  canvasDom.addEventListener('mouseout', outFunction)
  canvasDom.addEventListener('contextmenu', contextMenuFunction)
 
  // 禁止鼠标右键
  function contextMenuFunction(e) {
    e.preventDefault();
    return false
  }
 
  // 鼠标移出画布
  function outFunction(e) {
    let x = e.clientX;
    let y = e.clientY;
 
    let left = canvasDom.offsetLeft
    let top = canvasDom.offsetTop
    let width = canvasDom.offsetWidth
    let height = canvasDom.offsetHeight
 
    let limitX = left + width
    let limitY = top + height
 
    if(x < left || x > limitX || y < top || y > limitY ) {
      console.log('鼠标移出范围, 清除canvas,重新绘制');
      clearCanvas()
      drawOldRect()
    }
  }
 
  // 鼠标移动事件
  function moveFunction(e) {
    // console.log('鼠标移动', e);
    // 需要清除之前的辅助线
    clearCanvas()
    // 画辅助线
    drawRuler(e)
    // 清空辅助线和矩形数据后,这里重绘
    drawOldRect()
    // 画矩形
    drawRect(e)
    // 移动或缩放
    moveOrScale(e)
  }
 
  // 移动矩形框/缩放矩形框
  function moveOrScale(e) {
    let index = getEventIndex(e.offsetX, e.offsetY)
    let side = 0
    if(index > -1) {
      side = getEventArea(index, e.offsetX, e.offsetY)
      // 画移动小框
      if(side > 0) {
        drawLitRecs(index)
      }
    }
    // 鼠标样式
    changeResizeCursor(side);
 
    // 如果在移动
    moveRec(e)
 
    // 如果在缩放
    reSizeRec(e)
  }
  
  // 移动
  function moveRec(e) {
    if(curObj.drag && recArrs[curObj.index]) {
      let x = curObj.startX + e.offsetX - curObj.x
      let y = curObj.startY + e.offsetY - curObj.y
 
      let minX = canvasDom.offsetLeft
      let maxX = canvasDom.offsetLeft + canvasDom.offsetWidth - recArrs[curObj.index].w
      let minY = canvasDom.offsetTop
      let maxY = canvasDom.offsetTop + canvasDom.offsetHeight - recArrs[curObj.index].h
 
      if(x < minX) {
        x = minX
      }
      if( x > maxX) {
        x = maxX
      }
      if(y < minY) {
        y = minY
      }
      if( y > maxY) {
        y = maxY
      }
 
      recArrs[curObj.index].x = x
      recArrs[curObj.index].y = y
    }
  }
 
  // 缩放
  function reSizeRec(e) {
    const { side, index, recSize } = curObj
    const rec = recArrs[index]
    if(curObj.resize && rec) {
      const temX = rec.x;
      const temY = rec.y;
      const ex = e.offsetX
      const ey = e.offsetY
      if (side < 4 && temX + rec.w - ex > recSize) {
        rec.x = ex;
      }
      if (
        (side == 1 || side == 4 || side == 7) &&
        temY + rec.h - ey > recSize
      ) {
        rec.y = ey;
      }
      if (side < 4) {
        if (temX + rec.w - ex > recSize) {
          rec.w = temX + rec.w - ex;
        }
      } else if (side < 7) {
        if (ex - temX > recSize) {
          rec.w = ex - temX;
        }
      }
      if (side == 1 || side == 4 || side == 7) {
        if (temY + rec.h - ey > recSize) {
          rec.h = temY + rec.h - ey;
        }
      } else if (side == 3 || side == 6 || side == 8) {
        if (ey - temY > recSize) {
          rec.h = ey - temY;
        }
      }
    }
  }
 
  // 鼠标移动,画辅助线
  function drawRuler(e) {
    // 开始一条路径
    ctx2d.beginPath();
    // 填充色                            
    ctx2d.strokeStyle = "red";
    // 路径宽度                    
    ctx2d.lineWidth = 1;
    // 移动到 鼠标的 x位置, y位置 0(竖线的起点)                          
    ctx2d.moveTo(e.offsetX, 0);
    // lineTo() 方法添加一个新点,(竖线的终点)                   
    ctx2d.lineTo(e.offsetX, canvasHeight);
    // 移动到(x: 0, y:鼠标的位置)(横线的起点)
    ctx2d.moveTo(0, e.offsetY);
    // lineTo() 方法添加一个新点,(横线的终点)  
    ctx2d.lineTo(canvasWidth, e.offsetY);
    ctx2d.stroke();
  }
 
  // 清除canvas
  function clearCanvas() {
    ctx2d.clearRect(0, 0, canvasWidth, canvasHeight);
  }
 
  // 鼠标按下并移动,画矩形
  function drawRect(e) {
    if (curObj.draw) {
      ctx2d.strokeRect(
        curObj.x,
        curObj.y,
        e.offsetX - curObj.x,
        e.offsetY - curObj.y
      );
    }
  }
 
  // 画初始数据
  function drawOldRect() {
    if (!recArrs.length) return
    for (var i = 0; i < recArrs.length; i++) {
      // >2的判断是为了防止误触画出来的数据
      if(recArrs[i].w > 2 && recArrs[i].h > 2) {
        ctx2d.beginPath();
        ctx2d.lineWidth = 2;    // 矩形框的线条宽度
        ctx2d.strokeStyle = "rgb(255, 0, 0)";   // 矩形框的线条颜色
        ctx2d.strokeRect(recArrs[i].x, recArrs[i].y, recArrs[i].w, recArrs[i].h);     // 矩形框
 
        // 如果有文本信息,填充文本信息
        if (recArrs[i].text) {
          ctx2d.fillStyle = "purple";
          ctx2d.font = "14px 微软雅黑";
          ctx2d.lineWidth = 1;
          ctx2d.strokeStyle = "rgb(255,0,0)";
          ctx2d.strokeText(
            recArrs[i].text,
            (recArrs[i].x + recArrs[i].w / 2) - (recArrs[i].text.length / 2) * 16,
            recArrs[i].y - 20 < 0 ? recArrs[i].y + recArrs[i].h + 20 : recArrs[i].y - 10
          );
        }
      }
    }
  }
 
  // 鼠标按下事件
  function downFunction(e) {
    console.log('鼠标按下', e); // e.button 0 鼠标左键 1 鼠标滚轮 2 鼠标右键
    curObj.isRightClick = e.button > 1
    console.log('是否右键', curObj.isRightClick);
 
    // 赋值 x,y 轴起始数据
    curObj.x = e.offsetX
    curObj.y = e.offsetY
 
    // 判断是否落在的矩形框上
    //得到落点所在框的序数
    curObj.index = getEventIndex(curObj.x, curObj.y)
    console.log('落点矩形', curObj.index);
 
    // 如果是鼠标右键 TODO...
    if (curObj.isRightClick) {
 
    } else {
      // 鼠标左键
      // 如果鼠标落点不在矩形内,画矩形
      if(curObj.index === -1) {
        curObj.draw = true
      }else {
        // 落点在矩形内
        //移动或者放缩
        curObj.startX = recArrs[curObj.index].x;
        curObj.startY = recArrs[curObj.index].y;
        //得到落点在一个框中的区域
        curObj.side = getEventArea(curObj.index, curObj.x, curObj.y);
        console.log("curObj.side", curObj.side);
        if (curObj.side < 9) {
          //准备缩放
          console.log("在缩放");
          curObj.resize = true;
        } else {
          //准备拖动
          console.log("在拖动");
          curObj.drag = true;
        }
 
        // 画移动小框
        drawLitRecs(curObj.index);
      }
    }
    //判断小框类型
    changeResizeCursor(curObj.side);
  }
 
  // 修改鼠标样式
  function changeResizeCursor(side) {
    switch (side) {
      case 0:
        canvasDom.style.cursor = "crosshair";
        break;
      case 1:
        canvasDom.style.cursor = "se-resize";
        break;
      case 2:
        canvasDom.style.cursor = "e-resize";
        break;
      case 3:
        canvasDom.style.cursor = "ne-resize";
        break;
      case 4:
        canvasDom.style.cursor = "sw-resize";
        break;
      case 5:
        canvasDom.style.cursor = "w-resize";
        break;
      case 6:
        canvasDom.style.cursor = "nw-resize";
        break;
      case 7:
        canvasDom.style.cursor = "s-resize";
        break;
      case 8:
        canvasDom.style.cursor = "n-resize";
        break;
      case 9:
        canvasDom.style.cursor = "move";
        break;
      default:
        canvasDom.style.cursor = "default";
    }
  }
 
  //得到落点在一个框中的区域
  function getEventArea(index, x, y) {
    const { radious } = curObj
    const data = recArrs[index]
    console.log('recArrs', recArrs)
    console.log('data', data)
    if (x > data.x - radious && x < data.x + radious) {
      if (y > data.y - radious && y < data.y + radious) {
        return 1;
      } else if (y > data.y + radious && y < data.y + data.h - radious) {
        return 2;
      } else if (
        y > data.y + data.h - radious &&
        y < data.y + data.h + radious
      ) {
        return 3;
      }
    } else if (
      x > data.x + data.w - radious &&
      x < data.x + data.w + radious
    ) {
      if (y > data.y - radious && y < data.y + radious) {
        return 4;
      } else if (y > data.y + radious && y < data.y + data.h - radious) {
        return 5;
      } else if (
        y > data.y + data.h - radious &&
        y < data.y + data.h + radious
      ) {
        return 6;
      }
    } else {
      if (
        y > data.y - radious &&
        y < data.y + radious &&
        x > data.x + radious &&
        x < data.x + data.w - radious
      ) {
        return 7;
      } else if (
        y > data.y + data.h - radious &&
        y < data.y + data.h + radious &&
        x > data.x + radious &&
        x < data.x + data.w - radious
      ) {
        return 8;
      } else {
        return 9;
      }
    }
  }
 
  //把一个框的左上角坐标和宽高输入,得到8个坐标,左3,右3中2
  function prepareLitRecs(index) {
    const data = recArrs[index]
    var li = [];
    li[0] = [data.x, data.y];
    li[1] = [data.x, data.y + data.h / 2];
    li[2] = [data.x, data.y + data.h];
    li[3] = [data.x + data.w, data.y];
    li[4] = [data.x + data.w, data.y + data.h / 2];
    li[5] = [data.x + data.w, data.y + data.h];
    li[6] = [data.x + data.w / 2, data.y];
    li[7] = [data.x + data.w / 2, data.y + data.h];
    return li;
  }
 
  //画移动时的小框,data为矩形框9个点的坐标
  function drawLitRecs(index) {
    const data = prepareLitRecs(index)
    const { recSize } = curObj
    for (var i = 0; i < data.length; i++) {
      ctx2d.strokeRect(
        data[i][0] - recSize / 2,
        data[i][1] - recSize / 2,
        recSize,
        recSize,
      );
    }
  }
 
  // 获取鼠标落点所在矩形的index, -1 表示没有落在任何框内
  function getEventIndex(x, y) {
    if (!recArrs.length) return -1
    for (var i = 0; i < recArrs.length; i++) {
      const limitX = x > recArrs[i].x - curObj.radious
      const limitW = x < recArrs[i].x + recArrs[i].w + curObj.radious
      const limitY = y > recArrs[i].y - curObj.radious
      const limitH = y < recArrs[i].y + recArrs[i].h + curObj.radious
      // 有在范围内的,返回index
      if (limitX && limitY && limitW && limitH) {
        return i;
      }
      // 没有返回 -1
      if (i == recArrs.length - 1) {
        return -1;
      }
    }
  }
 
  // 鼠标抬起事件
  function upFunction(e) {
    console.log('鼠标抬起', e);
    if(curObj.isRightClick) {
      if(curObj.index !== -1) {
        // 删除,重绘
        recArrs.splice(curObj.index, 1)
        clearCanvas()
        drawOldRect()
      }
      curObj.isRightClick = false;
      return
    }
 
    curObj.resize = false
    curObj.drag = false
 
    // 如果是画图
    if (curObj.draw) {
      addToRecs(e)
      curObj.draw = false;
    }
 
    console.log('鼠标抬起-当前矩形框', recArrs);
  }
 
  // 添加矩形
  function addToRecs(e) {
    let rec = {
      x: curObj.x > e.offsetX ? e.offsetX : curObj.x, // x点
      y: curObj.y > e.offsetY ? e.offsetY : curObj.y, // y点
      w: Math.abs(e.offsetX - curObj.x), // 宽
      h: Math.abs(e.offsetY - curObj.y), // 高
      text: '矩形框' + recArrs.length, // 默认填充文本
      type: 1, // 类型
      // 其他需要的数据自行添加...
    };
    // 防止误触
    if(rec.w > 2 && rec.h > 2) {
      recArrs.push(rec)
      console.log('recArrs', recArrs);
    }
  }
</script>

在上面的基础上增加截图功能。效果如下:

代码,可直接更换图片运行查看效果

<!DOCTYPE html>
<html>
 
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    * {
      padding: 0;
      margin: 0;
    }
 
    html,
    body {
      height: 100vh;
      width: 100vw;
      background-color: #ececec;
    }
 
    body {
      display: flex;
      justify-content: center;
      align-items: center;
    }
 
    /* 图像容器 */
    .image_box {
      position: relative;
      height: 800px;
      width: 800px;
    }
 
    img,
    .canvas_dom {
      height: 100%;
      width: 100%;
      position: absolute;
    }
 
    .canvas_dom {
      background-color: rgba(0, 0, 0, .2);
      cursor: crosshair;
    }
 
    .cut-box {
      height: 800px;
      width: 800px;
      overflow: auto;
      background-color: bisque;
      margin-left: 20px;
    }
    .cut-img {
      height: 200px;
      width: 200px;
      position: relative;
      display: inline-block;
    }
  </style>
</head>
 
<body>
 
  <div>
    <!-- TODO 自行更换图片 -->
    <!-- <img src="./test.png" alt="" id="imgDom"> -->
    <!-- canvas 必须指定宽高,不然绘制出来的矩形会模糊和错误。 -->
    <canvas id="canvasDom" width="800" height="800">
      no canvas! Your Browser is not support canvas. Please change your browser.
    </canvas>
  </div>
 
  <div>
 
  </div>
</body>
 
</html>
<script>
  // 放置截图的容器
  let cutDom = document.querySelector('.cut-box')
  // 图像
  let imgDom = document.getElementById('imgDom')
  // canvas对象
  let canvasDom = document.getElementById('canvasDom')
  let ctx2d = canvasDom.getContext("2d");
  // 保存矩形数据
  let recArrs = []
  // 记录当前的信息
  let curObj = {
    isRightClick: false, // 鼠标右键按下标识
    radious: 4,          // 范围误差值
    recSize: 5,         // 移动小框的大小
    index: -1,          // 当前矩形框的index
    side: 0,            // 边界值
    resize: false, // 是否拖拽大小
    draw: false, // 是否画图
    drag: false, // 是否拖动
    x: 0, // 画图的起始x
    y: 0, // 画图的起始y
    startX: 0, // x轴开始位置
    startY: 0, // y轴开始位置
  }
 
  // 获取canvas的宽高
  const canvasHeight = canvasDom.offsetHeight
  const canvasWidth = canvasDom.offsetWidth
  console.log('高度--', canvasHeight);
  console.log('宽度--', canvasWidth);
 
  // 注册事件
  canvasDom.addEventListener('mousemove', moveFunction)
  canvasDom.addEventListener('mousedown', downFunction)
  canvasDom.addEventListener('mouseup', upFunction)
  canvasDom.addEventListener('mouseout', outFunction)
  canvasDom.addEventListener('contextmenu', contextMenuFunction)
 
  // 禁止鼠标右键
  function contextMenuFunction(e) {
    e.preventDefault();
    return false
  }
 
  // 鼠标移出画布
  function outFunction(e) {
    let x = e.clientX;
    let y = e.clientY;
 
    let left = canvasDom.offsetLeft
    let top = canvasDom.offsetTop
    let width = canvasDom.offsetWidth
    let height = canvasDom.offsetHeight
 
    let limitX = left + width
    let limitY = top + height
 
    if(x < left || x > limitX || y < top || y > limitY ) {
      console.log('鼠标移出范围, 清除canvas,重新绘制');
      clearCanvas()
      drawOldRect()
    }
  }
 
  // 鼠标移动事件
  function moveFunction(e) {
    // console.log('鼠标移动', e);
    // 需要清除之前的辅助线
    clearCanvas()
    // 画辅助线
    drawRuler(e)
    // 清空辅助线和矩形数据后,这里重绘
    drawOldRect()
    // 画矩形
    drawRect(e)
    // 移动或缩放
    moveOrScale(e)
  }
 
  // 移动矩形框/缩放矩形框
  function moveOrScale(e) {
    let index = getEventIndex(e.offsetX, e.offsetY)
    let side = 0
    if(index > -1) {
      side = getEventArea(index, e.offsetX, e.offsetY)
      // 画移动小框
      if(side > 0) {
        drawLitRecs(index)
      }
    }
    // 鼠标样式
    changeResizeCursor(side);
 
    // 如果在移动
    moveRec(e)
 
    // 如果在缩放
    reSizeRec(e)
  }
  
  // 移动
  function moveRec(e) {
    let rec = recArrs[curObj.index]
    if(curObj.drag && rec) {
      let x = curObj.startX + e.offsetX - curObj.x
      let y = curObj.startY + e.offsetY - curObj.y
 
      let minX = canvasDom.offsetLeft
      let maxX = canvasDom.offsetLeft + canvasDom.offsetWidth - rec.w
      let minY = canvasDom.offsetTop
      let maxY = canvasDom.offsetTop + canvasDom.offsetHeight - rec.h
 
      if(x < minX) {
        x = minX
      }
      if( x > maxX) {
        x = maxX
      }
      if(y < minY) {
        y = minY
      }
      if( y > maxY) {
        y = maxY
      }
 
      rec.x = x
      rec.y = y
 
      // 截图
      let { scleX, scleY } = getImgScale()
      let scx = rec.x / scleX
      let scy = rec.y / scleY
      let h = rec.h / scleY
      let w = rec.w / scleX
      rec.cutImgUrl = exportBase64(scx, scy, w, h, w, h)
      console.log('recArrs', recArrs);
      // 显示
      freshImg()
    }
  }
 
  // 缩放
  function reSizeRec(e) {
    const { side, index, recSize } = curObj
    const rec = recArrs[index]
    if(curObj.resize && rec) {
      const temX = rec.x;
      const temY = rec.y;
      const ex = e.offsetX
      const ey = e.offsetY
      if (side < 4 && temX + rec.w - ex > recSize) {
        rec.x = ex;
      }
      if (
        (side == 1 || side == 4 || side == 7) &&
        temY + rec.h - ey > recSize
      ) {
        rec.y = ey;
      }
      if (side < 4) {
        if (temX + rec.w - ex > recSize) {
          rec.w = temX + rec.w - ex;
        }
      } else if (side < 7) {
        if (ex - temX > recSize) {
          rec.w = ex - temX;
        }
      }
      if (side == 1 || side == 4 || side == 7) {
        if (temY + rec.h - ey > recSize) {
          rec.h = temY + rec.h - ey;
        }
      } else if (side == 3 || side == 6 || side == 8) {
        if (ey - temY > recSize) {
          rec.h = ey - temY;
        }
      }
 
      // 截图
      let { scleX, scleY } = getImgScale()
      let x = rec.x / scleX
      let y = rec.y / scleY
      let h = rec.h / scleY
      let w = rec.w / scleX
      rec.cutImgUrl = exportBase64(x, y, w, h, w, h)
      console.log('recArrs', recArrs);
      // 显示
      freshImg()
    }
  }
 
  // 鼠标移动,画辅助线
  function drawRuler(e) {
    // 开始一条路径
    ctx2d.beginPath();
    // 填充色                            
    ctx2d.strokeStyle = "red";
    // 路径宽度                    
    ctx2d.lineWidth = 1;
    // 移动到 鼠标的 x位置, y位置 0(竖线的起点)                          
    ctx2d.moveTo(e.offsetX, 0);
    // lineTo() 方法添加一个新点,(竖线的终点)                   
    ctx2d.lineTo(e.offsetX, canvasHeight);
    // 移动到(x: 0, y:鼠标的位置)(横线的起点)
    ctx2d.moveTo(0, e.offsetY);
    // lineTo() 方法添加一个新点,(横线的终点)  
    ctx2d.lineTo(canvasWidth, e.offsetY);
    ctx2d.stroke();
  }
 
  // 清除canvas
  function clearCanvas() {
    ctx2d.clearRect(0, 0, canvasWidth, canvasHeight);
  }
 
  // 鼠标按下并移动,画矩形
  function drawRect(e) {
    if (curObj.draw) {
      ctx2d.strokeRect(
        curObj.x,
        curObj.y,
        e.offsetX - curObj.x,
        e.offsetY - curObj.y
      );
    }
  }
 
  // 画初始数据
  function drawOldRect() {
    if (!recArrs.length) return
    for (var i = 0; i < recArrs.length; i++) {
      // >2的判断是为了防止误触画出来的数据
      if(recArrs[i].w > 2 && recArrs[i].h > 2) {
        ctx2d.beginPath();
        ctx2d.lineWidth = 2;    // 矩形框的线条宽度
        ctx2d.strokeStyle = "rgb(255, 0, 0)";   // 矩形框的线条颜色
        ctx2d.strokeRect(recArrs[i].x, recArrs[i].y, recArrs[i].w, recArrs[i].h);     // 矩形框
 
        // 如果有文本信息,填充文本信息
        if (recArrs[i].text) {
          ctx2d.fillStyle = "purple";
          ctx2d.font = "14px 微软雅黑";
          ctx2d.lineWidth = 1;
          ctx2d.strokeStyle = "rgb(255,0,0)";
          ctx2d.strokeText(
            recArrs[i].text,
            (recArrs[i].x + recArrs[i].w / 2) - (recArrs[i].text.length / 2) * 16,
            recArrs[i].y - 20 < 0 ? recArrs[i].y + recArrs[i].h + 20 : recArrs[i].y - 10
          );
        }
      }
    }
  }
 
  // 鼠标按下事件
  function downFunction(e) {
    console.log('鼠标按下', e); // e.button 0 鼠标左键 1 鼠标滚轮 2 鼠标右键
    curObj.isRightClick = e.button > 1
    console.log('是否右键', curObj.isRightClick);
 
    // 赋值 x,y 轴起始数据
    curObj.x = e.offsetX
    curObj.y = e.offsetY
 
    // 判断是否落在的矩形框上
    //得到落点所在框的序数
    curObj.index = getEventIndex(curObj.x, curObj.y)
    console.log('落点矩形', curObj.index);
 
    // 如果是鼠标右键 TODO...
    if (curObj.isRightClick) {
 
    } else {
      // 鼠标左键
      // 如果鼠标落点不在矩形内,画矩形
      if(curObj.index === -1) {
        curObj.draw = true
      }else {
        // 落点在矩形内
        //移动或者放缩
        curObj.startX = recArrs[curObj.index].x;
        curObj.startY = recArrs[curObj.index].y;
        //得到落点在一个框中的区域
        curObj.side = getEventArea(curObj.index, curObj.x, curObj.y);
        console.log("curObj.side", curObj.side);
        if (curObj.side < 9) {
          //准备缩放
          console.log("在缩放");
          curObj.resize = true;
        } else {
          //准备拖动
          console.log("在拖动");
          curObj.drag = true;
        }
 
        // 画移动小框
        drawLitRecs(curObj.index);
      }
    }
    //判断小框类型
    changeResizeCursor(curObj.side);
  }
 
  // 修改鼠标样式
  function changeResizeCursor(side) {
    switch (side) {
      case 0:
        canvasDom.style.cursor = "crosshair";
        break;
      case 1:
        canvasDom.style.cursor = "se-resize";
        break;
      case 2:
        canvasDom.style.cursor = "e-resize";
        break;
      case 3:
        canvasDom.style.cursor = "ne-resize";
        break;
      case 4:
        canvasDom.style.cursor = "sw-resize";
        break;
      case 5:
        canvasDom.style.cursor = "w-resize";
        break;
      case 6:
        canvasDom.style.cursor = "nw-resize";
        break;
      case 7:
        canvasDom.style.cursor = "s-resize";
        break;
      case 8:
        canvasDom.style.cursor = "n-resize";
        break;
      case 9:
        canvasDom.style.cursor = "move";
        break;
      default:
        canvasDom.style.cursor = "default";
    }
  }
 
  //得到落点在一个框中的区域
  function getEventArea(index, x, y) {
    const { radious } = curObj
    const data = recArrs[index]
    console.log('recArrs', recArrs)
    console.log('data', data)
    if (x > data.x - radious && x < data.x + radious) {
      if (y > data.y - radious && y < data.y + radious) {
        return 1;
      } else if (y > data.y + radious && y < data.y + data.h - radious) {
        return 2;
      } else if (
        y > data.y + data.h - radious &&
        y < data.y + data.h + radious
      ) {
        return 3;
      }
    } else if (
      x > data.x + data.w - radious &&
      x < data.x + data.w + radious
    ) {
      if (y > data.y - radious && y < data.y + radious) {
        return 4;
      } else if (y > data.y + radious && y < data.y + data.h - radious) {
        return 5;
      } else if (
        y > data.y + data.h - radious &&
        y < data.y + data.h + radious
      ) {
        return 6;
      }
    } else {
      if (
        y > data.y - radious &&
        y < data.y + radious &&
        x > data.x + radious &&
        x < data.x + data.w - radious
      ) {
        return 7;
      } else if (
        y > data.y + data.h - radious &&
        y < data.y + data.h + radious &&
        x > data.x + radious &&
        x < data.x + data.w - radious
      ) {
        return 8;
      } else {
        return 9;
      }
    }
  }
 
  //把一个框的左上角坐标和宽高输入,得到8个坐标,左3,右3中2
  function prepareLitRecs(index) {
    const data = recArrs[index]
    var li = [];
    li[0] = [data.x, data.y];
    li[1] = [data.x, data.y + data.h / 2];
    li[2] = [data.x, data.y + data.h];
    li[3] = [data.x + data.w, data.y];
    li[4] = [data.x + data.w, data.y + data.h / 2];
    li[5] = [data.x + data.w, data.y + data.h];
    li[6] = [data.x + data.w / 2, data.y];
    li[7] = [data.x + data.w / 2, data.y + data.h];
    return li;
  }
 
  //画移动时的小框,data为矩形框9个点的坐标
  function drawLitRecs(index) {
    const data = prepareLitRecs(index)
    const { recSize } = curObj
    for (var i = 0; i < data.length; i++) {
      ctx2d.strokeRect(
        data[i][0] - recSize / 2,
        data[i][1] - recSize / 2,
        recSize,
        recSize,
      );
    }
  }
 
  // 获取鼠标落点所在矩形的index, -1 表示没有落在任何框内
  function getEventIndex(x, y) {
    if (!recArrs.length) return -1
    for (var i = 0; i < recArrs.length; i++) {
      const limitX = x > recArrs[i].x - curObj.radious
      const limitW = x < recArrs[i].x + recArrs[i].w + curObj.radious
      const limitY = y > recArrs[i].y - curObj.radious
      const limitH = y < recArrs[i].y + recArrs[i].h + curObj.radious
      // 有在范围内的,返回index
      if (limitX && limitY && limitW && limitH) {
        return i;
      }
      // 没有返回 -1
      if (i == recArrs.length - 1) {
        return -1;
      }
    }
  }
 
  // 鼠标抬起事件
  function upFunction(e) {
    console.log('鼠标抬起', e);
    if(curObj.isRightClick) {
      if(curObj.index !== -1) {
        // 删除,重绘
        recArrs.splice(curObj.index, 1)
        clearCanvas()
        drawOldRect()
        // 显示
        freshImg()
      }
      curObj.isRightClick = false;
      return
    }
 
    curObj.resize = false
    curObj.drag = false
 
    // 如果是画图
    if (curObj.draw) {
      addToRecs(e)
      curObj.draw = false;
    }
 
    console.log('鼠标抬起-当前矩形框', recArrs);
  }
 
  // 添加矩形
  function addToRecs(e) {
    let rec = {
      x: curObj.x > e.offsetX ? e.offsetX : curObj.x, // x点
      y: curObj.y > e.offsetY ? e.offsetY : curObj.y, // y点
      w: Math.abs(e.offsetX - curObj.x), // 宽
      h: Math.abs(e.offsetY - curObj.y), // 高
      text: '矩形框' + recArrs.length, // 默认填充文本
      type: 1, // 类型
      // 其他需要的数据自行添加...
    };
    // 防止误触
    if(rec.w > 2 && rec.h > 2) {
      // 添加截图
      let { scleX, scleY } = getImgScale()
      let x = rec.x / scleX
      let y = rec.y / scleY
      let h = rec.h / scleY
      let w = rec.w / scleX
      rec.cutImgUrl = exportBase64(x, y, w, h, w, h)
      recArrs.push(rec)
      console.log('recArrs', recArrs);
 
      // 显示
      freshImg()
    }
  }
 
  // 获取当前图片的缩放比例
  function getImgScale() {
    // 图片原始大小
    let imgDefWidth = imgDom.naturalWidth
    let imgDefHeight = imgDom.naturalHeight
 
    // 图片的实际渲染尺寸
    let imgRenderWidth = imgDom.offsetWidth
    let imgRenderHeight = imgDom.offsetHeight
 
    // 图片x轴和y轴的缩放比例
    let scleX = imgRenderWidth / imgDefWidth
    let scleY = imgRenderHeight / imgDefHeight
 
    console.log('X轴缩放比例', scleX);
    console.log('Y轴缩放比例', scleY);
    return { scleX, scleY }
  }
 
  // 将所框的数据转为图片地址
  /**
   * 
   * @param {剪切的x轴开始坐标,相对于原图} x 
   * @param {剪切的y轴开始坐标,相对于原图} y 
   * @param {剪切的宽,相对于原图} sWidth 
   * @param {剪切的高,相对于原图} sHeight 
   * @param {输出的宽,默认按照剪切框大小输出} rW 
   * @param {输出的高,默认按照剪切框大小输出} rH 
   */
  function exportBase64(x, y, sWidth, sHeight, rW = 200, rH = 200) {
    // 创建一个新的canvas
    let canvasElement = document.createElement("canvas");
    canvasElement.width = `${rW}`
    canvasElement.height = `${rH}`
    let canvasContext = canvasElement.getContext("2d");
    canvasContext.drawImage(imgDom, x, y, sWidth, sHeight, 0, 0, rW, rH);
    let cutPicUrl = canvasElement.toDataURL("image/jpeg");
    return cutPicUrl
  }
 
  function freshImg() {
    // 先清除
    cutDom.innerHTML = ''
    recArrs.forEach( rec => {
      let item = document.createElement('img')
      item.src = rec.cutImgUrl
      item.className = 'cut-img'
      cutDom.appendChild(item)
    })
  }
</script>


猜你喜欢

【前端】使用canvas做一个可绘制矩形的画布(带有移动,缩放,删除)
使用canvas做一个可绘制矩形的画布(带有移动,缩放,删除)
发表于:2024-04-07 浏览:346 TAG:
【前端】vite和webpack的区别是什么
区别:1、webpack服务器启动速度比vite慢;由于vite启动的时候不需要打包,也就无需分析模块依赖、编译,所以启动速度非常快。2、vite热更新比webpack快;vite在hrm方面,当某个模块内容改变时,让浏览器去重新请求该模块即可。3、vite用esbuild预构建依赖,而webpack基于node。4、vite的生态不及webpack,加载器、插件不够丰富。本教程操作环境:windows7系统、vue3版,DELL G3电脑。写在开头最近的vite比较火,而且发布了2.0版本,v
发表于:2024-04-18 浏览:351 TAG:
【HTML】箩筐地图的使用,设置考勤范围
接上篇【PHP】html使用高德地图设置考勤范围,项目换掉了高德地图,替换成了箩筐地图,继续实现考勤打卡范围设置。需求:系统需要考勤功能,并在WEB端设置考勤范围,用于员工手机端在此范围内打卡签到,WEB端需要设置考勤地点以及考勤范围。 上篇高德地图已经实现了此功能,现在要换成箩筐地图实现此功能 基本思路:绘制地图-&gt;根据地址获取坐标-&gt;根据坐标绘制圆形范围-&gt;根据半径自适应显示圆形范围1 箩筐地图箩筐地图开放平台https://testlbs.luokuang.com/ 同样
发表于:2024-02-07 浏览:854 TAG:
【前端】vite+vue3+ts 项目安装 Ant Design of Vue方法
安装&nbsp;Ant Design of Vue 和@ant-design/icons-vue图标库npm&nbsp;add&nbsp;ant-design-vue&nbsp;@ant-design/icons-vue安装插件&nbsp;unplugin-vue-components&nbsp;配合vite可以自动帮我们引入组件npm&nbsp;add&nbsp;unplugin-vue-components&nbsp;-D配置 vite.config.tsimport&nbsp;{&amp;nbs
发表于:2024-02-23 浏览:306 TAG:
【前端】微信小程序跳转公众号的三种方式
&nbsp;最近因为项目需要,要在小程序页面添加按钮,点击跳转公众号对应页面,目前没有直接点击按钮从小程序跳转到公众号页面的方法,但也有变相的实现方法,最后采用小程序webview内嵌公众号页面的方法来实现相关功能,在此记录一下:相关参考:小程序跳转公众号的三种方法 | 微信开放社区 (qq.com)开放能力 / official-account (qq.com)方法: 1:&nbsp; 公众号组件&lt;official-account&gt;&lt;/official-account&gt;
发表于:2024-03-20 浏览:304 TAG:
【前端】前端实现文件下载自动打开预览的解决方法
问题:前端使用React开发,想要下载阿里云OSS存储的视频文件,使用了&lt;a&gt;标签,点击后就会在浏览器打开预览,查找到了几个解决办法,在此记录一下。一、 下载文件的三种通用方式1、使用iframe实现只需要传一个文件下载地址的url即可&nbsp;downloadFile&nbsp;=&nbsp;(url)&nbsp;=&gt;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;//下载方法 &nbsp;&nbsp;&nbsp;&nbsp;console.log(url)
发表于:2024-05-18 浏览:290 TAG:
【前端】PHP开发者的Vue与React入门指南
PHP是一种常用的服务器端脚本语言,用于开发动态网页和网站。许多PHP开发者经常需要学习前端框架来提升他们的技能,并且Vue.js和React.js是两个当前非常热门的前端框架。本文将为PHP开发者提供一份Vue.js和React.js的入门指南,带有具体的代码示例,帮助他们快速了解这两个框架的基本概念和用法。1. Vue.js入门指南Vue.js是一款轻量级的JavaScript框架,用于构建交互式的用户界面。下面是一个简单的Vue.js示例,展示了如何创建一个基本的Vue组件并进行数据绑定:
发表于:2024-03-16 浏览:335 TAG:
【前端】JavaScript判断数组对象是否含有某个值的方法(6种)
【JavaScript基础语法】web前端判断数组对象是否含有某个值的方法(6种)知识回调场景复现实现方式(6种)利用循环遍历数组元素利用some,filter方法利用array.indexOf方法利用array.includes方法利用array.find方法利用set中has方法本期小结知识回调文章内容文章链接vue3 antd table表格的增删改查(一)input输入框根据关键字搜索【后台管理系统纯前端filter过滤】https://blog.csdn.net/XSL_HR/arti
发表于:2024-02-23 浏览:340 TAG:
【前端】2023年最流行的10款前端UI框架排名
&nbsp; &nbsp; &nbsp; &nbsp; 上次我们发布了《2022年最流行的11款PHP框架》,争议很大!今天我们来继续探讨一下:2023年最流行的10款前端UI框架排名。一:前端UI框架是什么?前端UI框架是一种基于HTML、CSS、JavaScript等前端技术的开发工具集,提供了一系列的UI组件、样式、布局等基础功能,使得前端开发人员可以更加高效地开发出具有良好用户体验的Web应用。二:为什么要使用前端UI框架?前端UI框架可以大大减少前端开发人员的工作量,提高开发效
发表于:2023-12-06 浏览:393 TAG:
【HTML】必应地图BingMaps的使用
又换新地图了,之前换了箩筐地图,效果不能满足实际需要,这次换成了必应地图BingMaps,同样实现设置考勤区域的功能,效果还是挺不错的。相关阅读【HTML】html使用高德地图设置考勤范围&nbsp;【HTML】箩筐地图的使用,设置考勤范围一、&nbsp;获取应用KEY还是老样子,先注册账号,然后获取key。注册参考官方说明https://www.bingmap.cn/guide/db765008-dafe-11e8-a995-d46d6d978bfa?module=doc,这里不再过多说明。
发表于:2024-02-19 浏览:372 TAG: