PC端和微信小程序端关于利用canvas在图片上画多边形区块的踩坑、解坑技巧分享

一、接到新需求:图片可放大、缩小、拖拽且能在图片上画区域块,将画出的区块数据传给后端。

产品原型图: 小众:站在前人的肩膀上造轮子或者直接应用到项目中--高效。

vue2-drawboard:

www.npmjs.com/package/vue...

demo效果图: 引入组件:

js 复制代码
<template>
  <div class="pic-draw-box">
    <div class="middle">
      <span class="button-tip" v-show="!openDetail && noShowDrag">再次点击第一个点时结束标记</span>
      <div class="button" v-show="!openDetail">
        <el-button @click="cancel" icon="el-icon-delete" size="mini">
            清空
        </el-button>
      </div>
      <el-tooltip effect="dark" content="移动" placement="bottom">
        <i class="el-icon-drag" @click="changeToMove"></i>
      </el-tooltip>
      <span class="icon-line" v-show="!openDetail && !isDrawing"></span>
      <el-tooltip effect="dark" content="多边形" placement="right" v-show="!openDetail && !isDrawing">
        <i class="el-icon-polygon" @click="changeTool('polygon')"></i>
      </el-tooltip>
      <drawboard
        :url="fileUrl+mapUrl"        
        :class="{'detail':openDetail,'drawing':isDrawing,'no-show-drag':noShowDrag}"
        @updateData="updateData"
        @drawEventDone="drawEventDone"
        :labelDataOrigin="labelDataOrigin"
        style="width: 100%; height: 100%;"
        ref="myDrawBoard"
        @mouseup="canvasMouseup"
      >
      </drawboard>
    </div>
  </div>
</template>

出现放大图片后分辨率下降的问题: 传入vue2-drawboard组件的图片url在canvas上渲染导致,客户提出问题=》如何解决,看源码。

canvas绘图不清晰:www.cnblogs.com/chrissong/p...

我们知道img标签缩放分辨率没影响。

将要缩放拖拽画区块的图放img标签,作为背景图片,传组件一张透明图作隔层图。

img图片作为透明图下的背景,跟透明图大小保持一致。

js 复制代码
<template>
  <div class="pic-draw-box">
    <div class="middle">
      <span class="button-tip" v-show="!openDetail && noShowDrag">再次点击第一个点时结束标记</span>
      <div class="button" v-show="!openDetail">
        <el-button @click="cancel" icon="el-icon-delete" size="mini">
            清空
        </el-button>
      </div>
      <el-tooltip effect="dark" content="移动" placement="bottom">
        <i class="el-icon-drag" @click="changeToMove"></i>
      </el-tooltip>
      <span class="icon-line" v-show="!openDetail && !isDrawing"></span>
      <el-tooltip effect="dark" content="多边形" placement="right" v-show="!openDetail && !isDrawing">
        <i class="el-icon-polygon" @click="changeTool('polygon')"></i>
      </el-tooltip>
      <img :src="fileUrl+mapUrl" ref="imgMapUrl" alt="" style="width: 100%; height: 100%;position: absolute;bottom: 1000px;">
      <drawboard
        :url="transparentBg"        
        :class="{'detail':openDetail,'drawing':isDrawing,'no-show-drag':noShowDrag}"
        @updateData="updateData"
        @drawEventDone="drawEventDone"
        :labelDataOrigin="labelDataOrigin"
        style="width: 100%; height: 100%;"
        ref="myDrawBoard"
        @mouseup="canvasMouseup"
      >
      </drawboard>
    </div>
  </div>
</template>
<script>
import { getPlantBaseMapById } from '@/api/link/soli/show';
let transparentBg = require('./transparent-bg.png');
export default {
  mounted(){
    let that = this;
    setTimeout(()=>{
      let board = this.$refs.myDrawBoard.image;
      let myDrawBoard = this.$refs.myDrawBoard;
      console.log('myDrawBoard', myDrawBoard);
      
      board.style.backgroundImage = `url(${encodeURI(that.fileUrl + that.mapUrl)})`
      board.style.backgroundSize = `${board.width}px ${board.height}px`;
      board.style.backgroundRepeat = `no-repeat`;
      board.style.backgroundOrigin = `center center`;
      board.style.backgroundPosition = `center center`;
    },500)
  },
  methods: {
    //编辑图片拿到画图的边框数据
    updateData(data,flag) {
      console.log('data',data);
      let hasValue = true;
      if(!data || (data && data.length ==0)) {
        hasValue = false;
      }

      //flag为father时是回显 不进入下面代码
      if(data[0] && flag != 'father'){
        //占位底图片
        let {imgMapUrl} = this.$refs;
        let numX = imgMapUrl.naturalWidth / 880;
        let numY = imgMapUrl.naturalHeight / 439;//880 x 439 px
        //父亲保存时取labelData值
        this.labelData = [{
          points:multiplyPropertyInArray(data, numX, numY),
          type:"polygon",
        }]
      }

      function multiplyPropertyInArray(obj, numX, numY) {
        return obj[0].points.map(function(element) {
          element.x *= numX;
          element.y *= numY;
          return element;
        });
      }

      //markFlag为2说明出现了红色提示请画出地块范围,当前有值时候让父亲取消当前红色提示
      if(this.markFlag == '2' && hasValue){
        this.$emit('closeMarkTip',true);
      }
    },
  }
};
</script>

二、uniapp微信小程序 uniapp.dcloud.net.cn/ svg、polygon、canvas

js 复制代码
    init() {
      // let context = uni.createCanvasContext("firstCanvas",this);
      const query = wx.createSelectorQuery();
      query.select("#firstCanvas").fields({ node: true, size: true }).exec((res) => {
          const canvas = res[0].node;
          if(canvas){
            const context = canvas.getContext("2d");
            const dpr = wx.getSystemInfoSync().pixelRatio;
            canvas.width = this.imgWidth;
            canvas.height = this.imgHeight;
            // context.scale(dpr, dpr);
            context.scale(1, 1);
            this.context = context;
            context.fillStyle = "rgba(255, 229, 128, 0.60)";
            //context..  开始canvas画
          }
        });
    },
    
        //只有点在了地块内部才开始切换高亮地块
        context.clearRect(0,0,this.imgWidth,this.imgHeight);
        this.plotData.forEach((v, k) => {
          if (this.isInPolygon([x, y], v.points)) {
            this.hoverIndex = k;
            console.log("开始点击,点到里面啦");
            context.fillStyle = "#ffe580";
            context.strokeStyle = "#ffe580";

            this.soliId = v.key;
            //通过地块id查数据
            this.getPlantStatusBySoil();
            this.selectDeviceInfoBySoilId();
          }else {
            context.fillStyle = "rgba(255, 229, 128, 0.60)";
            context.strokeStyle = "rgba(255, 229, 128, 0.60)";
          }

          context.beginPath();
            v.points.forEach((v2,k2)=>{
              if(k2 == 0) {
                context.moveTo(v2[0], v2[1]);
              }else {
                context.lineTo(v2[0], v2[1]);
              }
              //再回到第一个点
              if(k2 == v.points.length -1){
                context.lineTo(v.points[0][0], v.points[0][1]);
              }
            })
            context.closePath();
            context.stroke();
            context.fill();
        });
相关推荐
zqx_721 分钟前
随记 前端框架React的初步认识
前端·react.js·前端框架
惜.己38 分钟前
javaScript基础(8个案例+代码+效果图)
开发语言·前端·javascript·vscode·css3·html5
什么鬼昵称1 小时前
Pikachu-csrf-CSRF(get)
前端·csrf
长天一色1 小时前
【ECMAScript 从入门到进阶教程】第三部分:高级主题(高级函数与范式,元编程,正则表达式,性能优化)
服务器·开发语言·前端·javascript·性能优化·ecmascript
NiNg_1_2342 小时前
npm、yarn、pnpm之间的区别
前端·npm·node.js
秋殇与星河2 小时前
CSS总结
前端·css
BigYe程普2 小时前
我开发了一个出海全栈SaaS工具,还写了一套全栈开发教程
开发语言·前端·chrome·chatgpt·reactjs·个人开发
余生H2 小时前
前端的全栈混合之路Meteor篇:关于前后端分离及与各框架的对比
前端·javascript·node.js·全栈
程序员-珍2 小时前
使用openapi生成前端请求文件报错 ‘Token “Integer“ does not exist.‘
java·前端·spring boot·后端·restful·个人开发
axihaihai2 小时前
网站开发的发展(后端路由/前后端分离/前端路由)
前端