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.rhkb.cn/news/241653.html

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

相关文章

Linux环境下,针对QT软件工程搭建C++Test单元测试环境的操作指南

文章目录 前言一、安装QT二、安装CTest三、使用QT生成.bdf文件四、创建CTest工程注意事项 前言 CTest是Parasoft公司出品的一款可以针对C/C源代码进行静态分析、单元测试、集成测试的测试工具。本文主要讲解如何在Linux环境下&#xff0c;搭建QT插件版的CTest测试环境。 一、…

深入Android S (12.0) 探索Framework之输入子系统InputReader的流程

Framework层之输入系统 第一篇 深入Android S (12.0) 探索Framework之输入系统IMS的构成与启动 第二篇 深入Android S (12.0) 探索Framework之输入子系统InputReader的流程 文章目录 Framework层之输入系统前言一、基础知识1、输入子系统2、INotify 与 Epoll2.1、INotify 机制…

01 MyBatisPlus快速入门

1. MyBatis-Plus快速入门 版本 3.5.31并非另起炉灶 , 而是MyBatis的增强 , 使用之前依然要导入MyBatis的依赖 , 且之前MyBatis的所有功能依然可以使用.局限性是仅限于单表操作, 对于多表仍需要手写 项目结构&#xff1a; 先导入依赖&#xff0c;比之前多了一个mybatis-plus…

使用 Kali Linux Hydra 工具进行攻击测试和警报生成

一、Hydra 工具和 Kali Linux 简介 在网络安全领域中&#xff0c;渗透测试是评估系统密码强度的重要组成部分。Hydra 是一款由黑客组织“The Hackers Choice”开发的开源登录破解工具&#xff0c;支持50多种协议。本教程将探索如何将 Hydra 与 Kali Linux 结合使用&#xff0c…

react native Gradle的原国外地址、本地下载、国内阿里腾讯镜像三种下载配置

一、国外地址&#xff1a;&#xff08;初始项目默认&#xff09; 下载地址&#xff1a;https://services.gradle.org/distributions/ 文件地址见下图&#xff1a; 注意&#xff1a;这个地址下载十次就有九次是连接超时&#xff0c;建议换另外两种方法 二、下载到本地&#x…

计算机网络 应用层

文章目录 应用层域名系统 DNS域名系统概述互联网的域名结构域名服务器 文件传送协议FTP 概述FTP 的基本工作原理简单文件传送协议 TFTP 远程终端协议 TELNET万维网 WWW统一资源定位符 URL超文本传送协议 HTTP万维网的信息检索系统 电子邮件电子邮件概述简单邮件传送协议 SMTP邮…

项目管理工具——禅道在企业内部的使用

目录 一、禅道的下载安装 1.1 禅道官网 1.2 安装步骤 二、禅道启动 2.1 访问禅道 三、禅道的使用 3.1 公司信息编辑 3.2 admin管理组织结构 3.2.1 岗位母部门添加 3.2.2 岗位子部门添加 3.2.3 用户新增 3.2.4 用户职位编辑 3.3 产品经理使用禅道 3.3.1 添加产品…

Kafka-消费者-KafkaConsumer分析-PartitionAssignor

Leader消费者在收到JoinGroupResponse后&#xff0c;会按照其中指定的分区分配策略进行分区分配&#xff0c;每个分区分配策略就是一个PartitionAssignor接口的实现。图是PartitionAssignor的继承结构及其中的组件。 PartitionAssignor接口中定义了Assignment和Subscription两个…

个人实现的QT拼图游戏(开源),QT拖拽事件详解

