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.mzph.cn/news/636689.shtml

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

相关文章

随身WiFi到底能不能买?一篇文章给你讲清楚!随身WiFi哪个品牌最靠谱 ,随身WiFi推荐第一名

随着移动设备的普及&#xff0c;人们对无线网络的需求越来越高。传统WiFi虽然覆盖面广&#xff0c;但移动性差&#xff0c;不能满足人们在外出、旅行或商务场合的上网需求。此时&#xff0c;随身WiFi的出现填补了这一空白。那么&#xff0c;随身WiFi究竟有何优势和劣势&#xf…

Zabbix 系统监控详解

1 介绍 1.1 摘要 本文深入浅出&#xff0c;切近实际运维应用&#xff0c;由 zabbix 3.4 版本入手&#xff0c;学习 zabbix 监控告警实现方式&#xff0c;由 zabbix 5.0 浅出实现快速部署、快速应用。本人从业多年&#xff0c;关注 zabbix 开源社区&#xff0c;以及 zabbix 官…

【开发必备】泳道图编辑工具及使用

1.什么是泳道图 事情的起因在与博主要和几位小伙伴一起开发一个小程序&#xff0c;那么涉及的人多时就需要用到需求文档这个玩意。然后博主当然要扛起写需求文档这项项目经理 &#xff08;牛马&#xff09;的职责了&#xff01; 然后&#xff0c;博主就发现需求文档中一个看似…

idea上传本地项目到gitlab

1. idea上传本地项目到gitlab 1. 配置idea里本地安装的git位置 即选择 Settings -> Version Control -> Git -> Path to Git executable 2. 在idea创建本地仓库 即选择 VCS -> Create Git Repository 然后选择目录&#xff0c;默认就是选择的当前项目&#xff…

【Linux】03 GCC编译器的使用

一、编译过程 在使用gcc编译程序时&#xff0c;编译过程可以简要划分为4个阶段&#xff1a; 预处理、编译、汇编、链接 1.1 预处理&#xff08;preprocessing&#xff09; 这个阶段主要处理源文件中的#indef、#include和#define预处理命令&#xff1b; 这里主要是把一些include…

Linux指令(四)

1.more指令 我们知道cat指令是用来读取文本文件的&#xff0c;但是如果是大文件&#xff0c;其实是不适合cat读取的&#xff0c;原因是&#xff1a;cat读取会直接到文本的结尾&#xff0c;所以我们引入&#xff1a;more指令 该指令不会将文件直接读到结尾&#xff0c;而是将最…

Python中二维数据(数组、列表)索引和切片的Bug

Python中有关数据结构索引和切片引起的Bug 一维数据索引和切片一维数组一维列表 二维数据的索引和切片二维数组二维(错误)列表 一维数据索引和切片 一维数组 对于一维数据进行索引和切片操作&#xff0c;大家都比较熟悉通过下面代码进行实现 import numpy as np data np.ra…

[陇剑杯 2021]webshell

[陇剑杯 2021]webshell 题目做法及思路解析&#xff08;个人分享&#xff09; 问一&#xff1a;单位网站被黑客挂马&#xff0c;请您从流量中分析出webshell&#xff0c;进行回答&#xff1a; 黑客登录系统使用的密码是_____________。 题目思路&#xff1a; 分析题目&…

logstack 日志技术栈-02-ELK 的缺点?loki 更轻量的解决方案?

ELK/EFK日志系统 如果今天谈论到要部署一套日志系统&#xff0c;相信用户首先会想到的就是经典的ELK架构&#xff0c;或者现在被称为Elastic Stack。 Elastic Stack架构为Elasticsearch Logstash Kibana Beats的组合&#xff0c;其中&#xff0c;Beats负责日志的采集&…

假设检验:以样本服从二项分布举例

目录 假设检验一、假设检验的思想二、假设检验的基本步骤1. 确定要进行检验的假设2. 选择检验统计量3. 确定用于做决策的拒绝域4. 求出检验统计量的值5. 查看样本结果是否位于拒绝域内6. 做出决策 三、举例说明例子1——某公司治疗打鼾例子2——女士品茶的故事 假设检验 一、假…

用Photoshop来制作GIF动画

录了个GIF格式的录屏文件&#xff0c;领导让再剪辑下&#xff0c;于是用Photoshop2023&#xff08;PS版本低至CS6操作方式一样&#xff09;进行剪辑&#xff0c;录屏文件有约1400帧&#xff0c;由于我处理的帧数太多&#xff0c;PS保存为GIF格式时&#xff0c;还是挺耗时的&…

【Docker】网络配置及自定义网络的使用

一、引言 1、什么是网络配置 Docker的网络配置主要是指Docker容器与外部网络之间的连接设置&#xff0c;包括容器内部的IP地址、端口号等。Docker提供了多种网络模式&#xff0c;包括bridge、host、none等&#xff0c;以满足不同的需求。 默认情况下&#xff0c;Docker使用brid…

用Python替代Mapinfo更快查找两张表中距离最近的点

目录 一、引言 二、准备工作 三、数据准备 四、计算距离 五、筛选最近点 六、完整代码示例 七、性能优化 八、总结 一、引言 在地理信息系统&#xff08;GIS&#xff09;中&#xff0c;经常需要查找两张表中距离最近的点。传统的做法是使用Mapinfo软件&#xff0c;但这…

探索世界,从一款好用的浏览器开始!

好用的浏览器分享 在这个数字化的时代&#xff0c;浏览器已经成为了我们生活中不可或缺的工具。从浏览新闻、社交媒体到工作学习&#xff0c;我们几乎无时无刻不在与浏览器打交道。那么&#xff0c;如何选择一款好用的浏览器呢&#xff1f;今天&#xff0c;我就来为大家分享几…

C++函数指针

目录 背景定义 实例运行结果 背景 定义 函数指针是一个指向函数的指针变量&#xff0c;它可以指向某个函数的入口地址&#xff0c;使得程序可以通过该指针变量调用该函数。 实例 #include <iostream> using namespace std;/*** 函数指针 */// 定义一个函数 int add(i…

SAP ABAP 指针

SAP ABAP 指针 目录 一、FIELD SYMBOL 字段符号 1、定义2、assign分配3.分配-内表4.动态内表 二、数据引用 一、FIELD SYMBOL 字段符号 定义&#xff1a;是已经存在的数据对象的占位符或者符号名称&#xff0c;通过关键字 FIELD-SYMBOLS 定义&#xff0c;类似于指针&#x…

systemverilog/verilog文件操作

1、Verilog文件操作 Verilog具有系统任务和功能,可以打开文件、将值输出到文件、从文件中读取值并加载到其他变量和关闭文件。 1.1 、Verilog文件操作 1.1.1、打开和关闭文件 module tb; // 声明一个变量存储 file handler integer fd; initial begin // 以写权限打开一个文…

机器学习实战15-推荐算法-协同过滤在电影推荐中的应用实践

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下机器学习实战15-推荐算法-协同过滤在电影推荐中的应用实践。 随着互联网的发展&#xff0c;信息过载问题日益严重&#xff0c;推荐系统应运而生。本文将详细介绍推荐算法在电影推荐领域的应用实践&#xff0c;以及其…

【Python】--- 基础语法(1)

目录 1.变量和表达式2.变量和类型2.1变量是什么2.2变量的语法2.3变量的类型2.3.1整数2.3.2浮点数&#xff08;小数&#xff09;2.3.3字符串2.3.4布尔2.3.5其他 2.4为什么要有这么多类型2.5动态类型特征 3.注释3.1注释的语法3.2注释的规范 结语 1.变量和表达式 对python的学习就…

muduo网络库剖析——通道Channel类

muduo网络库剖析——通道Channel类 前情从muduo到my_muduo 概要事件种类channel 框架与细节成员函数细节实现使用方法 源码结尾 前情 从muduo到my_muduo 作为一个宏大的、功能健全的muduo库&#xff0c;考虑的肯定是众多情况是否可以高效满足&#xff1b;而作为学习者&#x…