利用 Canvas 对图片进行标识,矩形 点,线段,并且可以拖动形状,缩放形状

庄萧迟
2023-12-01

1.canvas 主要的几个方法

//html 元素
<canvas width="500" height="300" id="canvas"></canvas>


//js 初始化背景
let canvas = document.getElementById("canvas")//获取canvas对象
//创建Image对象
let img = new Image();
img.src = this.imageSrc; //imgSrc 是背景图片的地址 我用的是 base64码
img.onload = function () {
 let ctx = canvas.getContext("2d"); //获取画图2d笔
 ctx.drawImage(this, 0, 0, canvas.width, canvas.height) //设置背景图的长宽和 canvas的一样
}

2.绘制形状

2.1 绘制矩形

// 绘制矩形
function drawerRect(ctx, left, top, w, h) {
  ctx.strokeStyle = '#889385'// 画笔颜色
  ctx.lineWidth = '2' // 画笔粗细
  //ctx.save()
  ctx.beginPath()
  ctx.rect(left, top, w, h)
  ctx.stroke()
  ctx.restore()
  return {
    data: [left, top, w, h]
  }
}

2.2 绘制线段

// 绘制直线
function drawerLine(ctx, x, y, z, n) {
  ctx.save()
  ctx.fillStyle = '#889385'
  ctx.lineWidth = '2' // 画笔粗细
  ctx.strokeStyle = '#889385'// 画笔颜色
  ctx.beginPath()
  ctx.moveTo(x, y)
  ctx.lineTo(z, n)
  ctx.stroke()
  ctx.restore()
  return {
    data: [x, y, z, n]
  }
}

2.3 绘制点

function drawerPoint(ctx, x, y) {
  ctx.lineWidth = '2' // 画笔粗细
  let w = 20
  ctx.moveTo(x - w / 2, y);
  ctx.lineTo(x + w / 2, y);
  ctx.strokeStyle = '#889385'
  ctx.stroke();
  ctx.beginPath();
  ctx.lineWidth = '2' // 画笔粗细
  ctx.moveTo(x, y - w / 2);
  ctx.lineTo(x, y + w / 2);
  ctx.strokeStyle = '#889385'
  ctx.stroke();
  return {
    data: [x, y]
  }
}

3.动态化图形

实际是对画布进行不间断的清除和 重画,我们可以将画好的图形保存在一个数组中,在清除重画后从这个数组里面找到所有的图形  并且调用上面的方法进行重画

清除画布:

ctx.clearRect(0, 0, canvas.width, canvas.height);

重新加载背景 :

ctx.drawImage(image,0,0,canvas.width,canvas.height)

在绘画的时候,需要进行监听,方法有onmousedown onmouseup onmousemove

4.拖拽绘制完毕的形状

拖拽实际就是监听鼠标的位置,并且在画布上不断的画图,注意:需要将数组中已经存在的目标形状清除掉

5.拖动改变大小

该操作实际和上面类似,监听鼠标即可

完整代码如下(没有优化  但是达到了效果):

在需要的页面调用:、

initCanvas(canvas,img)

绘制什么形状,调用

drawer(shape) //1矩形  3 线段  4 点

删除形状 方法:

setRemove()  在页面上点下想要删除的图形

全部删除:

setRemove() //设置系统需要进行删除
removeAllShape() //删除全部形状

let canvas = undefined
let ctx = undefined
export let shapeList = []
let backImage = new Image();
let canDraw = false //是否可以绘画
let canDrag = false //是否可以拖拽
let canResize = false //是否可以缩放
let canRemove = false //是否可以进行删除
export let currentShape = undefined
let resizePoint = {}

function Point(x, y, type) {
  this.x = x
  this.y = y
  this.type = type // 左击 1  右击 3
}

export function windowToCanvas(e, canvas) {
  // 返回元素的大小以及位置
  let rect = canvas.getBoundingClientRect()
  // rect 的宽度会加上 canvas 的 border 会影响精度
  return new Point(e.clientX - rect.left * (canvas.width / rect.width),
    e.clientY - rect.top * (canvas.height / rect.height), e.which)
}

function setTitle(x, y, context, name) {
  context.font = 'bold 15px Arial';
  context.textAlign = 'center';
  context.textBaseline = 'bottom';
  context.fillStyle = '#39e9d6';
  context.fillText(name, x, y);
  return true
}

// 绘制矩形
function drawerRect(ctx, left, top, w, h) {
  ctx.strokeStyle = '#889385'// 画笔颜色
  ctx.lineWidth = '2' // 画笔粗细
  //ctx.save()
  ctx.beginPath()
  ctx.rect(left, top, w, h)
  ctx.stroke()
  ctx.restore()
  return {
    data: [left, top, w, h]
  }
}

// 绘制圆
function drawerCircle(ctx, x, y, r) {
  if (canDraw && !canDrag) {
    ctx.strokeStyle = '#889385'// 画笔颜色
    ctx.lineWidth = '2' // 画笔粗细
    ctx.beginPath()// 开始路径
    ctx.arc(x, y, r, 0, Math.PI * 2, true)// 参数依次为圆心坐标x,y,半径,开始结束角,绘制方向顺时针
    ctx.stroke()
    ctx.restore()
    return {
      data: [x, y, r]
    }
  }
}

// 绘制直线
function drawerLine(ctx, x, y, z, n) {
  ctx.save()
  ctx.fillStyle = '#889385'
  ctx.lineWidth = '2' // 画笔粗细
  ctx.strokeStyle = '#889385'// 画笔颜色
  ctx.beginPath()
  ctx.moveTo(x, y)
  ctx.lineTo(z, n)
  ctx.stroke()
  ctx.restore()
  return {
    data: [x, y, z, n]
  }
}

function drawerPoint(ctx, x, y) {
  ctx.lineWidth = '2' // 画笔粗细
  let w = 20
  ctx.moveTo(x - w / 2, y);
  ctx.lineTo(x + w / 2, y);
  ctx.strokeStyle = '#889385'
  ctx.stroke();
  ctx.beginPath();
  ctx.lineWidth = '2' // 画笔粗细
  ctx.moveTo(x, y - w / 2);
  ctx.lineTo(x, y + w / 2);
  ctx.strokeStyle = '#889385'
  ctx.stroke();
  return {
    data: [x, y]
  }
}


function inSideCircle(x1, y1, x, y) {
  return Math.sqrt((x - x1) * (x - x1) + (y - y1) * (y - y1)) <= 5;
}

//鼠标是否落在矩形内
export function isInsideRect(p0, p1, p) {
  let x0 = p0.x
  let y0 = p0.y
  let x1 = p1.x
  let y1 = p1.y
  let x = p.x
  let y = p.y
  let distanceP1 = inSideCircle(x0, y0, x, y)
  let distanceP2 = inSideCircle(x1, y1, x, y)
  if (distanceP1 || distanceP2) {//缩放
    if (distanceP1) {
      resizePoint = {
        x: x1,
        y: y1
      }
    } else if (distanceP2) {
      resizePoint = {
        x: x0,
        y: y0
      }
    }
    return 2
  } else if ((x0 < x && x < x1) && ((y1 < y && y < y0) || (y0 < y && y < y1))) { //移动
    return 1
  } else { //默认
    return 0
  }
}

//鼠标是否落在线段所属范围内
export function isInsideLine(p1, p2, p3) {
  let distanceP1 = inSideCircle(p1.x, p1.y, p3.x, p3.y)
  let distanceP2 = inSideCircle(p2.x, p2.y, p3.x, p3.y)
  if (distanceP1 || distanceP2) {
    if (distanceP1) {
      resizePoint = {
        x: p2.x,
        y: p2.y
      }
    } else if (distanceP2) {
      resizePoint = {
        x: p1.x,
        y: p1.y
      }
    }
    return 2
  }
  let len;
  //如果p1.x==p2.x 说明是条竖着的线
  if (p1.x - p2.x == 0) {
    len = Math.abs(p3.x - p1.x)
  } else {
    let A = (p1.y - p2.y) / (p1.x - p2.x)
    let B = p1.y - A * p1.x
    len = Math.abs((A * p3.x + B - p3.y) / Math.sqrt(A * A + 1))
  }
  return len <= 1 ? 1 : 0;
}

//鼠标是否落在点上
export function isInPoint(p0, p1) {
  return Math.sqrt(Math.abs(p0.x - p1.x) * Math.abs(p0.x - p1.x) + Math.abs(p0.y - p1.y) * Math.abs(p0.y - p1.y)) <= 4 ? 1 : 0
}


function listenDrawRect() {
  let ctx = canvas.getContext('2d')
  let type = 1
  canvas.onmousedown = function (e) {
    e.preventDefault()
    let mousedown = windowToCanvas(e, canvas)
    canvas.onmousemove = function (e) {
      if (canDrag) {  //拖拽逻辑
        let point = windowToCanvas(e, canvas) //当前点的坐标
        let left = currentShape.shapeStart.x + point.x - mousedown.x;
        let top = currentShape.shapeStart.y + point.y - mousedown.y;
        removeCurrentShapeInShapeList()
        reDrawAllShape(ctx, canvas)
        drawerRect(ctx, left, top, currentShape.width, currentShape.height)
        setTitle(left, top, ctx, '矩形');
      } else if (canDraw) { //第一次画
        reDrawAllShape(ctx, canvas)
        e.preventDefault()
        let point = windowToCanvas(e, canvas)
        let w = Math.abs(point.x - mousedown.x)
        let h = Math.abs(point.y - mousedown.y)
        let left = point.x > mousedown.x ? mousedown.x : point.x
        let top = point.y > mousedown.y ? mousedown.y : point.y
        drawerRect(ctx, left, top, w, h)
      } else if (canResize) { //对角线缩放
        e.preventDefault()
        let point = windowToCanvas(e, canvas)
        let x = point.x
        let y = point.y
        removeCurrentShapeInShapeList()
        reDrawAllShape(ctx, canvas)
        let w = Math.abs(x - resizePoint.x)
        let h = Math.abs(y - resizePoint.y)
        let left = x > resizePoint.x ? resizePoint.x : x
        let top = y > resizePoint.y ? resizePoint.y : y
        drawerRect(ctx, left, top, w, h)
        setTitle(left, top, ctx, '矩形');
      }
    }
    canvas.onmouseup = function (e) {
      if (canDraw) {
        let result = setTitle(mousedown.x, mousedown.y, ctx, '矩形');
        if (result) {
          let point = windowToCanvas(e, canvas)
          let shapeStart = mousedown
          let shapeEnd = point
          let width = Math.abs(point.x - mousedown.x)
          let height = Math.abs(point.y - mousedown.y)
          let uid = guid();
          shapeList.push({shapeStart, shapeEnd, width, height, type, uid})
        }
        canDraw = false;
      } else if (canDrag) {
        let point = windowToCanvas(e, canvas)
        if (!(point.x===mousedown.x && point.y===mousedown.y)){
          currentShape.shapeStart.x = currentShape.shapeStart.x + point.x - mousedown.x
          currentShape.shapeStart.y = currentShape.shapeStart.y + point.y - mousedown.y
          currentShape.shapeEnd.x = currentShape.shapeEnd.x + point.x - mousedown.x
          currentShape.shapeEnd.y = currentShape.shapeEnd.y + point.y - mousedown.y
          shapeList.push(currentShape)
          canDrag = false;
        }
      } else if (canResize) {
        let point = windowToCanvas(e, canvas)
        currentShape.shapeEnd.x = point.x > resizePoint.x ? point.x : resizePoint.x
        currentShape.shapeEnd.y = point.y > resizePoint.y ? point.y : resizePoint.y
        currentShape.shapeStart.x = point.x > resizePoint.x ? resizePoint.x : point.x
        currentShape.shapeStart.y = point.y > resizePoint.y ? resizePoint.y : point.y
        currentShape.width = Math.abs(point.x - resizePoint.x)
        currentShape.height = Math.abs(point.y - resizePoint.y)
        shapeList.push(currentShape)
        canResize = false;
      }else if(canRemove) {
        removeCurrentShapeInShapeList()
        reDrawAllShape(ctx,canvas)
        canRemove = false
      }
      isInsideShape(canvas)
    }
  }
}

