fabric.js 组件 图片上传裁剪并进行自定义区域标记

目录

0. 前言

1. 安装fabric与引入

2. fabric组件的使用

3. 属性相关设置

4. 初始化加载

4. 方法

5. 全代码


0. 前言

利用fabric组件,实现图片上传、图片”裁剪“、自定义的区域标记一系列操作

先放一张效果图吧👇

1. 安装fabric与引入

npm i fabric -S

我用的是全局引入方式,视情况调整 

import fabric from 'fabric';
Vue.use(fabric);

先放一个fabric.js API地址☞Api | Fabric中文文档 (gitee.io) 

2. fabric组件的使用

定义容器id=canvas,注意宽高

  <div class="maintenancePlanAdd"><div class="panel-body"><div class="demo"><canvas id="canvas" :width="width" :height="height" /><div class="draw-btn-group" v-show="!readstate"><div><el-button class="el-icon-upload" size="mini" type="primary"style="width: 80px !important;" @click="uploadImgConfirm">图片上传</el-button><el-button size="mini" type="danger" icon="el-icon-delete" @click="clean">清除</el-button></div><div><el-buttonv-show="bgImgSrc !== ''"v-for="(item, index) in alarmLevel":key="index":style="{background:colorGrounp[index]}"size="mini"@click="drawPolygon(index)">{{ item }}</el-button></div></div></div></div><img id="expImg" :src="bgImgSrc"><img id="img" :src="bgImgSrc"><input v-show="false" type="file" @change="uploadImgChange" id="imgInput" accept="image/*"><p class="tip-title" v-show="bgImgSrc === ''">上传的图片可以进行拖拽调整大小和方向</p></div>

3. 属性相关设置

累了,不想写了

 data() {return {bgImgFlag: true,bgImgSrc: '',imgFile: {},width: 800,height: 400,alarmLevel: ['一级风险', '二级风险', '三级风险', '四级风险'],colorGrounp: ['rgba(51, 164, 255, 1)', 'rgba(255, 200, 89, 1)', 'rgba(255, 160, 89, 1)', 'rgba(196,43, 1, 1)'],colorGrounpFill: ['rgba(51, 164, 255, 0.3)', 'rgba(255, 200, 89, 0.3)', 'rgba(255, 160, 89, 0.3)', 'rgba(196,43, 1, 0.3)'],canvas: {},mouseFrom: {},mouseTo: {},drawType: '', // 当前绘制图像ROIdrawWidth: 2, // 笔触宽度drawingObject: null, // 当前绘制对象moveCount: 1, // 绘制移动计数器doDrawing: false, // 绘制状态// polygon 相关参数polygonMode: false,pointArray: [],lineArray: [],savePointsGroup: [],activeShape: false,activeLine: '',line: {},deleteIconURL: require('@/assets/screen/icon-close.png') // 区域标记取消的x号图标};},

4. 初始化加载

this.canvas = new fabric.Canvas('canvas', {skipTargetFind: false, // 当为真时,跳过目标检测。目标检测将返回始终未定义。点击选择将无效selectable: false, // 为false时,不能选择对象进行修改selection: false // 是否可以多个对象为一组});this.canvas.selectionColor = 'rgba(0,0,0,0.05)';this.canvas.on('mouse:down', this.mousedown);this.canvas.on('mouse:move', this.mousemove);document.onkeydown = e => {// 键盘 delect删除所选元素if (e.keyCode == 46) {this.deleteObj();}// ctrl+z 删除最近添加的元素if (e.keyCode == 90 && e.ctrlKey) {this.canvas.remove(this.canvas.getObjects()[this.canvas.getObjects().length - 1]);}};this.$nextTick(() => {this.loadDraw(); // 回显之前标注过的内容,底图和区域标记内容});

4. 方法

methods: {// 保存当前画布为png图片save() {var canvas = document.getElementById('canvas');var imgData = canvas.toDataURL('png');imgData = imgData.replace('image/png', 'image/octet-stream');// 下载后的问题名,可自由指定var filename = 'drawingboard_' + (new Date()).getTime() + '.' + 'png';this.saveFile(imgData, filename);},saveFile(data, filename) {var save_link = document.createElement('a');save_link.href = data;save_link.download = filename;var event = document.createEvent('MouseEvents');event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);save_link.dispatchEvent(event);},// 提交绘制内容submitDraw() {const params = {pointInfo: [],imgInfo: '',img: ''};this.canvas.toJSON(['alarmLevel', 'isBgImg']).objects.forEach(item => {const element = {alarmLevel: item.alarmLevel,pointInfo: ''};if (item?.points) {element.pointInfo = item.points;params.pointInfo.push(element);}if (item?.isBgImg) {params.imgInfo = item;params.img = item.src;delete params.imgInfo.src;}});this.$emit('saveDraw', params);},// 清除画布clean() {this.$confirm('是否清除图片和标记?', '提示', {confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning'}).then(() => {this.canvas.clear();this.bgImgSrc = '';this.bgImgFlag = true;});},// 从已渲染的DOM元素加载图片至canvasloadExpImg() {const imgElement = document.getElementById('expImg');imgElement.onload = () => {// eslint-disable-next-line new-capnew fabric.Image.fromURL(imgElement.src,img => {img.scale(0.3);img.set({originX: 'center',originY: 'center'}, { crossOrigin: 'anonymous' });img.on('scaling', e => { // 拉伸事件const h = img.scaleY;const w = img.scaleX;if (h !== w || w == h) { // 判断缩放值相等或不相等后执行图片等比缩放if (e.e.movementY == -1 || e.e.movementY == 1) {img.scale(h);// 缩放} else {img.scale(w);}}});img.setCoords();img.centeredScaling = true;img.centerTransform = true;this.canvas.add(img);this.canvas.centerObject(img);this.canvas.renderAll();}, {selectable: true,hasControls: true,centeredScaling: false,zIndex: -99,isBgImg: true});};},// 上传确认uploadImgConfirm() {if (this.bgImgSrc !== '') {this.$confirm('是否重新上传标记?', '提示', {confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning'}).then(() => {this.canvas.clear();this.bgImgFlag = true;document.getElementById('imgInput').click();});} else {document.getElementById('imgInput').click();}},// 从文件加载图片至canvasuploadImgChange() {// 获取文件var eleImportInput = document.getElementById('imgInput');this.imgFile = eleImportInput.files[0];var imgTitle = '';// 从reader中获取选择文件的srcif (/\.(jpe?g|png|gif)$/i.test(this.imgFile.name)) {var reader = new FileReader();var _this = this;reader.addEventListener('load',function() {imgTitle = _this.imgFile.name;_this.bgImgSrc = this.result;},false);reader.readAsDataURL(this.imgFile);}this.loadExpImg();},// 鼠标按下时触发mousedown(e) {if (undefined === e) return;// 记录鼠标按下时的坐标var xy = e.pointer || this.transformMouse(e.e.offsetX, e.e.offsetY);this.mouseFrom.x = xy.x;this.mouseFrom.y = xy.y;this.doDrawing = true;this.canvas.skipTargetFind = false;try {// 此段为判断是否闭合多边形,点击红点时闭合多边形if (this.pointArray.length > 1) {// e.target.id == this.pointArray[0].id 表示点击了初始红点if (e.target && e.target.id == this.pointArray[0].id) {this.generatePolygon();return;}}// 未点击红点则继续作画if (this.polygonMode && this.pointArray.length < 4) {this.addPoint(e);} else if (this.polygonMode && this.pointArray.length > 0) {this.$message.warning('最多设置四个点');}} catch (error) {console.log(error);}},// 鼠标松开执行mouseup(e) {if (undefined === e) return;var xy = e.pointer || this.transformMouse(e.e.offsetX, e.e.offsetY);this.mouseTo.x = xy.x;this.mouseTo.y = xy.y;this.drawingObject = null;this.moveCount = 1;},// 鼠标移动过程中已经完成了绘制mousemove(e) {if (undefined === e) return;if (this.moveCount % 2 && !this.doDrawing) {// 减少绘制频率return;}this.moveCount++;var xy = e.pointer || this.transformMouse(e.e.offsetX, e.e.offsetY);this.mouseTo.x = xy.x;this.mouseTo.y = xy.y;if (this.activeLine && this.activeLine.class == 'line') {var pointer = this.canvas.getPointer(e.e);this.activeLine.set({x2: pointer.x,y2: pointer.y});var points = this.activeShape.get('points');points[this.pointArray.length] = {x: pointer.x,y: pointer.y,zIndex: 1};this.activeShape.set({points: points});this.canvas.renderAll();}this.canvas.renderAll();},deleteObj() {this.canvas.getActiveObjects().map(item => {this.canvas.remove(item);});},transformMouse(mouseX, mouseY) {return {x: mouseX / 1,y: mouseY / 1};},// 绘制多边形开始drawPolygon(data) {if (this.bgImgFlag) {this.canvas.getObjects().forEach(obj => {if (obj.isBgImg) {obj.hasControls = false;obj.selectable = false;obj.evented = false;}});this.bgImgFlag = false;this.canvas.renderAll();}this.drawType = data;this.polygonMode = true;this.pointArray = []; // 顶点集合this.lineArray = []; // 线集合this.canvas.isDrawingMode = false;},addPoint(e) {var random = Math.floor(Math.random() * 10000);var id = new Date().getTime() + random;var circle = new fabric.Circle({radius: 5,fill: '#ffffff',stroke: '#333333',strokeWidth: 0.5,left: (e.pointer.x || e.e.layerX) / this.canvas.getZoom(),top: (e.pointer.y || e.e.layerY) / this.canvas.getZoom(),selectable: false,hasBorders: false,hasControls: false,originX: 'center',originY: 'center',id: id,objectCaching: false});if (this.pointArray.length == 0) {circle.set({fill: this.colorGrounp[this.drawType]});}var points = [(e.pointer.x || e.e.layerX) / this.canvas.getZoom(),(e.pointer.y || e.e.layerY) / this.canvas.getZoom(),(e.pointer.x || e.e.layerX) / this.canvas.getZoom(),(e.pointer.y || e.e.layerY) / this.canvas.getZoom()];this.line = new fabric.Line(points, {strokeWidth: 2,fill: this.colorGrounp[this.drawType],stroke: this.colorGrounp[this.drawType],class: 'line',originX: 'center',originY: 'center',selectable: false,hasBorders: false,hasControls: false,evented: false,objectCaching: false});if (this.activeShape) {var pos = this.canvas.getPointer(e.e);var points = this.activeShape.get('points');points.push({x: pos.x,y: pos.y});var polygon = new fabric.Polygon(points, {stroke: '#333333',strokeWidth: 1,fill: this.colorGrounpFill[this.drawType],opacity: 0.3,selectable: false,hasBorders: false,hasControls: false,evented: false,objectCaching: false});this.canvas.remove(this.activeShape);this.canvas.add(polygon);this.activeShape = polygon;this.canvas.renderAll();} else {var polyPoint = [{x: (e.pointer.x || e.e.layerX) / this.canvas.getZoom(),y: (e.pointer.y || e.e.layerY) / this.canvas.getZoom()}];var polygon = new fabric.Polygon(polyPoint, {stroke: '#333333',strokeWidth: 1,fill: '#cccccc',opacity: 0.3,selectable: false,hasBorders: false,hasControls: false,evented: false,objectCaching: false});this.activeShape = polygon;this.canvas.add(polygon);}this.activeLine = this.line;this.pointArray.push(circle);this.lineArray.push(this.line);this.canvas.add(this.line);this.canvas.add(circle);},generatePolygon() {var points = [];this.pointArray.map((point, index) => {points.push({x: point.left,y: point.top});this.canvas.remove(point);});this.lineArray.map((line, index) => {this.canvas.remove(line);});this.canvas.remove(this.activeShape).remove(this.activeLine);var polygon = new fabric.Polygon(points, {stroke: this.colorGrounp[this.drawType],strokeWidth: this.drawWidth,fill: this.colorGrounpFill[this.drawType],opacity: 1,selectable: false,hasBorders: false,hasControls: false,alarmLevel: this.drawType});let max = 0;for (let i = 1; i < this.canvas._objects.length; i++) {if (this.canvas._objects[i].index > max) max = this.canvas._objects[i].index;}polygon.index = max + 1;this.canvas.add(polygon);this.activeLine = null;this.activeShape = null;this.polygonMode = false;this.doDrawing = false;// this.drawType = null;fabric.Image.fromURL(this.deleteIconURL, this.deletecallback);},// 从画布中删除当前选中的对象deleteObject() {const activeObject = this.canvas.getActiveObject();if (activeObject) {this.canvas._objects.forEach(item => {if (item.index === activeObject.index) {this.canvas.remove(item);}});this.canvas.remove(activeObject);this.canvas.renderAll();}},// 渲染删除按钮async deletecallback(img) {const self = this;let max = 0;for (let i = 1; i < this.canvas._objects.length; i++) {if (this.canvas._objects[i].index > max) max = this.canvas._objects[i].index;}img.index = max;const oImg = await img.set({left: this.pointArray[0].left - 20,top: this.pointArray[0].top - 20,width: 40,height: 40,angle: 0}).scale(0.8);this.canvas.add(oImg).renderAll();this.canvas.setActiveObject(oImg);oImg.on('mousedown', function() {self.deleteObject();});},// 回显详情信息loadDraw() {const self = this;if (self.drawinfo.id === '') return;const pointGroup = JSON.parse(self.drawinfo.pointInfo);const imgInfo = JSON.parse(self.drawinfo.imgInfo);self.bgImgSrc = self.drawinfo.img;imgInfo.src = self.drawinfo.img;// 1、加载底图fabric.util.enlivenObjects([imgInfo], objects => {objects.forEach(o => {o.selectable = false;o.hasControls = false;o.centeredScaling = false;this.canvas.add(o);});// 2、处理多边形绘制回显操作pointGroup.forEach(async (item, index) => {if (item.pointInfo !== '') {const polygon = new fabric.Polygon(item.pointInfo, {stroke: self.colorGrounp[item.alarmLevel],strokeWidth: self.drawWidth,fill: self.colorGrounpFill[item.alarmLevel],opacity: 1,selectable: false,hasBorders: false,hasControls: false,alarmLevel: item.alarmLevel});polygon.index = index;self.canvas.add(polygon);self.activeLine = null;self.activeShape = null;self.polygonMode = false;self.doDrawing = false;if (!self.readstate) {fabric.Image.fromURL(self.deleteIconURL, async img => {const _self = this;img.index = index;const oImg = await img.set({left: item.pointInfo[0].x - 20,top: item.pointInfo[0].y - 20,width: 40,height: 40,angle: 0}).scale(0.8);this.canvas.add(oImg);oImg.on('mousedown', function() {_self.deleteObject();});});}}});});self.canvas.renderAll();}}

5. 全代码

累了累了,开始摆烂,以后再调整,直接放全代码吧

<template><div class="maintenancePlanAdd"><div class="panel-body"><div class="demo"><canvas id="canvas" :width="width" :height="height" /><div class="draw-btn-group" v-show="!readstate"><div><el-button class="el-icon-upload" size="mini" type="primary"style="width: 80px !important;" @click="uploadImgConfirm">图片上传</el-button><el-button size="mini" type="danger" icon="el-icon-delete" @click="clean">清除</el-button></div><div><el-buttonv-show="bgImgSrc !== ''"v-for="(item, index) in alarmLevel":key="index":style="{background:colorGrounp[index]}"size="mini"@click="drawPolygon(index)">{{ item }}</el-button></div></div></div></div><img id="expImg" :src="bgImgSrc"><img id="img" :src="bgImgSrc"><input v-show="false" type="file" @change="uploadImgChange" id="imgInput" accept="image/*"><p class="tip-title" v-show="bgImgSrc === ''">上传的图片可以进行拖拽调整大小和方向</p></div>
</template>
<script>
export default {props: ['readstate', 'drawinfo'],data() {return {bgImgFlag: true,bgImgSrc: '',imgFile: {},width: 800,height: 400,alarmLevel: ['一级风险', '二级风险', '三级风险', '四级风险'],colorGrounp: ['rgba(51, 164, 255, 1)', 'rgba(255, 200, 89, 1)', 'rgba(255, 160, 89, 1)', 'rgba(196,43, 1, 1)'],colorGrounpFill: ['rgba(51, 164, 255, 0.3)', 'rgba(255, 200, 89, 0.3)', 'rgba(255, 160, 89, 0.3)', 'rgba(196,43, 1, 0.3)'],canvas: {},mouseFrom: {},mouseTo: {},drawType: '', // 当前绘制图像ROIdrawWidth: 2, // 笔触宽度drawingObject: null, // 当前绘制对象moveCount: 1, // 绘制移动计数器doDrawing: false, // 绘制状态// polygon 相关参数polygonMode: false,pointArray: [],lineArray: [],savePointsGroup: [],activeShape: false,activeLine: '',line: {},deleteIconURL: require('@/assets/screen/icon-close.png')};},watch: {drawinfo: {handler(n) {this.drawinfo = n;this.$nextTick(() => {this.loadDraw();});},deep: true},readstate: {handler(n) {this.readstate = n;},deep: true},width() {this.canvas.setWidth(this.width);},height() {this.canvas.setHeight(this.height);}},mounted() {this.canvas = new fabric.Canvas('canvas', {skipTargetFind: false, // 当为真时,跳过目标检测。目标检测将返回始终未定义。点击选择将无效selectable: false, // 为false时,不能选择对象进行修改selection: false // 是否可以多个对象为一组});this.canvas.selectionColor = 'rgba(0,0,0,0.05)';this.canvas.on('mouse:down', this.mousedown);this.canvas.on('mouse:move', this.mousemove);document.onkeydown = e => {// 键盘 delect删除所选元素if (e.keyCode == 46) {this.deleteObj();}// ctrl+z 删除最近添加的元素if (e.keyCode == 90 && e.ctrlKey) {this.canvas.remove(this.canvas.getObjects()[this.canvas.getObjects().length - 1]);}};this.$nextTick(() => {this.loadDraw();});},methods: {// 保存当前画布为png图片save() {var canvas = document.getElementById('canvas');var imgData = canvas.toDataURL('png');imgData = imgData.replace('image/png', 'image/octet-stream');// 下载后的问题名,可自由指定var filename = 'drawingboard_' + (new Date()).getTime() + '.' + 'png';this.saveFile(imgData, filename);},saveFile(data, filename) {var save_link = document.createElement('a');save_link.href = data;save_link.download = filename;var event = document.createEvent('MouseEvents');event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);save_link.dispatchEvent(event);},// 提交绘制内容submitDraw() {const params = {pointInfo: [],imgInfo: '',img: ''};this.canvas.toJSON(['alarmLevel', 'isBgImg']).objects.forEach(item => {const element = {alarmLevel: item.alarmLevel,pointInfo: ''};if (item?.points) {element.pointInfo = item.points;params.pointInfo.push(element);}if (item?.isBgImg) {params.imgInfo = item;params.img = item.src;delete params.imgInfo.src;}});this.$emit('saveDraw', params);},// 清除画布clean() {this.$confirm('是否清除图片和标记?', '提示', {confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning'}).then(() => {this.canvas.clear();this.bgImgSrc = '';this.bgImgFlag = true;});},// 从已渲染的DOM元素加载图片至canvasloadExpImg() {const imgElement = document.getElementById('expImg');imgElement.onload = () => {// eslint-disable-next-line new-capnew fabric.Image.fromURL(imgElement.src,img => {img.scale(0.3);img.set({originX: 'center',originY: 'center'}, { crossOrigin: 'anonymous' });img.on('scaling', e => { // 拉伸事件const h = img.scaleY;const w = img.scaleX;if (h !== w || w == h) { // 判断缩放值相等或不相等后执行图片等比缩放if (e.e.movementY == -1 || e.e.movementY == 1) {img.scale(h);// 缩放} else {img.scale(w);}}});img.setCoords();img.centeredScaling = true;img.centerTransform = true;this.canvas.add(img);this.canvas.centerObject(img);this.canvas.renderAll();}, {selectable: true,hasControls: true,centeredScaling: false,zIndex: -99,isBgImg: true});};},// 上传确认uploadImgConfirm() {if (this.bgImgSrc !== '') {this.$confirm('是否重新上传标记?', '提示', {confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning'}).then(() => {this.canvas.clear();this.bgImgFlag = true;document.getElementById('imgInput').click();});} else {document.getElementById('imgInput').click();}},// 从文件加载图片至canvasuploadImgChange() {// 获取文件var eleImportInput = document.getElementById('imgInput');this.imgFile = eleImportInput.files[0];var imgTitle = '';// 从reader中获取选择文件的srcif (/\.(jpe?g|png|gif)$/i.test(this.imgFile.name)) {var reader = new FileReader();var _this = this;reader.addEventListener('load',function() {imgTitle = _this.imgFile.name;_this.bgImgSrc = this.result;},false);reader.readAsDataURL(this.imgFile);}this.loadExpImg();},// 鼠标按下时触发mousedown(e) {if (undefined === e) return;// 记录鼠标按下时的坐标var xy = e.pointer || this.transformMouse(e.e.offsetX, e.e.offsetY);this.mouseFrom.x = xy.x;this.mouseFrom.y = xy.y;this.doDrawing = true;this.canvas.skipTargetFind = false;try {// 此段为判断是否闭合多边形,点击红点时闭合多边形if (this.pointArray.length > 1) {// e.target.id == this.pointArray[0].id 表示点击了初始红点if (e.target && e.target.id == this.pointArray[0].id) {this.generatePolygon();return;}}// 未点击红点则继续作画if (this.polygonMode && this.pointArray.length < 4) {this.addPoint(e);} else if (this.polygonMode && this.pointArray.length > 0) {this.$message.warning('最多设置四个点');}} catch (error) {console.log(error);}},// 鼠标松开执行mouseup(e) {if (undefined === e) return;var xy = e.pointer || this.transformMouse(e.e.offsetX, e.e.offsetY);this.mouseTo.x = xy.x;this.mouseTo.y = xy.y;this.drawingObject = null;this.moveCount = 1;},// 鼠标移动过程中已经完成了绘制mousemove(e) {if (undefined === e) return;if (this.moveCount % 2 && !this.doDrawing) {// 减少绘制频率return;}this.moveCount++;var xy = e.pointer || this.transformMouse(e.e.offsetX, e.e.offsetY);this.mouseTo.x = xy.x;this.mouseTo.y = xy.y;if (this.activeLine && this.activeLine.class == 'line') {var pointer = this.canvas.getPointer(e.e);this.activeLine.set({x2: pointer.x,y2: pointer.y});var points = this.activeShape.get('points');points[this.pointArray.length] = {x: pointer.x,y: pointer.y,zIndex: 1};this.activeShape.set({points: points});this.canvas.renderAll();}this.canvas.renderAll();},deleteObj() {this.canvas.getActiveObjects().map(item => {this.canvas.remove(item);});},transformMouse(mouseX, mouseY) {return {x: mouseX / 1,y: mouseY / 1};},// 绘制多边形开始drawPolygon(data) {if (this.bgImgFlag) {this.canvas.getObjects().forEach(obj => {if (obj.isBgImg) {obj.hasControls = false;obj.selectable = false;obj.evented = false;}});this.bgImgFlag = false;this.canvas.renderAll();}this.drawType = data;this.polygonMode = true;this.pointArray = []; // 顶点集合this.lineArray = []; // 线集合this.canvas.isDrawingMode = false;},addPoint(e) {var random = Math.floor(Math.random() * 10000);var id = new Date().getTime() + random;var circle = new fabric.Circle({radius: 5,fill: '#ffffff',stroke: '#333333',strokeWidth: 0.5,left: (e.pointer.x || e.e.layerX) / this.canvas.getZoom(),top: (e.pointer.y || e.e.layerY) / this.canvas.getZoom(),selectable: false,hasBorders: false,hasControls: false,originX: 'center',originY: 'center',id: id,objectCaching: false});if (this.pointArray.length == 0) {circle.set({fill: this.colorGrounp[this.drawType]});}var points = [(e.pointer.x || e.e.layerX) / this.canvas.getZoom(),(e.pointer.y || e.e.layerY) / this.canvas.getZoom(),(e.pointer.x || e.e.layerX) / this.canvas.getZoom(),(e.pointer.y || e.e.layerY) / this.canvas.getZoom()];this.line = new fabric.Line(points, {strokeWidth: 2,fill: this.colorGrounp[this.drawType],stroke: this.colorGrounp[this.drawType],class: 'line',originX: 'center',originY: 'center',selectable: false,hasBorders: false,hasControls: false,evented: false,objectCaching: false});if (this.activeShape) {var pos = this.canvas.getPointer(e.e);var points = this.activeShape.get('points');points.push({x: pos.x,y: pos.y});var polygon = new fabric.Polygon(points, {stroke: '#333333',strokeWidth: 1,fill: this.colorGrounpFill[this.drawType],opacity: 0.3,selectable: false,hasBorders: false,hasControls: false,evented: false,objectCaching: false});this.canvas.remove(this.activeShape);this.canvas.add(polygon);this.activeShape = polygon;this.canvas.renderAll();} else {var polyPoint = [{x: (e.pointer.x || e.e.layerX) / this.canvas.getZoom(),y: (e.pointer.y || e.e.layerY) / this.canvas.getZoom()}];var polygon = new fabric.Polygon(polyPoint, {stroke: '#333333',strokeWidth: 1,fill: '#cccccc',opacity: 0.3,selectable: false,hasBorders: false,hasControls: false,evented: false,objectCaching: false});this.activeShape = polygon;this.canvas.add(polygon);}this.activeLine = this.line;this.pointArray.push(circle);this.lineArray.push(this.line);this.canvas.add(this.line);this.canvas.add(circle);},generatePolygon() {var points = [];this.pointArray.map((point, index) => {points.push({x: point.left,y: point.top});this.canvas.remove(point);});this.lineArray.map((line, index) => {this.canvas.remove(line);});this.canvas.remove(this.activeShape).remove(this.activeLine);var polygon = new fabric.Polygon(points, {stroke: this.colorGrounp[this.drawType],strokeWidth: this.drawWidth,fill: this.colorGrounpFill[this.drawType],opacity: 1,selectable: false,hasBorders: false,hasControls: false,alarmLevel: this.drawType});let max = 0;for (let i = 1; i < this.canvas._objects.length; i++) {if (this.canvas._objects[i].index > max) max = this.canvas._objects[i].index;}polygon.index = max + 1;this.canvas.add(polygon);this.activeLine = null;this.activeShape = null;this.polygonMode = false;this.doDrawing = false;// this.drawType = null;fabric.Image.fromURL(this.deleteIconURL, this.deletecallback);},// 从画布中删除当前选中的对象deleteObject() {const activeObject = this.canvas.getActiveObject();if (activeObject) {this.canvas._objects.forEach(item => {if (item.index === activeObject.index) {this.canvas.remove(item);}});this.canvas.remove(activeObject);this.canvas.renderAll();}},// 渲染删除按钮async deletecallback(img) {const self = this;let max = 0;for (let i = 1; i < this.canvas._objects.length; i++) {if (this.canvas._objects[i].index > max) max = this.canvas._objects[i].index;}img.index = max;const oImg = await img.set({left: this.pointArray[0].left - 20,top: this.pointArray[0].top - 20,width: 40,height: 40,angle: 0}).scale(0.8);this.canvas.add(oImg).renderAll();this.canvas.setActiveObject(oImg);oImg.on('mousedown', function() {self.deleteObject();});},// 回显详情信息loadDraw() {const self = this;if (self.drawinfo.id === '') return;const pointGroup = JSON.parse(self.drawinfo.pointInfo);const imgInfo = JSON.parse(self.drawinfo.imgInfo);self.bgImgSrc = self.drawinfo.img;imgInfo.src = self.drawinfo.img;// 1、加载底图fabric.util.enlivenObjects([imgInfo], objects => {objects.forEach(o => {o.selectable = false;o.hasControls = false;o.centeredScaling = false;this.canvas.add(o);});// 2、处理多边形绘制回显操作pointGroup.forEach(async (item, index) => {if (item.pointInfo !== '') {const polygon = new fabric.Polygon(item.pointInfo, {stroke: self.colorGrounp[item.alarmLevel],strokeWidth: self.drawWidth,fill: self.colorGrounpFill[item.alarmLevel],opacity: 1,selectable: false,hasBorders: false,hasControls: false,alarmLevel: item.alarmLevel});polygon.index = index;self.canvas.add(polygon);self.activeLine = null;self.activeShape = null;self.polygonMode = false;self.doDrawing = false;if (!self.readstate) {fabric.Image.fromURL(self.deleteIconURL, async img => {const _self = this;img.index = index;const oImg = await img.set({left: item.pointInfo[0].x - 20,top: item.pointInfo[0].y - 20,width: 40,height: 40,angle: 0}).scale(0.8);this.canvas.add(oImg);oImg.on('mousedown', function() {_self.deleteObject();});});}}});});self.canvas.renderAll();}}
};
</script><style lang="scss" scoped>
.el-container {flex-direction: column;
}img {display: none;
}.demo {display: flex;flex-direction: column;align-items: center;
}canvas {border: 1px dashed #2695F9;
}.draw-btn-group {width: 100%;margin-top: 10px;display: flex;align-items: center;justify-content: space-between;.el-button {color: #ffffff;}
}.tip-title {top: 40%;left: 33%;position: absolute;font-size: 16px;color: #C2C7CC;margin: 0;
}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/418671.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

x-cmd pkg | mermaid - 流程图、时序图等图表绘制工具

简介 mermaid-cli 是由 Mermaid 官方提供的命令行工具&#xff0c;用于将 Mermaid 语法的文本转换为 SVG / PNG / PDF。 Mermaid 是一个基于 JavaScript 的图表绘制工具&#xff0c;它使用简单的文本描述语法&#xff0c;就可以绘制出流程图、时序图、甘特图等多种图表。 首次…

C#调用C动态链接库

前言 已经没写过博客好久了&#xff0c;上一篇还是1年半前写的LTE Gold序列学习笔记&#xff0c;因为工作是做通信协议的&#xff0c;然后因为大学时没好好学习专业课&#xff0c;现在理论还不扎实&#xff0c;不敢瞎写&#xff1b; 因为工作原因&#xff0c;经常需要分析一些字…

4496 蓝桥杯 求函数零点 简单

4496 蓝桥杯 求函数零点 简单 //C风格解法1&#xff0c;通过率100% #include <bits/stdc.h> // int a, b; 一定会自动初始化为 0int main(){int a 2, b 3; // 定义a&#xff0c;b&#xff0c;不会自动初始化&#xff0c;最好自己定义时初始化// windows环境下a值固定&…

Redis原理篇(SkipList)

一.概述 本质是双端链表&#xff0c;只不过在正向遍历时可以不一个一个遍历&#xff0c;而是可以跳着遍历。 怎么实现的呢&#xff0c;下面是SkipList源码 二.源码 1. zskiplist 意义&#xff1a;跳表 zskiplist里面有头指针和尾指针&#xff0c;节点数量&#xff0c;最大…

acwing讲解篇之93. 递归实现组合型枚举

文章目录 题目描述题解思路题解代码 题目描述 题解思路 本题相当于二叉树的深度优先遍历&#xff0c;树的第i层表示第i个数选或不选&#xff0c;当选择了m次左节点后退出 我们记录当前递归的深度deep 然后用state进行状态压缩&#xff0c;state第i位是1表示选第i个数&#xff…

Maven 依赖传递和冲突、继承和聚合

一、依赖传递和冲突 1.1 Maven 依赖传递特性 1.1.1 概念 假如有三个 Maven 项目 A、B 和 C&#xff0c;其中项目 A 依赖 B&#xff0c;项目 B 依赖 C。那么我们可以说 A 依赖 C。也就是说&#xff0c;依赖的关系为&#xff1a;A—>B—>C&#xff0c; 那么我们执行项目 …

ubuntu设置每天定时关机

ubuntu设置每天定时关机 终端输入命令&#xff1a; sudo crontab -e输入密码&#xff0c;回车。 我这里使用nano作为编辑器&#xff0c;你可以选择vim。 在末尾输入以下命令&#xff1a; 59 23 * * * sudo -u root shutdown now设置&#xff1a;每天23:59分&#xff0c;电脑…

基于springboot+vue养老院管理系统

摘要 这是一个基于Spring Boot 和 Vue.js 的养老院管理系统的项目。该系统旨在提供一套全面的解决方案&#xff0c;以简化养老院的日常管理任务&#xff0c;包括居民信息管理、员工调度、医疗服务追踪、财务管理等。通过结合后端的Spring Boot框架和前端的Vue.js框架&#xff0…

Oracle 经典练习题 50 题

文章目录 一 CreateTable二 练习题1 查询"01"课程比"02"课程成绩高的学生的信息及课程分数2 查询"01"课程比"02"课程成绩低的学生的信息及课程分数3 查询平均成绩大于等于60分的同学的学生编号和学生姓名和平均成绩4 查询平均成绩小于…

VS Code + Python + Selenium 自动化测试基础-01

VS Code Python Selenium 自动化测试基础-01 让我们来讲一个故事为什么要写自动化开发前的准备工作牛刀小试开常用的web DriverAPI-定位元素id定位&#xff1a;find_element_by_id()name 定位&#xff1a;find_element_by_name()class 定位&#xff1a;find_element_by_class…

redis远程连接不上解决办法

问题描述&#xff1a; redis远程服务端运行在192.168.3.90计算机上&#xff0c;客户端计算机&#xff08;ip:192.168.3.110&#xff09;通过redsi-cli.exe客户端工具连接时&#xff0c;没有反应&#xff0c;连接不上。 如图所示&#xff1a; 解决步骤&#xff1a; 步骤一&…

Civil 3D安装教程,免费使用,带安装包和工具,一分钟轻松搞的安装

前言 Civil 3D是一款面向基础设施行业的建筑信息模型&#xff08;BIM&#xff09;解决方案。它为基础设施行业的各类技术人员提供了强大的设计、分析以及文档编制功能&#xff0c;广泛适用于勘察测绘、岩土工程、交通运输、水利水电、市政给排水、城市规划和总图设计等众多领域…