文章目录 效果图引言玩法 拖拽概念基本概念如何在Qt中使用拖放注意事项 游戏关键问题总结 效果图 ![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/c6dd66befd314442adf07e1dec0d550c.png 引言 在学习QT demo时&#xff0c;发现有一个拼图demo&#xff0c;介绍拖…

ES框架详解

ES框架详解 1、全文检索的介绍 ​ 那么对于一般的公司&#xff0c;初期是没有那么多数据的&#xff0c;所以很多公司更倾向于使用传统的数据库&#xff1a;mysql&#xff1b;比如我们要查找关键字”传智播客“&#xff0c;那么查询的方式大概就是:select * from table where …

【AI接口】语音版、文心一言大模型和AI绘图、图片检测API

文章目录 一、语音版大模型AI1、接口2、请求参数3、请求参数示例4、接口返回示例 二、AI图片鉴黄合规检测API1、接口2、请求参数3、请求参数示例4、接口返回示例5、报错说明6、代码开源 三、人工智能AI绘画API1、接口2、请求参数3、请求参数示例4、接口返回示例5、AI绘画成果展…

RDMA编程实践-SEND-RECEICVE原语应用

RDMA编程实践 本文描述了RDMA编程过程中的SEND-RECEIVE双边原语的代码实现。包含多个版本&#xff0c;1、client向server发送消息&#xff0c;server回复client收到消息(ACK)&#xff0c;然后两边断开连接。2、server端循环等待客户端建立连接&#xff0c;client发送一次消息后…

Linux 【C编程】 引入线程,线程相关函数

1.线程的引入 1.1使用线程同时读取键盘和鼠标 代码演示&#xff1a; #include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h> #include <termios.h> #include <fcntl.h> #include <string.h> // 读取…

鸿蒙HarmonyOS实战-ArkTS语言(基本语法)

&#x1f680;一、ArkTS语言基本语法 &#x1f50e;1.简介 HarmonyOS的ArkTS语言是一种基于TypeScript开发的语言&#xff0c;它专为HarmonyOS系统开发而设计。ArkTS语言结合了JavaScript的灵活性和TypeScript的严谨性&#xff0c;使得开发者能够快速、高效地开发出高质量的Har…

如何从命令行运行testng.xml?

目录 创建一个新的java项目并从命令行运行testng.xml 使用命令行运行XML文件 从命令行运行现有maven项目的XML文件 在这篇文章中&#xff0c;我们将使用命令行运行testng.xml。有多种场景需要使用命令行工具运行testng.xml。也许您已经创建了一个maven项目&#xff0c;现在想…

git提交代码到远端仓库的方法详解

一、何为git git就是版本控制器&#xff0c;就比如说你新建了一个git文件夹&#xff0c;里面用于存放你的C语言实习报告&#xff0c;现在要用git对该文件夹进行接管。当你修改了你的C语言实习报告点击保存之后&#xff0c;就用git的相关命令&#xff0c;提交给git&#xff0c;让…

Flask 3.x log全域配置(包含pytest)

最近使用到flask3.x&#xff0c;配置了全域的log&#xff0c;这边记录下 首先需要创建logging的配置文件&#xff0c;我是放在项目根目录的&#xff0c; Logging 配置 logging.json {"version": 1, # 配置文件版本号"formatters": {"default&qu…

git中合并分支时出现了代码冲突怎么办

目录 第一章、Git代码冲突介绍1.1&#xff09;什么是Git代码冲突①git merge命令介绍②代码冲突原因 1.2&#xff09;提示代码冲突的两种情况①本地不同分支的文件有差异时&#xff1a;②本地仓库和git远程仓库的文件有差异时&#xff1a; 1.3&#xff09;解决合并时的代码冲突…

音乐人声分离工具:极简的人声和背景音乐分离工具

这是一个极简的人声和背景音乐分离工具&#xff0c;本地化网页操作&#xff0c;无需连接外网&#xff0c;使用 2stems/4stems/5stems 模型。 将一首歌曲或者含有背景音乐的音视频文件&#xff0c;拖拽到本地网页中&#xff0c;即可将其中的人声和音乐声分离为单独的音频wav文件…

04 SpringBoot整合Druid/MyBatis/事务/AOP+打包项目

整合Druid 项目结构&#xff1a; 引入依赖&#xff1a; <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaL…