import fabric from '../../js/fabric-adapter' import del from './images/sc.svg' import rotate from './images/xz.svg' import symmetry from './images/jx.svg' import jia from './images/jia.svg' import jian from './images/jian.svg' import pingpu from './images/pingpu.svg' import { uploadBaseImg } from '@/apis/other' export default { data() { return { action: 'select', drawWidth: 20, eraseOpacity:1, //橡皮擦 透明度 min: 2, max: 80, color: '#ffffff', //撤销 canvasState: [],//操作记录 stateIndex: -1, canUndoState:false, //画笔在层上操作 group:null, arr:[], } }, props: { toolConfig: { type: Array, default: () => ['select','color','draw','eraser','empty','undo'] }, controlToolConfig: { type: Array, default: () => ['del','rotate','symmetry'] }, defaultColor:{ //画笔默认颜色 type: String, default:'#ffffff' } }, computed: { //是否可以撤销 canUndo() { return this.stateIndex >= 0; }, }, watch: { action(val,old){ if(old==='color' && this.showStraw){ this.hideColorXg() } }, drawWidth() { if (this.fcanvas){ this.fcanvas.freeDrawingBrush.width = this.drawWidth this.setCursor() } }, color() { if (this.fcanvas) { this.fcanvas.freeDrawingBrush.color = this.color this.toDraw() } }, eraseOpacity(){ if (this.fcanvas) { this.fcanvas.freeDrawingBrush.opacity = this.eraseOpacity this.setCursor() } } }, created() { this.color = this.defaultColor }, activated() { //设置控件 添加镜像删除等icon this.setControlTool(); }, mounted() { //设置控件 添加镜像删除等icon this.setControlTool(); }, methods: { actionInit(){ //监听撤销 if(this.toolConfig.includes('undo')){ this.setUndo() } //修改画笔 每次在此图层上操作 if(this.toolConfig.includes('draw')){ this.fcanvas.on('path:created', (opt)=>{ if(this.action === 'erase') { this.selected.map(item=>{ this.updateLayerCover(item) }) this.getLayers() return; } if(this.action !== 'draw') return; let object = this.fcanvas.getObjects() let path = object[object.length-1] let selected = [] let name = 'pencli' let sort = null let id = null if(this.selected.length === 1){ name = this.selected[0].name sort = this.selected[0].sort id = this.selected[0].id selected = this.selected }else{ id = this.getNextLayersId() sort = this.getNextLayersSort() } selected.push(path) var sel = new fabric.ActiveSelection(selected, { canvas: this.fcanvas, }); this.fcanvas.setActiveObject(sel); // this.canUndoState = true; const group = this.fcanvas.getActiveObject().toGroup(); group.setControlsVisibility({ 'mtr':false, }) group.set({ name, sort, id, erasable:false, }) group.moveTo(sort) this.fcanvas.discardActiveObject() this.selected = [group] // this.updateCanvasState(); this.getLayers() }) } this.fcanvas.on('mouse:down', (opt)=>{ if(this.action !== 'erase') return; this.toEraseInLayer(); }) //设置默认笔画 setTimeout(() => { this.fcanvas.freeDrawingBrush.width = this.drawWidth this.fcanvas.freeDrawingBrush.color = this.color }, 100) }, // 新增图片图层 async addMaps(url, options = { left:50, bottom:50, }, ) { return new Promise((resolve, reject) => { if(!this.fcanvas.add){ reject(false); } fabric.Image.fromURL(url, (img) => { let sort = options.sort || this.getNextLayersSort() let erasable = options.erasable === false ? false : true let selectable = options.selectable === false ? false : true let id = this.getNextLayersId() this.fcanvas.discardActiveObject() img.setControlsVisibility({ 'mtr':false, }) this.fcanvas.add(img.set({ name:"image", ...options, sort, id, erasable:erasable, })) img.moveTo(sort) if(img.getScaledWidth() > options.maxWidth){ let zoom = this.fcanvas.getZoom() img.scaleToWidth(options.maxWidth * this.fcanvas.getZoom()) } if(img.getScaledHeight() > options.maxHeight){ img.scaleToHeight(options.maxHeight * this.fcanvas.getZoom()) } if(selectable) this.fcanvas.setActiveObject(img) this.toSelect() this.getLayers() if(selectable) this.select(img) this.fcanvas.renderAll(); console.log(img) resolve(img) }, { crossOrigin: 'anonymous', }) }) }, //新增 svg addSvg(url, options = { left:50, top:50, width:200, height:200 },){ if(!this.fcanvas.add){ return; } fabric.loadSVGFromURL(url, (objects,options) => { const svg = fabric.util.groupSVGElements(objects, options); let sort = options.sort || this.getNextLayersSort() let erasable = options.erasable || false let id = this.getNextLayersId() this.fcanvas.discardActiveObject() svg.setControlsVisibility({ 'mtr':false, }) this.fcanvas.add(svg.set({ ...options, name:"svg", sort, id, erasable:erasable, })) svg.moveTo(sort) this.fcanvas.setActiveObject(svg) this.toSelect() this.getLayers() this.select(svg) this.fcanvas.renderAll(); }) }, //新增文字图层 addText( options = { left:50, top:50, },){ if(!this.fcanvas.add){ return; } const textbox = new fabric.Textbox('', { left: 50, top: 50, width: 150, fontSize: 20 }); let sort = options.sort || this.getNextLayersSort() let erasable = options.erasable || false let id = this.getNextLayersId() this.fcanvas.discardActiveObject() this.fcanvas.add(textbox.set({ ...options, name:"text", sort, id, erasable:erasable, })) textbox.moveTo(sort) this.fcanvas.setActiveObject(textbox) this.toSelect() this.getLayers() this.select(textbox) textbox.enterEditing(); this.fcanvas.renderAll(); }, // 选择 toSelect() { this.action = 'select' this.fcanvas.isDrawingMode = false }, async setCursor(){ if(!['draw','erase'].includes(this.action)) return let fcanvasZoom = this.fcanvas.getZoom() let deviceZoom = detectZoom.device() let zoom = fcanvasZoom/deviceZoom; const canvas = document.createElement('canvas') const fcanvas = new fabric.Canvas(canvas, { containerClass:"fcanvas", // 元素对象被选中时保持在当前z轴,不会跳到最顶层 preserveObjectStacking:true, width: this.drawWidth*zoom, height: this.drawWidth*zoom, }) const color = this.action === 'draw' ? this.color : '#ffffff'; var rect = new fabric.Circle({ radius: (this.drawWidth*zoom)/2, fill:color, left: 0, opacity: 1, strokeWidth: 0, strokeUniform: true }); fcanvas.add(rect); const base64 = await fcanvas.toDataURL({ format: 'png', enableRetinaScaling: true }) this.fcanvas.freeDrawingCursor = `url( ${base64} ) ${(this.drawWidth*fcanvasZoom)/2} ${(this.drawWidth*fcanvasZoom)/2}, auto`; }, // 画笔 toDraw() { this.action = 'draw' this.fcanvas.freeDrawingBrush = new fabric.PencilBrush(this.fcanvas) this.fcanvas.freeDrawingBrush.canvas.selectable = false; this.setFreeDrawingBrushStyle() this.setCursor() this.fcanvas.renderAll(); }, // 橡皮擦 toErase() { this.action = 'erase' console.log(this.fcanvas.getObjects()) this.fcanvas.freeDrawingBrush = new fabric.EraserBrush(this.fcanvas) this.toEraseInLayer(); this.setFreeDrawingBrushStyle() this.setCursor() }, //拖拽 toDrag(){ this.action = 'drag' this.fcanvas.isDrawingMode = false this.fcanvas.hoverCursor = 'grab' }, //设置橡皮擦作用图层 toEraseInLayer(){ this.fcanvas.getObjects().map(item=>{ if(this.selectedIds.includes(item.id)){ item.set({ erasable:true, }) }else{ item.set({ erasable:false, }) } }) }, // 清空 setEmpty() { this.fcanvas.remove.apply(this.fcanvas, this.fcanvas.getObjects()) this.getLayers() this.undoAfterSelectLayers(); }, //合并组 toGroup(){ this.fcanvas.getActiveObject().toGroup(); this.fcanvas.requestRenderAll(); }, // 设置画笔样式 setFreeDrawingBrushStyle() { if (!this.fcanvas) return this.fcanvas.freeDrawingBrush.width = this.drawWidth this.fcanvas.freeDrawingBrush.color = this.color this.fcanvas.freeDrawingBrush.opacity = this.eraseOpacity this.fcanvas.isDrawingMode = true }, //设置控件 setControlTool(){ let self = this; //修改 controls 默认样式 fabric.Object.prototype.transparentCorners = false; fabric.Object.prototype.cornerColor = '#333333'; fabric.Object.prototype.cornerStrokeColor = '#333333'; fabric.Object.prototype.borderColor = '#000000'; fabric.Object.prototype.cornerStyle = 'circle'; fabric.Textbox.prototype.transparentCorners = false; fabric.Textbox.prototype.cornerColor = '#333333'; fabric.Textbox.prototype.cornerStrokeColor = '#333333'; fabric.Textbox.prototype.borderColor = '#000000'; fabric.Textbox.prototype.cornerStyle = 'circle'; //删除控件, 每次 重新计算 let controlToolConfigFun = { del:'deleteControl', rotate:'rotateControl', tile:"tileControl", tileIconjian:"tileIconjianControl", symmetry:'symmetryControl', } Object.keys(controlToolConfigFun).map(item=>{ delete fabric.Object.prototype.controls[controlToolConfigFun[item]] delete fabric.Textbox.prototype.controls[controlToolConfigFun[item]] }) delete fabric.Object.prototype.controls.tileIconppControl delete fabric.Textbox.prototype.controls.tileIconppControl if(this.controlToolConfig.includes('del')) setToolDel() if(this.controlToolConfig.includes('rotate')) setToolRotate() if(this.controlToolConfig.includes('symmetry')) setToolSymmetry() if(this.controlToolConfig.includes('tile')) setTooTile() // 增加图标 function renderIcon(icon) { return function renderIcon(ctx, left, top, styleOverride, fabricObject) { var size = this.cornerSize ctx.save() ctx.translate(left, top) ctx.rotate(fabric.util.degreesToRadians(fabricObject.angle)) ctx.drawImage(icon, -size / 2, -size / 2, size, size) ctx.restore() } } // 删除 function setToolDel() { var icon = document.createElement('img') icon.src = del icon.title = '删除' fabric.Object.prototype.controls.deleteControl = new fabric.Control({ x: -0.5, y: -0.5, offsetY: -25, cursorStyle:"Pointer", mouseUpHandler: deleteObject, render: renderIcon(icon), cornerSize: 20 }) fabric.Textbox.prototype.controls.deleteControl = fabric.Object.prototype.controls.deleteControl function deleteObject(eventData, transform) { try { var target = transform.target var canvas = target.canvas if(target.delable === false){ self.$message.warning('该元素无法被删除') return; } canvas.remove(target) if(target.hasOwnProperty('_objects') && target.name !== 'svg'){ target._objects.map(item=>{ if(item.delable !== false){ canvas.remove(item) } }) canvas.discardActiveObject() } canvas.requestRenderAll() self.getLayers() self.undoAfterSelectLayers(); }catch (e) { console.log(e) } } } // 旋转 function setToolRotate() { var icon = document.createElement('img') icon.src = rotate icon.title = '旋转' fabric.Object.prototype.controls.rotateControl = new fabric.Control({ x: 0, y: -0.5, actionHandler: fabric.controlsUtils.rotationWithSnapping, cursorStyleHandler: fabric.controlsUtils.rotationStyleHandler, offsetY: -40, withConnection: true, actionName: 'rotate', // 渲染图标 cursorStyle:"Pointer", render: renderIcon(icon), // 设置控制点大小 cornerSize: 20 }) fabric.Textbox.prototype.controls.rotateControl = fabric.Object.prototype.controls.rotateControl } // 镜像 function setToolSymmetry() { let icon = document.createElement('img') icon.src = symmetry icon.title = '镜像' fabric.Object.prototype.controls.symmetryControl = new fabric.Control({ x: 0.5, y: -0.5, offsetY: -25, offsetX: 0, cursorStyle:"Pointer", mouseUpHandler: deleteObject, render: renderIcon(icon), cornerSize: 20 }) fabric.Textbox.prototype.controls.symmetryControl = fabric.Object.prototype.controls.symmetryControl function deleteObject(eventData, transform) { var target = transform.target target.set({ flipX:!target.flipX, }) var canvas = target.canvas self.updateCanvasState() canvas.requestRenderAll() } } // 平铺 function setTooTile() { let iconpp = document.createElement('img') iconpp.src = pingpu iconpp.title = '平铺' fabric.Object.prototype.controls.tileIconppControl = new fabric.Control({ x: -0.5, y: -0.5, offsetX: 30, offsetY: -25, cursorStyle:"default", render: renderIcon(iconpp), cornerSize: 60 }) let icon = document.createElement('img') icon.src = jia icon.title = '平铺加' fabric.Object.prototype.controls.tileControl = new fabric.Control({ x: -0.5, y: -0.5, offsetX: 60, offsetY: -25, cursorStyle:"Pointer", mouseUpHandler: tileObject, render: renderIcon(icon), cornerSize: 20 }) let iconjian = document.createElement('img') iconjian.src = jian iconjian.title = '平铺减' fabric.Object.prototype.controls.tileIconjianControl = new fabric.Control({ x: -0.5, y: -0.5, offsetX: 0, offsetY: -25, cursorStyle:"Pointer", mouseUpHandler: tileObject, render: renderIcon(iconjian), cornerSize: 20 }) async function tileObject(eventData, transform) { var target = transform.target if(target.name !== 'image' || !target.tile ) return; if(target.tile.src){ let multiple = target.tile.multiple + 1 if(transform.corner === 'tileIconjianControl'){ if(target.tile.multiple === 1){ self.$message.error('无法在放大/缩小了') return } multiple = Math.max(target.tile.multiple - 1,1) }else{ if(target.tile.multiple === 20){ self.$message.error('无法在放大/缩小了') return } } let thisOssOptios = { ...target.tile.ossOptios, w:parseInt((target.tile.ossOptios.w)/multiple), h:parseInt((target.tile.ossOptios.h)/multiple) } let multipleImgSrc = self.$appfun.ossResize(target.tile.src,thisOssOptios) const image = await self.$appfun.loadingImg(multipleImgSrc) const canvas = document.createElement('canvas') canvas.width = target.width canvas.height = target.height const ctx = canvas.getContext('2d') var pat = ctx.createPattern(image,"repeat"); ctx.rect(0,0,canvas.width,canvas.height); ctx.fillStyle = pat; ctx.fill(); const url = canvas.toDataURL('image/png') target.setSrc(url,()=>{ target.tile.multiple = multiple self.updateCanvasState() target.canvas.requestRenderAll() }) } } } }, //撤销 historyState(index){ if (this.canUndo) { this.canUndoState = true; this.fcanvas.loadFromJSON(this.canvasState[index], () => { this.fcanvas.renderAll(); this.stateIndex = index; this.canvasState.splice(this.stateIndex+1); this.getLayers() this.undoAfterSelectLayers(); this.canUndoState = false; }); } }, setUndo(){ //监听撤销 this.fcanvas.on("object:modified", ()=>{ this.updateCanvasState() }); }, updateCanvasState(e) { if (!this.canUndoState) { const canvasAsJson = JSON.stringify(this.fcanvas.toJSON(['name','sort','mtr','id','selectable','erasable'])); this.canvasState.splice(this.stateIndex+1); this.canvasState.push(canvasAsJson); this.stateIndex = this.canvasState.length - 1; // if(this.viewStatus) this.updateMiniMap(); } else { this.canUndoState = false; } }, } }