function listenDrawLine() {
  let ctx = canvas.getContext('2d')
  let type = 3
  canvas.onmousedown = function (e) {
    e.preventDefault()
    let mousedown = windowToCanvas(e, canvas)
    canvas.onmousemove = function (e) {
      if (canDrag) {  //拖拽逻辑
        let point = windowToCanvas(e, canvas) //当前点的坐标
        let p1_x = currentShape.shapeStart.x + point.x - mousedown.x;
        let p1_y = currentShape.shapeStart.y + point.y - mousedown.y;
        let p2_x = currentShape.shapeEnd.x + point.x - mousedown.x;
        let p2_y = currentShape.shapeEnd.y + point.y - mousedown.y;
        removeCurrentShapeInShapeList()
        reDrawAllShape(ctx, canvas)
        drawerLine(ctx, p1_x, p1_y, p2_x, p2_y)
        setTitle(p1_x, p1_y, ctx, '线段');
      } else if (canDraw) { //第一次画
        reDrawAllShape(ctx, canvas)
        e.preventDefault()
        let point = windowToCanvas(e, canvas)
        drawerLine(ctx, mousedown.x, mousedown.y, point.x, point.y)
      } else if (canResize) {
        e.preventDefault()
        let point = windowToCanvas(e, canvas)
        let x = point.x
        let y = point.y
        removeCurrentShapeInShapeList()
        reDrawAllShape(ctx, canvas)
        drawerLine(ctx, x, y, resizePoint.x, resizePoint.y)
        setTitle(x, y, ctx, '线段');
      }
    }
    canvas.onmouseup = function (e) {
      if (canDraw) {
        let result = setTitle(mousedown.x, mousedown.y, ctx, '线段');
        if (result) {
          let point = windowToCanvas(e, canvas)
          let shapeStart = mousedown
          let shapeEnd = point
          let uid = guid()
          shapeList.push({shapeStart, shapeEnd, type, uid})
        }
        canDraw = false
      } else if (canDrag) {
        let point = windowToCanvas(e, canvas)
        let result = setTitle(currentShape.shapeStart.x + point.x - mousedown.x, currentShape.shapeStart.y + point.y - mousedown.y, ctx, '线段');
        if (result) {
          if (!(point.x===mousedown.x && point.y===mousedown.y)){
            currentShape.shapeStart.x = currentShape.shapeStart.x + point.x - mousedown.x;
            currentShape.shapeStart.y = currentShape.shapeStart.y + point.y - mousedown.y;
            currentShape.shapeEnd.x = currentShape.shapeEnd.x + point.x - mousedown.x;
            currentShape.shapeEnd.y = currentShape.shapeEnd.y + point.y - mousedown.y;
            shapeList.push(currentShape)
            canDrag = false
          }
        }
      } else if (canResize) {
        let point = windowToCanvas(e, canvas)
        currentShape.shapeStart.x = point.x
        currentShape.shapeStart.y = point.y
        currentShape.shapeEnd.x = resizePoint.x
        currentShape.shapeEnd.y = resizePoint.y
        shapeList.push(currentShape)
        canResize = false
      }else if(canRemove) {
        removeCurrentShapeInShapeList()
        reDrawAllShape(ctx,canvas)
        canRemove = false
      }
      isInsideShape(canvas)
    }
  }
}

function listenDrawPoint() {
  let ctx = canvas.getContext('2d')
  let type = 4
  canvas.onmousedown = function (event) {
    canvas.onmousemove = function (e) {
      if (canDrag) {  //拖拽逻辑
        let point = windowToCanvas(e, canvas) //当前点的坐标
        removeCurrentShapeInShapeList()
        reDrawAllShape(ctx, canvas)
        drawerPoint(ctx, point.x, point.y)
        setTitle(point.x - 10, point.y - 10, ctx, '点');
      } else if (canDraw) {
      }
    }
    canvas.onmouseup = function (e) {
      let point = windowToCanvas(e, canvas)
      if (canDraw) {
        let result = setTitle(point.x - 10, point.y - 10, ctx, '点');
        if (result) {
          let uid = guid()
          let shapeStart = point
          drawerPoint(ctx, point.x, point.y)
          shapeList.push({shapeStart, type, uid})
        }
        canDraw = false
      } else if (canDrag) {
        let result = setTitle(point.x - 10, point.y - 10, ctx, '点');
        if (result) {
          currentShape.shapeStart.x = point.x;
          currentShape.shapeStart.y = point.y;
          shapeList.push(currentShape)
          drawerPoint(ctx, currentShape.shapeStart.x, currentShape.shapeStart.y)
          canDrag = false
        }
      }else if(canRemove) {
        removeCurrentShapeInShapeList()
        reDrawAllShape(ctx,canvas)
        canRemove = false
      }
      isInsideShape(canvas)
    }
  }
}

export function removeCurrentShapeInShapeList() {
  let index = shapeList.findIndex(item => {
    if (item.uid === currentShape.uid) {
      return true
    }
  });
  if (index !== -1) {
    shapeList.splice(index, 1)
  }
}

export function setRemove(){
  canRemove = true
}
export function removeAllShape(){
  shapeList = []
  reDrawAllShape()
}



// 绘制的方法及事件,根据当前选择的工具进行不同的方法绘制图形
export function drawer(type) {
  canDraw = true
  canvas.onclick = null
  canvas.onmousedown = null
  canvas.onmouseup = null
  canvas.onmousemove = null
  switch (type) {
    case 1: //矩形
      listenDrawRect(canvas)
      break;
    case 2:  //圆形
      /*canvas.onmousedown = function (e) {
        e.preventDefault()
        let mousedown = windowToCanvas(e, canvas)
        canvas.onmousemove = function (e) {
          ctx.clearRect(0, 0, canvas.width, canvas.height);
          ctx.drawImage(backImage,0,0,canvas.width,canvas.height)

          e.preventDefault()
          let point = windowToCanvas(e, canvas)
          let rx = (point.x - mousedown.x) / 2
          let ry = (point.y - mousedown.y) / 2
          let r = Math.sqrt(rx * rx + ry * ry)
          drawerCircle(ctx, rx + mousedown.x, ry + mousedown.y, r)
        }
        canvas.onmouseup = function (e) {
          let result = setTitle(mousedown.x, mousedown.y, ctx, '圆形');
          if (result) {
            let point = windowToCanvas(e, canvas)
            shapeList.push(point)
          }
          e.preventDefault()
          canvas.onmousemove = null
          canDraw = false
          for (let i = 0; i < shapeList.length; i++) {
            console.log(shapeList[i])
          }
        }
      }*/
      break;
    case 3:  //线段
      listenDrawLine(canvas)
      break;
    case 4:  //点
      listenDrawPoint(canvas)
      break;
    default:
      canDraw = false
      canResize = false
      break;
  }
}

