canvas在图片上绘制标记,可拖拽、缩放,基于ZRender

如下图所示,在图片上做标记,如圆形、矩形等。

该demo实现画布在页面布局中缩放后居中显示,可拖拽、缩放、做标记说明。

项目下载地址:https://gitlab.com/zhangcw66/draw_mark
canvas在图片上绘制标记,可拖拽、缩放,基于ZRender_第1张图片
如粘贴以下代码,请安装zrender

<template>
<div class="draw-mark">
  <div id="canvas" style="height:710px;background-color:#F5F5F5"></div>
</div>
</template>

<script>
import zrender from 'zrender'
export default {
  name: 'DrawMark',
  data() {
    return {
      imgSrc: require('@/assets/timg.jpg'),
      markInfo: [[440, 260],[590, 360],[770, 370]],
      isShowPoint: true,

      zr: {},
      // zrender group实例
      group: {},
      imgWidth: 0, // 大图宽度
      imgHeight: 0, // 大图高度
      whRatio: 0, // 大图比例
      frameScale: 0, // 缩放比
      subSet: [], // 存放已经绘画完成的矩形框信息
      circle: {},
      scale: 1,
      scaleMax: 15, // 最大比例
      scaleMin: 0.5 // 最小比例
    }
  },
  mounted() {
    this.initCanvas()
  },
  methods: {
    // 初始化画布绘制图片
    initCanvas() {
      const vm = this
      // 获取放置画布的元素
      const container = document.getElementById('canvas')
      // 初始化zr实例 zrender容器
      vm.zr = zrender.init(container)
      vm.group = new zrender.Group({
        slient: true // 组内子孙元素是否响应鼠标事件
      })
      // 创建图片对象
      const imgs = new Image()
      // 图片的src等于大图的地址
      imgs.crossOrigin = 'Anonymous'
      imgs.setAttribute('crossOrigin', 'Anonymous')
      imgs.src = this.imgSrc
      imgs.onerror = _ => {
        this.$message.error('图片加载失败!')
        return false
      }
      // 图片加载成功后
      imgs.onload = () => {
        const canvasWidth = container.clientWidth
        const canvasHeight = container.clientHeight
        // 画布宽高比
        const canvasRatio = canvasWidth / canvasHeight
        // 图片的宽高比
        vm.whRatio = imgs.width / imgs.height
        const originalHeight = imgs.height
        // 图片相对于画布的尺寸进行调整
        // 如果图片的宽高比大于画布的宽高比
        if (vm.whRatio > canvasRatio) {
          imgs.width = canvasWidth
          imgs.height = imgs.width / vm.whRatio
        } else {
          imgs.height = canvasHeight
          imgs.width = imgs.height * vm.whRatio
        }
        // 对图片的宽高进行调整
        vm.imgWidth = imgs.width
        vm.imgHeight = imgs.height
        vm.frameScale = originalHeight / vm.imgHeight
        // 绘制图片
        const myImg = new zrender.Image({
          style: {
            image: this.imgSrc,
            x: 0,
            y: 0,
            width: vm.imgWidth,
            height: vm.imgHeight
          },
          z: 1,
          onmouseover: _ => {
            document.body.style.overflowY = 'hidden'
          },
          onmousewheel: _ => {
            document.body.style.overflowY = 'hidden'
          },
          onmouseout: item => {
            document.body.style.overflowY = ''
          }
        })
        // 将图片绘制至子节点中
        vm.group.add(myImg)
        // 调整图片在canvas中的位置
        if (vm.zr.getWidth() > vm.imgWidth) {
          vm.group.position[0] = (vm.zr.getWidth() - vm.imgWidth) / 2
        }
        if (vm.zr.getHeight() > vm.imgHeight) {
          vm.group.position[1] = (vm.zr.getHeight() - vm.imgHeight) / 2
        }
        // 将子节点添加至画布中
        vm.zr.add(vm.group)
        // 绘制mark
        this.markInfo.forEach((item, index) => {
          const opt = {
            stroke: '#f00',
            pointsSet: this.AssemblyData(item),
            id: index
          }
          vm.drawMark(opt)
        })
        // 调用鼠标的滚动事件
        vm.handleZoom()
        // 调用鼠标的拖拽事件
        vm.handleDrop()
      }
    },
    // 坐标框的转换
    AssemblyData(item) {
      let newArr = item
      const arr = newArr.map(item => {
        return Math.round(item /= this.frameScale)
      })
      return arr
    },
    // 画标记
    drawMark(opt) {
      const vm = this
      // vm.subSet = []
      vm.circle = new zrender.Circle({
        style: {
          stroke: opt.stroke || '#f00',
          fill: opt.stroke || '#f00',
          text: this.isShowPoint ? opt.id : null,
          textHeight: 6,
          textWidth: 6,
          fontWeight: 700,
          textPosition: [2, -11],
          textLineWidth: 2,
          textPadding: 1,
          fontSize: 12,
          textFill: opt.stroke || '#fff'

        },
        shape: {
          cx: opt.pointsSet[0],
          cy: opt.pointsSet[1],
          r: 3
        },
        hoverable: true,
        z: 2
      })
      vm.subSet.push(vm.circle)
      vm.group.add(vm.circle)
    },
    // 滚动缩放
    handleZoom() {
      const vm = this
      // 在画布子节点上监听鼠标滚轮事件  事件对象
      vm.group.on('mousewheel', ev => {
        // wheeldata 返回值说明:正值向上滚动,负值向下滚动 均为120的倍数 缩小20倍 结果可能是 +e.wheelDelta/20 或者是 -e.wheelData/20
        // 缩小20倍数 调整为10
        // 监听到鼠标再
        const e = (ev || event).wheelDelta / 60
        // scale原始的缩放比例是1 每次滚动在这个基础上加上或者减去滚轮缩放后的数据
        vm.scale += e
        // 调用缩放的函数 将缩放的比例传入
        vm.setScale(vm.scale)
      })
    },

    /** * 缩放 */
    setScale(scale, item) {
      if (scale > this.scaleMax) {
        scale = this.scaleMax
      } else if (scale < this.scaleMin) {
        scale = this.scaleMin
      }
      this.scale = scale
      this.group.attr({
        scale: [this.scale, this.scale],
        origin: [this.zr.getWidth() / 5, this.zr.getHeight() / 5]
      })
    },
    // 拖拽
    handleDrop() {
      const vm = this
      vm.zr.dragData = {
        drag: false,
        pos: [0, 0],
        group: null,
        target: null
      }
      vm.zr.on('mousedown', e => {
        // 画布拖拽的起始位置是 事件对象中的画布的坐标位置
        vm.zr.dragData.pos = [e.event.zrX, e.event.zrY]
        // 画布的拖拽目标
        vm.zr.dragData.target = e.target
        if (e.target === undefined) {
          // !!!
          vm.zr.dragData.drag = false
        } else if (e.target.parent && e.target.parent.type === 'group') {
          vm.zr.dragData.drag = true
          vm.zr.dragData.group = e.target.parent
        }
      })
      // 鼠标抬起事件 关闭拖拽  将拖拽的目标元素设置为空
      vm.zr.on('mouseup', e => {
        vm.zr.dragData.drag = false
        vm.zr.dragData.group = null
      })
      // 鼠标移出事件 关闭拖拽
      vm.zr.on('mouseout', e => {
        vm.zr.dragData.drag = false
        vm.zr.dragData.group = null
      })
      // 鼠标移动事件
      vm.zr.on('mousemove', e => {
        if (vm.zr.dragData.drag !== true) return
        const new_pos = [e.event.zrX, e.event.zrY]
        if (vm.zr.dragData.group != null) {
          var pos = [
            new_pos[0] - vm.zr.dragData.pos[0],
            new_pos[1] - vm.zr.dragData.pos[1]
          ]
          vm.zr.dragData.group.children().forEach(x => {
            x.position = [0, 0]
          })
          vm.zr.dragData.group.position[0] += pos[0]
          vm.zr.dragData.group.position[1] += pos[1]
          vm.zr.dragData.group.dirty()
        } else {
          // eslint-disable-next-line no-redeclare
          var pos = [
            new_pos[0] - vm.zr.dragData.pos[0],
            new_pos[1] - vm.zr.dragData.pos[1]
          ]
          vm.zr.storage.getDisplayList(true, true).forEach(x => {
            x.position[0] += pos[0]
            x.position[1] += pos[1]
            x.dirty()
          })
        }
        vm.zr.dragData.pos = [e.event.zrX, e.event.zrY]
      })
    }
  }
}
</script>
<style scoped>
.draw-mark {
  padding: 30px;
}
</style>

你可能感兴趣的:(canvas绘图,拖拽,缩放,图片标记)