vue+canvas绘制矩形框,input框可以输入内容

最近,做了一个使用canvas绘制矩形框,并生成对应的input进行输入,刚开始想偷懒,便找了很多类似的文章,可没有一个很完全满足需求的,只能自己搞了。记录一下。先看示例图,有点简陋啊,如果有需要的小伙伴可以拿去用

简单说下示例的功能:绘制一个矩形框,对应左上角生成一个input默认值填充,支持修改输入,右键矩形框可以删除对应矩形框

代码如下:

js 复制代码
<template>
    <div class="mycanvas-container">
      <div ref="canvasRef" style="position: relative;"><canvas ref="markCanvas" width="640" height="480"></canvas></div>
      <select name="mode" v-model="mode">
          <option value="rect">绘制矩形框</option>
      </select>
    </div>
</template>
  
  <script>
  export default {
    data() {
      return {
        rectArray: [
          { "x": 275.5375061035156, "y": 83, "w": 252, "h": 185, 'label_id': '测试' }
        ], // 矩形的数据存储
        history: [],
        dragging: false,
        mode: 'rect',
        mousedown: null,
        ctx: null,
        rectIndex: undefined // 当前点击的矩形的索引
      }
    },
    mounted() {
      // 禁用掉
      document.oncontextmenu = function(e) {
        e.preventDefault()
      }
      this.initCanvas(); // 画布初始化
    },
    beforeDestroy() {
    },
    methods: {
      /* 画布初始化 */
        initCanvas() {
            let that = this
            this.$nextTick(() => {
                // 初始化canvas宽高
                let cav = this.$refs.markCanvas;
                cav.width = '800';
                cav.height = '600';
                that.ctx = cav.getContext('2d');
                that.ctx.strokeStyle = 'red'
                cav.style.cursor = 'crosshair'
                cav.style.border="1px solid black"
                that.addHistoy(cav)
                // 数据回显绘制矩形
                this.rectArray.length > 0 && this.rectArray.forEach(item => {
                  that.rectIndex = 0
                  that.ctx.beginPath();
                  that.ctx.lineWidth=2
                  that.ctx.rect(item.x, item.y, item.w, item.h);
                  that.ctx.stroke();
                  that.ctx.restore();
                  if (item.label_id) {
                    that.addInput(item.x, item.y, item.label_id)
                  }
                  that.addHistoy(cav, 'rect')
                })
                // 鼠标事件
                cav.onmousedown = function(e) {
                  let sX = e.clientX
                  let sY = e.clientY
                  e.preventDefault();
                  that.mousedown = that.windowToCanvas(cav, e.clientX, e.clientY, e.which)
                  that.dragging = true
                  that.rectIndex = undefined
                  that.rectArray.forEach(function (value, index) {
                      if (value.w > 0 && value.h > 0 && sX > value.x && sX < value.x + value.w && sY > value.y && sY < value.y + value.h) {
                          // 鼠标在右下方向生成的矩形中
                          that.rectIndex = index
                      }
                  })
                  // 右键点击矩形框进行删除
                  if(e.button == 2 && that.rectIndex !== undefined) {
                    const selfChild = that.$refs.canvasRef.querySelectorAll('input')
                    that.rectArray.forEach((item, index) => {
                      that.$refs.canvasRef.removeChild(selfChild[index])
                    })
                    that.rectArray.splice(that.rectIndex, 1)
                    that.rectIndex = undefined
                    that.dragging = false
                    that.initCanvas()
                    return
                  }
                }
                cav.onmousemove = function(e) {
                    e.preventDefault();
                    if (that.dragging && that.mode === 'rect') { // 只有绘制矩形框时有效果
                        that.showLastHistory() // 每次绘制先清除上一次
                        that.updateRect(that.ctx, that.windowToCanvas(cav, e.clientX, e.clientY, e.which))
                    }
                    
                }
                cav.onmouseup = function(e) {
                  e.preventDefault();
                  if(that.dragging) {
                    that.mode === 'rect' && that.drawRect(cav, that.windowToCanvas(cav, e.clientX, e.clientY, e.which))
                  }
                  that.dragging = false
                }
                // 阻止页面的右击菜单栏
                cav.oncontextmenu = function(e) {
                    e.preventDefault()
                }
            })
        },
        // 坐标转化为canvas坐标
        windowToCanvas(cav, x, y, type) {
            //返回元素的大小以及位置
            let bbox = cav.getBoundingClientRect();
            // bbox 的宽度会加上 canvas 的 border 会影响精度
            return {
              x: x - bbox.left * (cav.width / bbox.width),
              y: y - bbox.top * (cav.height / bbox.height),
              type: type
            }
        },
        updateRect(ctx, point) {
            let w = Math.abs(point.x - this.mousedown.x)
            let h = Math.abs(point.y - this.mousedown.y)

            let x = point.x > this.mousedown.x ? this.mousedown.x : point.x
            let y = point.y > this.mousedown.y ? this.mousedown.y : point.y
            ctx.save();
            ctx.beginPath();
            ctx.lineWidth=2
            ctx.rect(x, y, w, h);
            ctx.stroke();
            ctx.restore();
        },
        drawRect(cav, point) {
          let w = Math.abs(point.x - this.mousedown.x)
          let h = Math.abs(point.y - this.mousedown.y)
          if(w > 0 && h > 0) {
            let left = point.x > this.mousedown.x ? this.mousedown.x : point.x
            let top = point.y > this.mousedown.y ? this.mousedown.y : point.y
            this.rectArray.push({ x: left, y: top, w: w, h: h, label_id: '表' + (this.rectArray.length + 1), index: this.rectArray.length })
            this.addHistoy(cav) // 保存上一次数据
            this.addInput(this.mousedown.x, this.mousedown.y)
          }
        },
        addInput(x, y, name) {
          let container = this.$refs.canvasRef
          let input = document.createElement('input');
          input.type = 'text';
          input.style.position = 'absolute';
          input.style.left = x + 'px';
          input.style.top = (y - 30) + 'px';
          input.dataset.aa = this.rectArray.length
          input.value = name ? name : '表' + this.rectArray.length

          input.onkeyup = this.handleEnter;

          container.appendChild(input);

          input.focus()
        },
        handleEnter(e) {
            this.rectArray[e.target.dataset.aa - 1]['label_id'] = e.target.value
            this.$forceUpdate()
        },
        addHistoy(cav, mode) {
          this.history.push({
              mode: mode || this.mode,
              data: this.ctx.getImageData(0, 0, cav.width, cav.height)
          })
        },
        showLastHistory() {
          this.ctx.putImageData(this.history[this.history.length - 1]['data'], 0, 0)
        }
    }
  }
  </script>
  <style lang="less" scoped>
// 设置绘图样式1
body {
    user-select: none;
}

.myMenu {
    position: fixed;
    top: 400px;
    left: 400px;
    width: 100px;
    padding: 8px 0;
    background-color: #fff;
    > * {
        width: 100%;
    }
}

#canvas > div {
    /* border: 2px solid green; */
    position: absolute;
    background-color: transparent;
}

#canvas > div > span {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    font-family: simsun;
    font-size: 9pt;
}

// 设置绘图样式2

.mycanvas-container {
    display: flex;
    justify-content: center;
    align-items: center;
    .left,
    .center,
    .right {
        width: 300px;
        // height: 520px;
        margin: 20px;

        p {
            text-align: center;
        }

        .myshow,
        .myedit {
            width: 300px;
            height: 500px;
            border: 1px solid #000;
            position: relative;
            .myedit-span {
                position: absolute;
                border: 1px dashed #fff;
                background: white;
                background-size: contain;
            }

            .mycanvas {
                border: 1px solid pink;
                position: absolute;
                top: 0;
                left: 0;
            }

            img {
                width: 100%;
            }
        }
    }

    .right {
        width: 150px;
        display: flex;
        justify-content: center;
        align-items: left;
        flex-direction: column;

        .mybutton {
            margin-top: 20px;
            display: block;
        }
    }
}
</style>
相关推荐
柳晓黑胡椒1 天前
cesiusm实现 多图例展示+点聚合(base64图标)
css3·canvas·base64·cesium·animation
余生H9 天前
即时可玩web小游戏(二):打砖块(支持移动端版) - 集成InsCode快来阅读并即时体验吧~
前端·javascript·inscode·canvas·h5游戏
普兰店拉马努金16 天前
【Canvas与图标】牛皮纸文件袋图标
canvas·图标·文件袋·牛皮纸
德育处主任18 天前
前端啊,拿Lottie炫个动画吧
前端·svg·canvas
GDAL19 天前
深入剖析Canvas的getBoundingClientRect:精准定位与交互事件实现
canvas
剑亦未配妥21 天前
使用js和canvas、html实现简单的俄罗斯方块小游戏
前端·javascript·canvas·1024程序员节
howard200522 天前
2.1 HTML5 - Canvas标签
html5·canvas
普兰店拉马努金1 个月前
【Canvas与标牌】立入禁止标牌
canvas·警示·标识·立入禁止
Anlige1 个月前
Javascript:使用canvas画二维码矩阵
javascript·canvas·qrcode
牛老师讲GIS1 个月前
分享一个从图片中提取色卡的实现
canvas·提取颜色