export function initCanvas(ca,img){
  canvas = ca
  ctx = canvas.getContext("2d")
  backImage = img
  isInsideShape()
}

function guid() {
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
    let r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
    return v.toString(16);
  });
}

//是否可以被拖动
function isInsideShape() {
  canvas.onmousemove = function (e) {
    let bool = undefined;
    canDrag = false
    canResize = false
    canDraw = false
    canvas.style.cursor = 'default'
    cas:for (let i = 0; i < shapeList.length; i++) {
      switch (shapeList[i].type) {
        case 1:
          bool = isInsideRect(shapeList[i].shapeStart, shapeList[i].shapeEnd, windowToCanvas(e, canvas));
          bool === 1 ? canvas.style.cursor = 'move' : (bool === 2 ? canvas.style.cursor = 'se-resize' : 'default');
          if (bool === 1 ) {
            currentShape = shapeList[i]
            canDrag = !canRemove
          } else if (bool === 2) {
            currentShape = shapeList[i]
            canResize = true
          }
          listenDrawRect(canvas)
          break;
        case 3:
          bool = isInsideLine(shapeList[i].shapeStart, shapeList[i].shapeEnd, windowToCanvas(e, canvas));
          bool === 1 ? (canvas.style.cursor = 'move') : (bool === 2 ? canvas.style.cursor = 'se-resize' : canvas.style.cursor = 'default');
          if (bool === 1) {
            currentShape = shapeList[i]
            canDrag = !canRemove
            listenDrawLine(canvas)
          } else if (bool === 2) {
            currentShape = shapeList[i]
            canResize = true
            listenDrawLine(canvas)
          }
          break;
        case 4:
          bool = isInPoint(shapeList[i].shapeStart, windowToCanvas(e, canvas));
          bool === 1 ? canvas.style.cursor = 'move' : canvas.style.cursor = 'default';
          if (bool === 1 && !canRemove) {
            currentShape = shapeList[i]
            canDrag = true
            listenDrawPoint(canvas)
          }else if(canRemove) {
            currentShape = shapeList[i]
            listenDrawPoint(canvas)
          }
          break;
        default:
          bool = undefined
          canvas.style.cursor = 'default'
      }
      if (bool===1 || bool===2) {
        break cas;
      }
    }
  }
}


function reDrawAllShape() {
  ctx.beginPath()
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.drawImage(backImage, 0, 0, canvas.width, canvas.height)
  for (let i = 0; i < shapeList.length; i++) {
    let left = undefined;
    let top = undefined;
    if (shapeList[i].type === 1) {
      left = shapeList[i].shapeStart.x > shapeList[i].shapeEnd.x ? shapeList[i].shapeEnd.x : shapeList[i].shapeStart.x
      top = shapeList[i].shapeStart.y > shapeList[i].shapeEnd.y ? shapeList[i].shapeEnd.y : shapeList[i].shapeStart.y
      drawerRect(ctx, left, top, shapeList[i].width, shapeList[i].height)
      setTitle(left, top, ctx, '矩形');
    }
    if (shapeList[i].type === 2) { //圆形
    }
    if (shapeList[i].type === 3) { //线段
      drawerLine(ctx, shapeList[i].shapeStart.x, shapeList[i].shapeStart.y, shapeList[i].shapeEnd.x, shapeList[i].shapeEnd.y)
      top = shapeList[i].shapeStart.y
      left = shapeList[i].shapeStart.x
      setTitle(left, top, ctx, '线段');
    }
    if (shapeList[i].type === 4) { //线段
      drawerPoint(ctx, shapeList[i].shapeStart.x, shapeList[i].shapeStart.y)
      top = shapeList[i].shapeStart.y
      left = shapeList[i].shapeStart.x
      setTitle(left - 10, top - 10, ctx, '点');
    }
  }
}


 类似资料: