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

效果如下

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

<!DOCTYPE html>
<html lang="en">

<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 class="image_box">
    <!-- canvas 必须指定宽高,不然绘制出来的矩形会模糊和错误。 -->
    <canvas class="canvas_dom" 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 lang="en">

<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 class="image_box">
    <!-- TODO 自行更换图片 -->
    <!-- <img src="./test.png" alt="" id="imgDom"> -->
    <!-- canvas 必须指定宽高,不然绘制出来的矩形会模糊和错误。 -->
    <canvas class="canvas_dom" id="canvasDom" width="800" height="800">
      no canvas! Your Browser is not support canvas. Please change your browser.
    </canvas>
  </div>

  <div class="cut-box">

  </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>

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Canvas是HTML5新增的一个用于绘制图形的标签,可以通过JavaScript来控制Canvas进行图形的绘制。在进行Canvas绘制时,可以通过调整画布的位置、大小和旋转角度等属性,来控制绘制出来的图形的位置和形态。 下面我们就来介绍一下Canvas画布移动缩放和旋转: 1. 画布移动 画布移动可以通过Canvas提供的translate方法来实现。translate方法接收两个参数,分别表示x轴和y轴方向上的偏移量。偏移量为正值表示向右或向下移动,为负值表示向左或向上移动。 例如,我们可以通过下面的代码将画布向右移动50个像素,向下移动100个像素: ``` context.translate(50, 100); ``` 2. 画布缩放 画布缩放可以通过Canvas提供的scale方法来实现。scale方法接收两个参数,分别表示x轴和y轴方向上的缩放比例。缩放比例为大于1的值表示放大,小于1的值表示缩小。 例如,我们可以通过下面的代码将画布在x轴和y轴方向上都放大2倍: ``` context.scale(2, 2); ``` 3. 画布旋转 画布旋转可以通过Canvas提供的rotate方法来实现。rotate方法接收一个参数,表示旋转的角度,单位为弧度。 例如,我们可以通过下面的代码将画布旋转45度: ``` context.rotate(Math.PI / 4); ``` 需要注意的是,Canvas绘图的坐标系原点默认在画布的左上角,而移动缩放和旋转操作都是相对于原点进行的。因此,在进行这些操作时,需要先将画布的原点移动到需要的位置,再进行操作。例如,如果需要将画布向右移动50个像素,需要先将原点移动到(50,0)的位置,再进行移动操作: ``` context.translate(50, 0); ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值