VUE canvas實現右鍵標籤添加及拖動

<template>
  <el-form ref="form" :rules="rules" :model="form" label-width="80px" > 
    <el-row>
      <el-col :span="10" :offset="1">
        <el-form-item label="證書" prop="certificateUrl">
          <el-upload
            :disabled="disabled"
            class="avatar-uploader"
            action="#"
            :show-file-list="false"
            :on-change="handleAvatarChange"
            :on-success="handleAvatarSuccess"
            :before-upload="beforeAvatarUpload"
            :with-credentials="true"
          >
            <img v-if="form.certificateUrl" :src="form.certificateUrl" class="avatar">
            <i v-else class="el-icon-plus avatar-uploader-icon"></i>
          </el-upload>
        </el-form-item>
      </el-col>
      <el-col :span="10" :offset="1">
        <el-form-item label="內容" prop="content">
          <el-input
            type="textarea"
            placeholder="請輸入內容"
            v-model="form.content" resize="none" :disabled="disabled" @blur="contentBlur()"></el-input>
        </el-form-item>
      </el-col>
    </el-row>
    <el-row >
      <el-col :span="21" :offset="1">
        <el-form-item label="" prop="content">
        <div ref="divCanvas" class="divCanvas">
          <canvas ref="myCanvas" style="background-color: #FFFFFF;" @contextmenu="canvaContextmenu"
                  @mousedown="canvasClick" @mouseup="stopDragging" @mouseout="stopDragging" @mousemove="dragCircle">
            您的瀏覽器不支持 HTML5 canvas 標籤。
          </canvas>
          <el-menu ref="canvaRightMenu" class="el-menu-hidden" @select="handleRightMenuSelect">
            <el-menu-item   v-for="item in comboxData" :index="item.code" v-if="item.disabled">
              <span slot="title">{{item.name}}</span>
            </el-menu-item>
            <el-menu-item index="clearAllTag">
              <span slot="title">清除所有標籤</span>
            </el-menu-item>
          </el-menu>
        </div>
        </el-form-item>
      </el-col>
    </el-row> 
  </el-form>
</template>

<script>
 
  export default {
    name: 'certificateEdit',
    data() {
      return {
        form: {
          certificateUrl: "",
          content: "姓名________________,________________至________________,在我校人力資源培訓中心進修。修完培養計劃規定的全部內容,表現良好,准予結業。",
          coordinates: []
        },
        rules: { 
          certificateUrl: [
            { required: true, message: '請上傳圖片', trigger: 'blur' }
          ],
        },
        comboxData: [{ code: 'name', name:'名稱', disabled: true },{ code: 'createTimeStart', name:'開始時間', disabled: true},{ code: 'createTimeEnd', name:'結束時間', disabled: true}], 
        inParams: {},
        disabled: false,
        canvasX: 0,
        canvasY: 0,
        isDragging:false,
        previousSelectedCircle:null
      }
    },
    created() { 
    },
    watch:{
      'form.certificateUrl':{
        handler(newValue,oldValue){
          if(newValue)
          {
            this.initCanvas();
          }
        },
        deep:true
      }
    },
    methods: {
      handleAvatarChange(file, fileList) {
           //圖片改變
           this.form.certificateUrl = URL.createObjectURL(file.raw); 
      },
      handleAvatarSuccess(res, file) {
        //上傳完成
        // if(res && res.code==200)
        //{
        //      this.form.certificateUrl = res.result.url;
        //}
      },
      beforeAvatarUpload(file) {
        const isJPG = file.type === 'image/jpeg' || file.type === 'image/png' || file.type === 'image/jpg';
        const isLt2M = file.size / 1024 / 1024 < 2;

        if (!isJPG) {
          this.$message.error('上傳頭像圖片只能是 JPG,png 格式!');
        }
        if (!isLt2M) {
          this.$message.error('上傳頭像圖片大小不能超過 2MB!');
        }
        return isJPG && isLt2M;
      }, 
      contentBlur() {
        //內容輸入完成初始化canvas
        this.initCanvas();
      },
      canvaContextmenu(event) {
       //canva右鍵列表
        var event = event || window.event;
        this.canvasX = event.offsetX+10;
        this.canvasY = event.offsetY+20;
        this.$refs.canvaRightMenu.$el.style.display = "block";
        this.$refs.canvaRightMenu.$el.style.position = "absolute";
        this.$refs.canvaRightMenu.$el.style.border = "solid 1px #dcdfe6";
        this.$refs.canvaRightMenu.$el.style.left =event.offsetX + "px";
        this.$refs.canvaRightMenu.$el.style.top = event.offsetY+ "px";
        event.preventDefault();
      },
      handleRightMenuSelect(index, indexPath) {
      //右鍵菜單選擇標籤
        if (index === 'clearAllTag') {
          this.form.coordinates = [];
        } else {
          let item = this.form.coordinates.find(t => t.labelCode === index);
          if (!item) {
            let value = this.comboxData.find(t => t.code === index);
            this.form.coordinates.push({
              labelCode: value.code,
              labelName: value.name,
              xaxis: this.canvasX,
              yaxis: this.canvasY
            });
            value.disabled = false;
          }
        } 
        this.$refs.canvaRightMenu.$el.style.display = "none";
        this.initCanvas();
      },
      initCanvas() {
		//初始化canvas
        console.log("initCanvas");
        let vue = this;
        if(!vue.form.certificateUrl)
        {
          return;
        }
        vue.$refs.divCanvas.style.display = "block";
        let canvas = vue.$refs.myCanvas;
        let ctx = canvas.getContext('2d');

        let img = new Image();
        img.src = vue.form.certificateUrl;
        //圖片加載完成添加圖片及文字
        img.onload = function() {
          canvas.width = img.width;
          canvas.height = img.height;
          let x = 100;//內容X座標
          let y = 400;//內容Y座標
          let lineWidth = canvas.width - 200;//內容長度
          let lineHeight = 30;//內容行高
          let lineSpace = 20;//內容行間距
          let font = "22px 微軟雅黑";
          //清除canvas
          ctx.clearRect(0, 0, canvas.width, canvas.hegiht);
          //添加背景
          ctx.drawImage(img, 0, 0);
          //添加內容
          let resultLines = vue.breakLinesForCanvas(ctx, vue.form.content, lineWidth, font);
          resultLines.forEach(function(line, index) {
            ctx.fillText(line, x, (lineHeight * index + y + lineSpace * index));
          });
          //添加標籤
          vue.form.coordinates.forEach(item => {
            ctx.fillText('{' + item.labelName + '}', item.xaxis, item.yaxis);
          })
        }
      },
      findBreakPoint(text, width, context) {
       //尋找切換斷點
        let min = 0;
        let max = text.length - 1;

        while (min <= max) {
          let middle = Math.floor((min + max) / 2);
          let middleWidth = context.measureText(text.substr(0, middle)).width;
          let oneCharWiderThanMiddleWidth = context.measureText(text.substr(0, middle + 1)).width;
          if (middleWidth <= width && oneCharWiderThanMiddleWidth > width) {
            return middle;
          }
          if (middleWidth < width) {
            min = middle + 1;
          } else {
            max = middle - 1;
          }
        }

        return -1;
      },
      breakLinesForCanvas(context, text, width, font) {
       //內容換行
        let result = [];
        let breakPoint = 0;
        if (font) {
          context.font = font;
        }
        while ((breakPoint = this.findBreakPoint(text, width, context)) !== -1) {
          result.push(text.substr(0, breakPoint));
          text = text.substr(breakPoint);
        }
        if (text) {
          result.push(text);
        }
        return result;
      },
      canvasClick(e) {

        let canvas = this.$refs.myCanvas;
        // 取得畫布上被單擊的點
        var clickX = e.offsetX - canvas.offsetLeft;
        var clickY = e.offsetY - canvas.offsetTop;
        this.$refs.canvaRightMenu.$el.style.display = "none";
        // 查找被單擊的
        let datas = this.form.coordinates;
        for (var i = 0; i < datas.length; i++) {
          var circle = datas[i];
          // 判斷這個點是否在中
          let xz= clickX >= circle.xaxis && clickX <= circle.xaxis + 100;
          let yz= clickY >= circle.yaxis-20 && clickY <= circle.yaxis + 5;
          if (xz && yz) {
            // 清除之前選擇的
            if (this.previousSelectedCircle != null) this.previousSelectedCircle = null;
            this.previousSelectedCircle = circle;
            // 使允許拖拽
            this.isDragging = true;
            //更新顯示
            this.initCanvas();
            //停止搜索
            return;
          }
        }
      },
      stopDragging() {
        this.isDragging = false;
      },
      dragCircle(e) {
        let canvas = this.$refs.myCanvas;
        // 判斷是否開始拖拽
        if (this.isDragging == true) {
          // 判斷拖拽對象是否存在
          if (this.previousSelectedCircle != null) {
            // 取得鼠標位置
            var x = e.offsetX - canvas.offsetLeft;
            var y = e.offsetY - canvas.offsetTop;

            // 將移動到鼠標位置
            this.previousSelectedCircle.xaxis = x;
            this.previousSelectedCircle.yaxis = y;
            // 更新畫布
            this.initCanvas();
          }
        }
      }
    }
  }
</script>

<style scoped>
  .avatar-uploader >>> .el-upload {
    border: 1px dashed #d9d9d9;
    border-radius: 6px;
    cursor: pointer;
    position: relative;
    overflow: hidden;
    border-radius: 0%;
    float: left;
  }
  .avatar-uploader >>> .el-upload:hover {
    border-color: #409EFF;
  }
  .avatar-uploader-icon {
    font-size: 28px;
    color: #8c939d;
    width: 178px;
    height: 178px;
    line-height: 178px;
    text-align: center;
  }
  .avatar {
    width: 178px;
    height: 178px;
    display: block;
  }
  .el-textarea >>> .el-textarea__inner{
    height: 178px;
  }
  .el-dialog__wrapper >>> .el-dialog{
    width: 1200px;
    height: 880px;
  }
  .divCanvas {
    width: 100%;
    min-height: 300px;
    max-height: 500px;
    overflow: auto;
    margin-bottom: 10px;
    border: solid 1px #f0f2f5;
    float: left;
    position: relative;
    display: none;
  }
  .el-tree-node {
    font-size: 14px;
    margin-right: 20px;
  }
  .el-menu-hidden {display: none;}
</style>

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章