import { exportSele, JSRectUpdata, JSchangeType, JScanvasMouseDown, JScanvasMouseMove, JScreateCheck, JSSetTexture, JSSetRemoveImage, } from "@/tool/canvasDrawingCopy"; import { getMousePosition } from "@/tool/mdEvent"; import { file } from "jszip"; class MyCanvas { canvas=null constructor() { this.pencilList = { textFontFamilyList:[ { value: 'Arial', name: 'select font' }, { value: 'EN_slogan_art1', name: 'select font' }, { value: 'EN_slogan_art2', name: 'select font' }, { value: 'EN_slogan_art3', name: 'select font' }, { value: 'EN_slogan_art4', name: 'select font' }, { value: 'EN_slogan_art5', name: 'select font' }, { value: 'EN_slogan_art6', name: 'select font' }, { value: 'EN_slogan_art7', name: 'select font' }, { value: '微软雅黑', name: '请选择字体' }, { value: 'CN_slogan_art1', name: '请选择字体' }, { value: 'CN_slogan_art2', name: '请选择字体' }, { value: 'CN_slogan_art3', name: '请选择字体' }, { value: 'CN_slogan_art4', name: '请选择字体' }, { value: '华文行楷', name: '请选择字体' }, { value: '隶书', name: '请选择字体' }, ], brushList:[ { value:'PencilBrush', url:'/image/brush/PencilBrush.jpg' },{ value:'Marking', url:'/image/brush/PencilBrush-2.jpg' },{ value:'InkBrush', url:'/image/brush/InkBrush.jpg' },{ value:'CrayonBrush', url:'/image/brush/CrayonBrush.jpg' },{ value:'RibbonBrush', url:'/image/brush/RibbonBrush.jpg' },{ value:'MarkerBrush', url:'/image/brush/MarkerBrush.jpg' },{ value:'WritingBrush', url:'/image/brush/WritingBrush.jpg' },{ value:'LongfurBrush', url:'/image/brush/LongfurBrush.jpg' },{ value:'SpraypaintBrush', url:'/image/brush/SpraypaintBrush.jpg' }, ] } this.canvasDomParent = null; this.canvas = MyCanvas.canvas; this.addPresent = {} this.canvasWH={ width:0, height:0, } this.createPatterning = {//创建图形 state:false, content:null, current:null, //临时图形 polyLineBtn:null, textDataShow:false, } this.mouse = { point:null, upPoint:null, isMovePostion:false, isDown:false, lastPosX:0, lastPosY:0, } this.keyboard={ down:[] } this.pencilbtnStyle ={//笔触图形 background:'', width:0+'px', height:0+'px', display:'none', left:0+'px', top:0+'px', } this.layer = { list:[], selectLayer:{ group:null, id:-1, }, currentIndex:0, } this.liquefaction = { img:null, type:'', dashed:null, } this.clipPath = { isImg:false, img:null, clipGroup:null } this.dashed = { state:false,//用来判断鼠标是否移入dashed 如果移入就变为可拖拽 isDrawingMode:false,//用来获取移入前是否是绘画模式 } this.fontFamily = this.pencilList.textFontFamilyList[0].value this.createText=null this.pencilBtn = null; this.pencilType = null; this.canvasPencilColor = '#000000' this.colorHistoryList = ['rgb(0, 0, 0)'] this.keyDown = [];//键盘按下 this.oldOperation = '' //笔触 this.brushwork = { value:'PencilBrush', width:{}, color:'#000000', texture:0, } this.texture={ value:0, list:[], } for (let index = 0; index < 20; index++) { this.texture.list.push({value:index,url:`/image/texture/texture${index}.webp`}) } this._clipboard = null; //剪切板 this.isLoadCanvas = false//撤回或者反撤回false为撤回 this.reverseCanvasState=[];//撤回 this.normalCanvasState=[];//反撤回 this.canvasState = {}//当前内容 this.operation = 'movePosition' this.operationMode = 'fill' this.setTimeOut = {//定时器 color:null, width:null, colorHistory:null, canvasWH:null, updataLayer:null, }//给切换颜色设置防抖 } async canvasInit (dom, val){//初始化 this.canvasClear() this.canvasWH = val this.canvasDomParent = dom; var canvasDom = document.createElement("canvas"); this.canvasDomParent.appendChild(canvasDom); MyCanvas.canvas = new fabric.Canvas(canvasDom, { backgroundColor: "rgba(230, 230, 230)", width: this.canvasWH.width, height: this.canvasWH.height, isDrawingMode: val.isDrawingMode, // 开启绘图模式 selectionFullyContained: true, selectionKey:'ctrlKey', includeDefaultValues: false,//尚未测试 精简导出JSON // freeDrawingCursor: 'none', preserveObjectStacking:true, }); // MyCanvas.canvas.freeDrawingCursor= 'none' this.initCanvasWH('init') fabric.Object.prototype.cornerSize = 10//选中后的操作按钮大小 fabric.Object.prototype.transparentCorners = false //实心 if(!fabric.Object.prototype.controls.deleteControl){//设置元素删除 JSSetRemoveImage(this.deleteObject.bind(this)) }else{ fabric.Object.prototype.controls.deleteControl.mouseUpHandler = this.deleteObject.bind(this) } MyCanvas.canvas.on("object:modified", (event)=>{ this.updateCanvasState('') this.updataLayer() }); this.canvasKeyDown = this.canvasKeyDown.bind(this); this.canvasKeyUp = this.canvasKeyUp.bind(this); // let rect1 = new fabric.Rect({ // left: 0, // top: 0, // width: 500, // height: 500, // fill: "red", // }); // MyCanvas.canvas.add(rect1) // rect1.on('mouseover', function() { // // rect1.set({ fill: 'green' }); // 鼠标移入时改变颜色为绿色 // // canvas.renderAll(); // 重新渲染画布 // }); // // 为矩形添加 mouseleave 事件 // rect1.on('mouseout', function() { // // rect1.set({ fill: 'blue' }); // 鼠标移出时恢复颜色为蓝色 // // canvas.renderAll(); // 重新渲染画布 // }); // return // rect1.filters.push(new fabric.Image.filters.Grayscale()) // MyCanvas.canvas.add(rect1) // // MyCanvas.canvas.add(rect1) // let rect = new fabric.Rect({ // left: 0, // top: 0, // width: MyCanvas.canvas.width, // height: MyCanvas.canvas.height, // inverted:true, // fill: "red", // }); // // let rect2 = new fabric.Rect({ // // width: MyCanvas.canvas.width, // // height: MyCanvas.canvas.height, // // fill:'#FFF' // // }); // // // MyCanvas.canvas.add(rect2) // fabric.Image.fromURL('https://www.minio.aida.com.hk:12024/aida-collection-element/83/Moodboard/7a5fec0f-5a8c-4f1e-80c0-c4cda335d7c5.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=admin%2F20241112%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20241112T060752Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=25c5fa23dc447d1d06b4efeeec23454c797219df9c45d2cec231d0c8372754ce',(img)=>{ // let scale = { // X:MyCanvas.canvas.width/img.width, // Y:MyCanvas.canvas.height/img.height // } // rect.set({ // fill: new fabric.Pattern({ // source: img.getElement(), // 传入图片元素 // repeat: 'no-repeat' // 设置图片不重复 // }), // clipPath : rect1 // }) // img.set({ // scaleX:scale.X, // scaleY:scale.Y, // left:0, // top:0, // }) // MyCanvas.canvas.add(rect) // },{ crossOrigin: "Anonymous" }) // // // MyCanvas.canvas.add(rect1) // // // rect1.clipPath = group // // // MyCanvas.canvas.forEachObject((item) =>{ // // // if(item.clipPath){ // // // item.clipPath.add(rect2) // // // // item.clipPath = group // // // } // // // }) // return MyCanvas.canvas.on("mouse:move", this.setCanvasMove.bind(this)); MyCanvas.canvas.on("mouse:down",this.setCanvasDown.bind(this)); MyCanvas.canvas.on("mouse:up",this.setCanvasUp.bind(this)); MyCanvas.canvas.on("mouse:wheel",this.setCanvasWheel.bind(this)); //双击 MyCanvas.canvas.on("mouse:dblclick", event=>{ if(this.operation == 'fold'){ this.foldEnd('Enter') } }); document.addEventListener('mousemove', this.mouseMove.bind(this)); document.addEventListener('touchmove', this.touchmove.bind(this)); document.addEventListener("keydown", this.canvasKeyDown); document.addEventListener("keyup", this.canvasKeyUp); initAligningGuidelines(MyCanvas.canvas, true); JSchangeType(MyCanvas.canvas,'init') // await this.createBg() await this.createLayer({str:'init'})//创建图层并且使用 MyCanvas.canvas.on("object:added", (event)=>{ this.addLayer(event) }); this.canvas = MyCanvas.canvas; return MyCanvas.canvas } initCanvasWH(str){ let scale if(this.canvasWH.width>this.canvasWH.height)scale = this.canvasDomParent.offsetWidth / this.canvasWH.width if(this.canvasWH.width{ if(obj.type != 'group' && !obj.isType('path')){ obj.selectable = false } }); }else if(['dashedPencil', 'dashed'].includes(str)){ this.setGroupGrid('all') MyCanvas.canvas.isDrawingMode = false this.pencilbtnStyle.display = `none` this.setCursor('crosshair') MyCanvas.canvas.freeDrawingCursor = 'crosshair' if(str == 'dashedPencil')this.setDashedPencil() MyCanvas.canvas.forEachObject((obj) =>{ if(obj.type != 'group' && !obj.isType('path')){ obj.selectable = false } }); }else if(str){ this.pencilbtnStyle.display = `none` this.setCursor('auto'); MyCanvas.canvas.isDrawingMode = false MyCanvas.canvas.forEachObject((obj) =>{ if(obj.type != 'group' && !obj.isType('path')){ obj.selectable = false } }); } if(str == 'dashed' || str == 'dashedPencil'){ // if(str == 'dashed' || str == 'dashedPencil' || str != 'movePosition'){ if(this.layer.selectLayer.group.custom.groupType == 'Grid')this.setGroupGrid('all') this.clipPath = { isImg:false, clipGroup:null, img:null, } } } setCursor(str){ MyCanvas.canvas.defaultCursor = str; MyCanvas.canvas.moveCursor = str; MyCanvas.canvas.hoverCursor = str; MyCanvas.canvas.upperCanvasEl.style.cursor = str; } setCanvasMove = (event)=>{ if(this.mouse.isMovePostion && this.operation == 'move') return this.setCanvasPosition(event) if(MyCanvas.canvas.isDrawingMode){ }else{ let getActiveObject = MyCanvas.canvas.getActiveObject() if( !this.clipPath.isImg && this.mouse.isDown && getActiveObject && getActiveObject.custom.dashed && this.layer.selectLayer.group.custom?.groupType == 'Grid' && this.operation == 'movePosition' ){ this.setclipPathImg() } if(this.createPatterning.state){ let str = this.operation if(this.operation == 'dashed')str = 'rect' JScanvasMouseMove(str,event,this.createPatterning.current,this.mouse.point,this.keyboard.down) MyCanvas.canvas.requestRenderAll() } } } setCanvasPosition(event){ const e = event; const vpt = MyCanvas.canvas.viewportTransform; vpt[4] += e.pointer.x - this.mouse.lastPosX; // 更新水平偏移 vpt[5] += e.pointer.y - this.mouse.lastPosY; // 更新垂直偏移 MyCanvas.canvas.requestRenderAll(); // 请求重绘画布 this.mouse.lastPosX = e.pointer.x; this.mouse.lastPosY = e.pointer.y; } // setRemoveDashed(){ // MyCanvas.canvas.forEachObject((obj) =>{ // if(obj.custom.dashed && obj._objects){ // let rect = obj._objects.filter(item => item.type == 'rect')[0] // obj.remove(rect) // MyCanvas.canvas.renderAll(); // } // }); // } //设置再画布上按下 setCanvasDown(event){ if(event.target && event.target.custom?.dashed && ['dashedPencil', 'dashed'].includes(this.operation)){ // if(MyCanvas.canvas.isDrawingMode){ // MyCanvas.canvas.isDrawingMode = false; // } this.dashed.state = true }else if(!event.target?.custom?.dashed){ // if(['dashedPencil'].includes(this.operation)){ // MyCanvas.canvas.isDrawingMode = true; // } this.dashed.state = false } let dashedGroup = MyCanvas.canvas.getObjects().filter(obj => obj?.custom?.dashed)[0]; if(this.dashed.state && (this.operation == 'dashed' || this.operation == 'dashedPencil') && dashedGroup)return if(!this.dashed.state && (this.operation == 'dashed' || this.operation == 'dashedPencil') && !event.target?.custom?.dashed && dashedGroup){ MyCanvas.canvas.remove(dashedGroup) } if(this.operation == 'movePosition' && !event?.target?.custom?.dashed && this.layer.selectLayer.group.custom.groupType == 'Grid')this.setGroupGrid('all'); //设置移动端按下添加元素 this.mouse.isDown = true this.mouse.lastPosX = event.pointer.x; this.mouse.lastPosY = event.pointer.y; if(this.operation == 'move'){ this.mouse.isMovePostion = true; this.setCursor('grabbing'); return } if(this.operation == 'zoomIn' || this.operation == 'zoomOut'){ this.setCanvasZoom(event) return } // this.setRemoveDashed() if(this.addPresent.upScaleChecked){//创建元素 this.present.upScaleChecked = false this.present = {} let pointerVpt = canvas.restorePointerVpt(event.pointer) switch (currentType.value.type) { case 'colorBoards': let rect = setGroup(currentType.value.data) setCanvasColor(pointerVpt.y, pointerVpt.x,rect) break default : createImage(pointerVpt.y, pointerVpt.x,currentType.value.type) break } // 创建完元素,把当前操作的元素类型设置回 null currentType.value.type = null currentType.value.data = null } this.mouse.point = event.absolutePointer let arr = ['rect','line','circle','triangle','ellipse','fold','dashed'] if(arr.indexOf(this.operation) > -1){ JSchangeType(MyCanvas.canvas,this.operation) this.createPatterning.state = true MyCanvas.canvas.forEachObject((obj) =>{ obj.set('selectable', false); // 禁用选中 }); if(this.createPatterning.current && this.operation == 'fold'){ MyCanvas.canvas.skipTargetFind = false this.createPatterning.current.points.push({ x: this.mouse.point.x, y: this.mouse.point.y }) }else{ let width = this.brushwork.width[this.operation]?this.brushwork.width[this.operation]:20 let data = { str:this.operation, width, selectable:true, } if(this.operation == 'dashed'){ width = 1 / MyCanvas.canvas.getZoom() data = { fill:'transparent', strokeDashArray: [width*3, width*3], width, radius:0, str:'rect', selectable:true, } }else if(this.operation == 'rect'){ data.width = 0 } this.createPatterning.current = JScanvasMouseDown(event,data) if(this.operation == 'dashed')this.createPatterning.current.set({custom:{dashed:true}}) MyCanvas.canvas.add(this.createPatterning.current) if(this.operation == 'fold'){ this.createPatterning.polyLineBtn = JScreateCheck(event) this.createPatterning.polyLineBtn.on('mousedown',this.foldEnd.bind(this)) MyCanvas.canvas.add(this.createPatterning.polyLineBtn) } } }else{ // var clickedObject = event.target; // if (clickedObject instanceof fabric.Textbox && this.operation != 'text') { // this.createPatterning.textDataShow = true // this.createText = clickedObject // console.log(1); // }else{ // console.log(2); // this.createPatterning.textDataShow = false // this.createText = {} // } // this.createPatterning.state = false } } setTextData(obj){ this.fontFamily = obj.fontFamily this.brushwork.width['text'] = obj.fontSize this.brushwork.color = obj.fill } async setCanvasUp(event){ this.mouse.isDown = false if(this.mouse.isMovePostion){ this.mouse.isMovePostion = false this.setCursor('grab'); return } if(this.operation == 'eraser')return this.updataLayer() this.mouse.upPoint = event.absolutePointer if(MyCanvas.canvas.isDrawingMode){ setTimeout(() => { // pencilbtnStyle.value.display = `none` this.updateCanvasState() }, 100); }else{ // event.target && (event.target.bringToFront())//设置优先级 } if(this.createPatterning.state){ switch (this.operation) { case 'line': this.createPatterning.current.set({stroke: this.brushwork.color}) break case 'fold': // this.createPatterning.current.set({stroke: this.brushwork.color}) break default : if(JSON.stringify(this.mouse.point) == JSON.stringify(this.mouse.upPoint)){ MyCanvas.canvas.remove(this.createPatterning.current) return } if(this.operation == 'dashed'){ this.createPatterning.current.set({fill: 'transparent'}) }else if(this.operationMode == 'fill'){ this.createPatterning.current.set({fill: this.brushwork.color}) }else if (this.operationMode == 'border'){ this.createPatterning.current.set({fill: 'transparent',stroke: this.brushwork.color,strokeWidth: this.brushwork.width[this.operation]?this.brushwork.width[this.operation]:20}) } break } if(this.operation == 'fold'){ MyCanvas.canvas.forEachObject((obj) =>{ if(this.isSelectable(obj)){ obj.selectable = true }else{ obj.selectable = false } }); MyCanvas.canvas.bringToFront(this.createPatterning.polyLineBtn);//设置优先级最高 }else if(this.operation){ this.createPatterning.state = false MyCanvas.canvas.renderAll(); if(this.operation == 'dashed'){ await this.setDashed(this.createPatterning.current) }else{ this.addLayer({target:this.createPatterning?.current}) this.setOperation('movePosition') } this.clearPatterning()//临时图形置为空并且添加撤回对象里面 } } } async setclipPathImg(){//裁剪图片 if(!this.clipPath.clipGroup)return this.clipPath.isImg = true let clipPathElement = this.clipPath.clipGroup._objects.filter(obj => obj.custom)[0] let imgBG = MyCanvas.canvas.getObjects().filter(obj => obj.custom.isSelectable && obj.type == 'rect')[0]; let position = { left:this.clipPath.clipGroup?.left - (imgBG.left?imgBG.left:0), top:this.clipPath.clipGroup?.top - (imgBG.top?imgBG.top:0), width:this.clipPath.clipGroup?.width, height:this.clipPath.clipGroup?.height, } // await this.setGroupGrid() //添加裁剪后的图片 let elements = MyCanvas.canvas.getObjects().filter(obj => this.isAddToLayer(obj)); let imgData = await this.groupToImg(elements,position,'clipPath') let img = await this.createImage({minioUrl:imgData}) img.set({ scaleX:1, scaleY:1, left:0 - position.width/2, top:0 - position.height/2, }) img.clipPath = clipPathElement this.clipPath.clipGroup.insertAt(img) //裁剪 this.layer.selectLayer.group.custom.groupType = 'Grid' clipPathElement.clone(cloned=>{ cloned.set({left:position.left,top:position.top,strokeWidth:0}) if(imgBG?.custom?.state != 'success')this.setClipPath(imgBG,cloned) }) } setDashedPencil(){ MyCanvas.canvas.isDrawingMode = true let pencil = new fabric.Test(MyCanvas.canvas,{}); //普通笔 MyCanvas.canvas.freeDrawingBrush = pencil } async setDashed(obj){ MyCanvas.canvas.remove(obj) let position = { left:obj.left, top:obj.top, width:obj.width, height:obj.height, } await obj.clone((cloned)=>{ let {width,height,left,top} = position cloned.set({ custom:{ dashed:true } }) // cloned.set({left:cloned.strokeWidth/2,top:cloned.strokeWidth/2}) let group = new fabric.Group([cloned],{ left:left + cloned.strokeWidth/2, top:top + cloned.strokeWidth/2, width,height, custom:{ dashed:true } }) console.log(223); MyCanvas.canvas.add(group) }) } async setGridOrObject(id,str){ this.layer.list.forEach((item) => { if(item.id == id)item.groupType = str }) await this.lookingLayer(id).then(rv=>{ rv.custom.groupType = str }) if(str == 'Grid'){ await this.setGroupGrid() }else{ await this.setGroupGrid('all') } this.setOperation('movePosition') } async copyLayer(id){ let createId = new Date().getTime() let elements = MyCanvas.canvas.getObjects().filter(obj => obj.custom.layerId == id) let copyElements = [] for (let i = 0; i < elements.length; i++) { await new Promise((resolve, reject) => { let item = elements[i] item.clone((clonedObj)=>{ this.setClone(item,clonedObj) clonedObj.custom.layerId = createId copyElements.push(clonedObj) resolve('') }) }) } await this.createLayer({id:createId}) copyElements.forEach( item=>MyCanvas.canvas.add(item)) } async setGroupGrid(str){//group变为图片 // MyCanvas.canvas.discardActiveObject();//取消所有选中边框 let elements = MyCanvas.canvas.getObjects().filter(obj => this.isAddToLayer(obj) && !obj.custom.dashed && obj.type); if(str == 'all'){ elements = MyCanvas.canvas.getObjects().filter(obj => this.isAddToLayer(obj) && obj.type); } let layerBg = elements.filter(obj => obj.custom.isSelectable)[0] let scaleXY = { scaleX:layerBg?.scaleX?layerBg?.scaleX:1, scaleY:layerBg?.scaleY?layerBg?.scaleY:1, width:layerBg?.width?layerBg?.width:MyCanvas.canvas.width, height:layerBg?.height?layerBg?.height:MyCanvas.canvas.height, } let imgData = await this.groupToImg(elements,{},'setGrid',scaleXY) let img = await this.createImage({minioUrl:imgData}) elements.forEach((obj) =>{ if(obj.custom.isSelectable){ }else{ MyCanvas.canvas.remove(obj) } }); if(layerBg){ layerBg.custom.state = '' delete layerBg.eraser layerBg.set({ scaleX:1, scaleY:1, width: scaleXY.width * scaleXY.scaleX, height: scaleXY.height * scaleXY.scaleY, clipPath:null, fill: new fabric.Pattern({ source: img.getElement(), // 传入图片元素 repeat: 'no-repeat' // 设置图片不重复 }), hasBorders: true, hasControls: true }) this.setGroupStyle(layerBg) }else{ const rect = new fabric.Rect({ // left:0, // top:0, width: MyCanvas.canvas.width, height: MyCanvas.canvas.height, strokeWidth:0, fill: new fabric.Pattern({ source: img.getElement(), // 传入图片元素 repeat: 'no-repeat' // 设置图片不重复 }), custom:{ isSelectable:true, }, hasBorders: true, hasControls: true }); this.setGroupStyle(rect) MyCanvas.canvas.add(rect) } MyCanvas.canvas.renderAll() // 刷新画布,改变group的visible属性,必须通过刷新画布,才能应用新属性值 } //液化 getLiquefactionImgObj(){ return new Promise(async (resolve, reject) => { let img,str = null const activeObject = this.canvas.getActiveObject(); // 获取选中的对象 let dashedGroup = MyCanvas.canvas.getObjects().filter(obj => obj.custom.dashed)[0]; if (activeObject && activeObject.type === 'image' && activeObject.custom.type == 'upImgFiles' ){ img = activeObject this.liquefaction.type = 'upImgFiles' }else if(dashedGroup){//判断选取是否存在 if( dashedGroup._objects.filter(item => item.type == 'image').length == 0 && this.layer.selectLayer.group.custom.groupType == 'Grid' ){ await this.setclipPathImg() }else if(dashedGroup._objects.filter(item => item.type == 'image').length == 0){ resolve() return } await new Promise((resolve, reject) => { dashedGroup.clone(async (cloned)=>{ this.setClone(dashedGroup,cloned) this.liquefaction.dashed = dashedGroup this.liquefaction.type = 'dashed' cloned._objects = cloned._objects.filter(item => item.type == 'image') img = await cloned.toDataURL() resolve() }) }) }else if(this.layer.selectLayer.group.custom.groupType == 'Grid'){//判断当前图层是否可以做为液化图片 let bg = this.canvas.getObjects().filter(item => item.custom?.isSelectable)[0] img = bg.fill.source.src this.liquefaction.type = 'layer' } this.liquefaction.img = img resolve(this.liquefaction) }) } async setLiquefactionImgObj(liquefaction,data){ if(liquefaction.type == 'upImgFiles'){ await new Promise((resolve, reject) => { liquefaction.img.setSrc(data, (value)=>{ delete liquefaction.img.minioUrl resolve() }) }) }else if(liquefaction.type == 'dashed'){ if(liquefaction.dashed){ await new Promise((resolve, reject) => { liquefaction.dashed._objects = liquefaction.dashed._objects.filter(item => item.type != 'image') fabric.Image.fromURL(data, (value)=>{ value.set({ left:-liquefaction.dashed.width/2, top:-liquefaction.dashed.height/2, }) liquefaction.dashed.insertAt(value) resolve() }) }) // canvasObj.addDashedImg(value) } }else if(liquefaction.type == 'layer'){ let bg = this.canvas.getObjects().filter(item => item.custom?.isSelectable)[0] let img = await this.createImage({minioUrl:data}) bg.set({ fill: new fabric.Pattern({ source: img.getElement(), // 传入图片元素 repeat: 'no-repeat' // 设置图片不重复 }), }) } MyCanvas.canvas.renderAll(); this.updateCanvasState() } //end液化 setGroupStyle(obj){ obj.set({ borderColor:'#4584ff', cornerStrokeColor :'#4584ff', cornerStyle:'circle' }) obj.setControlsVisibility({ mtr:false, deleteControl:false }) // obj.controls.deleteControl.mouseUpHandler = null } setClipPath(img,clipPath){ let imgScale = { pX:(img?.scaleX?img.scaleX:1), pY:(img?.scaleY?img.scaleY:1), } clipPath.set({ left: clipPath.left - img.width*imgScale.pX/2, top: clipPath.top - img.height*imgScale.pY/2, inverted:true, }) img.set({clipPath:clipPath}) img.custom.state = 'success' MyCanvas.canvas.renderAll() // 刷新画布,改变group的visible属性,必须通过刷新画布,才能应用新属性值 } async groupToImg(elements,imgData,type,scaleXY){ let temporar = this.temporarilyExport(scaleXY) let bg = elements.filter(item => item.custom?.isSelectable)[0] let xy = { x:bg?.left?bg?.left:0, y:bg?.top?bg?.top:0, } for (let index = 0; index < elements.length; index++) { const item = elements[index]; await new Promise((resolve, reject) => { item.clone((cloned)=>{ this.setClone(item,cloned) if(item.custom?.dashed && item._objects){ cloned._objects = cloned._objects.filter(item => item.type == 'image') } // if(type == 'setGrid'){ cloned.set({ left: cloned.left - xy.x, top: cloned.top - xy.y, }) // } if(cloned.custom?.layerId != -1){ temporar.add(cloned) } resolve() }) }) } let data = temporar.toDataURL(imgData); this.closeTemporarilyExport(temporar) return data } setCanvasWheel(opt){ const delta = opt.e.deltaY // 滚轮,向上滚一下是 -100,向下滚一下是 100 let zoom = MyCanvas.canvas.getZoom() // 获取画布当前缩放值 zoom *= 0.999 ** delta if (zoom > 20) zoom = 20 if (zoom < 0.01) zoom = 0.01 MyCanvas.canvas.zoomToPoint( { // 关键点 x: opt.e.offsetX, y: opt.e.offsetY }, zoom ) opt.e.preventDefault() opt.e.stopPropagation() this.setPencilWidth() } foldEnd(key){ MyCanvas.canvas.skipTargetFind = true let points = this.createPatterning.current.points if(key == 'Enter'){ }else{ points.pop() } points.pop() this.createPatterning.state = false MyCanvas.canvas.remove(this.createPatterning.current) MyCanvas.canvas.remove(this.createPatterning.polyLineBtn) let polyline = new fabric.Polyline(points, { fill: this.operationMode == 'fill'? this.brushwork.color : 'transparent', stroke: this.brushwork.color, strokeWidth:this.brushwork.width[this.operation]?this.brushwork.width[this.operation]:20, // selection:false, }) polyline.set({ left:polyline.left+polyline.strokeWidth / 2, top:polyline.top+polyline.strokeWidth / 2, }) MyCanvas.canvas.add(polyline) this.clearPatterning()//临时图形置为空并且添加撤回对象里面 // this.layerAddElement(polyline) } clearPatterning(){ if(this.createPatterning.state){ MyCanvas.canvas.remove(this.createPatterning.current) } this.createPatterning.current = null MyCanvas.canvas.remove(this.createPatterning.polyLineBtn) this.updateCanvasState() } textureValueChange = (value)=>{ this.texture.value = value this.setTexture() } setOperationMode(str){ this.operationMode = str } brushworkChange = (value)=>{ this.brushwork.value = value this.setPencil() } setLayerIndex(str){//设置优先级 var activeObject = MyCanvas.canvas.getActiveObject(); switch (str) { case 'Front': MyCanvas.canvas.bringToFront(activeObject)//顶层 // this.layer.selectLayer.group.bringToFront(activeObject) break case 'Back': MyCanvas.canvas.sendToBack(activeObject)//底层 // this.layer.selectLayer.group.sendToBack(activeObject) break case 'Forward': MyCanvas.canvas.bringForward(activeObject) // this.layer.selectLayer.group.bringForward(activeObject) break case 'Backwards': MyCanvas.canvas.sendBackwards(activeObject) // this.layer.selectLayer.group.sendBackwards(activeObject) break } } setPencil(){ let pencil MyCanvas.canvas.isDrawingMode = true//开启绘画模式 if(this.brushwork.value == 'PencilBrush'){ pencil = new fabric.PencilBrush(MyCanvas.canvas,{}); //普通笔 }else if(this.brushwork.value == 'Marking'){ pencil = new fabric.PencilBrush(MyCanvas.canvas,); //记号笔 }else if(this.brushwork.value == 'InkBrush'){ pencil = new fabric.InkBrush(MyCanvas.canvas,{}); //油画笔 }else if(this.brushwork.value=='CrayonBrush'){ pencil = new fabric.CrayonBrush(MyCanvas.canvas,{}); //蜡笔 }else if(this.brushwork.value == 'RibbonBrush'){ pencil = new fabric.RibbonBrush(MyCanvas.canvas,{width: 1,}); //色带 }else if(this.brushwork.value == 'MarkerBrush'){ pencil = new fabric.MarkerBrush(MyCanvas.canvas,{}); //书写笔 // pencil = new fabric.PenBrush(MyCanvas.canvas,{}); //书写笔 }else if(this.brushwork.value == 'WritingBrush'){ pencil = new fabric.WritingBrush(MyCanvas.canvas,{}); //毛笔 }else if(this.brushwork.value == 'LongfurBrush'){ pencil = new fabric.LongfurBrush(MyCanvas.canvas,{width: 1,}); //色带 }else if(this.brushwork.value == 'SpraypaintBrush'){ pencil = new fabric.SpraypaintBrush(MyCanvas.canvas,{}); //长毛刷 } MyCanvas.canvas.freeDrawingBrush = pencil if(this.brushwork.value == 'RibbonBrush' || this.brushwork.value == 'LongfurBrush'){ MyCanvas.canvas.freeDrawingBrush.width = 1; } if(this.brushwork.value == 'Marking'){ MyCanvas.canvas.freeDrawingBrush.color = this.hexToRgba(this.brushwork.color,.5); // }else if(this.brushwork.value == 'InkBrush'){ // canvas.freeDrawingBrush.color = this.hexToRgba(this.brushwork.color,.2); }else{ MyCanvas.canvas.freeDrawingBrush.color = this.hexToRgba(this.brushwork.color,1); } this.setPencilWidth() this.pencilbtnStyle.background = this.brushwork.color MyCanvas.canvas.freeDrawingBrush.isEraser = false } setCanvasZoom(opt){ let zoom = MyCanvas.canvas.getZoom() // 获取画布当前缩放值 let num = -100 if(this.operation == 'zoomOut') num = 100 zoom *= 0.999 ** num if (zoom > 20) zoom = 20 if (zoom < 0.01) zoom = 0.01 // MyCanvas.canvas.setDimensions(MyCanvas.canvas.width *= zoom,MyCanvas.canvas.height *= zoom) // MyCanvas.canvas.wrapperEl.style.zoom = zoom MyCanvas.canvas.zoomToPoint( { // 关键点 x: opt.pointer.x, y: opt.pointer.y }, zoom ) opt.e.preventDefault() opt.e.stopPropagation() this.setPencilWidth() } async addImage (imgData){ let img = await this.createImage(imgData) let position = { // left: 0, // top:MyCanvas.canvas.wrapperEl.parentNode.scrollTop, left: MyCanvas.canvas.width/2, top: MyCanvas.canvas.height/2, } img.set({ custom:{ type:'upImgFiles' } }) await this.createLayer({}) this.setCanvasImage(img,position,"upImgFiles",imgData) } createImage(imgData){ return new Promise((resolve, reject) => { fabric.Image.fromURL(imgData.minioUrl,(img) => { resolve(img) },{ crossOrigin: "Anonymous" }) }) } setImageWidth = (key,img)=>{ const CW = MyCanvas.canvas.width const CH = MyCanvas.canvas.height let WH = CW>CH?'H':'W' let imgWidth; //这是设置画布等宽 if(WH == 'W'){ imgWidth = CW / 2; }else{ let heightScale = (CH/2) / img.height imgWidth = img.width * heightScale } // let sketchGrouping = 6 // if(key == 'upImgFiles'){ // imgWidth = imgWidth / 8; // }else if (key == "printboardFiles") { // imgWidth = imgWidth / 14; // }else if (key == "sketchboardFiles"||key == 'moodboardFiles'||key == 'FinalizeImage') { // imgWidth = // (imgWidth - // (sketchGrouping - 1) * 20) / // sketchGrouping; // }else if (key == "likeDesignCollectionList") { // if(img){ // let imgObj = JSON.parse(JSON.stringify(img)) // let height = imgObj.height // imgObj.height = imgWidth / 8 * 1.8 // let heightScale = imgObj.height / height // imgWidth = imgObj.width * heightScale // } // }else { // if(img){ // let imgObj = JSON.parse(JSON.stringify(img)) // if(imgObj.height > imgObj.width){ // let heightScale = (MyCanvas.canvas.height/2) / imgObj.height // imgWidth = imgObj.width * heightScale // }else{ // imgWidth /= 2 // } // } // } return imgWidth } async setCanvasImage(img,position,key,data){ let minioUrl = ''//表示收藏或者generate let imgUrl = data.imgUrl if (key == "likeDesignCollectionList") { imgUrl = data.designOutfitUrl; } let imgWidth = await this.setImageWidth(key,img); if(imgUrl?.split('?')){ // let url = imgUrl.split('?')[0] // var match = url.match(/^(?:https?:\/\/[^\/]+)\/(.*)/); // minioUrl = match[1] const { pathname } = new URL(imgUrl); const result = pathname.slice(1); minioUrl = result } let proportion = img.height / img.width; //计算图形宽高比例 let scaleWH = imgWidth / img.width; //计算放到画布上缩小倍率 let scale = { X: imgWidth / img.width, Y: (img.width * proportion * scaleWH) / img.height, } img.set({ left:position.left - img.width*scale.X/2, top:position.top - img.height*scale.Y / 2, minioUrl, scaleX:scale.X, scaleY:scale.Y, }); MyCanvas.canvas.add(img) this.updateCanvasState('') } setMove(){ MyCanvas.canvas.isDrawingMode = false MyCanvas.canvas.forEachObject((obj) =>{ if(this.isSelectable(obj)){ obj.selectable = true }else{ obj.selectable = false } }); MyCanvas.canvas.renderAll() // 刷新画布,改变group的visible属性,必须通过刷新画布,才能应用新属性值 } async setTexture(){ MyCanvas.canvas.isDrawingMode = true//开启绘画模式 let img = await JSSetTexture(this.texture.list[this.texture.value].url) let zoom = MyCanvas.canvas.getZoom() // 获取画布当前缩放值 let patternBrush = new fabric.PatternBrush(MyCanvas.canvas) patternBrush.source = img patternBrush.width = Number(this.brushwork.width[this.operation]) / zoom; // 设置画笔大小 this.setPencilWidth() MyCanvas.canvas.freeDrawingBrush = patternBrush } setEraser(){ MyCanvas.canvas.isDrawingMode = true let eraser = new fabric.EraserBrush(MyCanvas.canvas) MyCanvas.canvas.freeDrawingBrush = eraser this.pencilbtnStyle.background = '#fff' MyCanvas.canvas.freeDrawingBrush.isEraser = true this.setPencilWidth() MyCanvas.canvas.requestRenderAll(); } hexToRgba(hex, alpha){ const r = parseInt(hex.slice(1, 3), 16); const g = parseInt(hex.slice(3, 5), 16); const b = parseInt(hex.slice(5, 7), 16); return `rgba(${r}, ${g}, ${b}, ${alpha})`; } pencilColor(){ if(this.createText?.set){ this.setFontFamily() return } if(MyCanvas.canvas.freeDrawingBrush.isEraser){ }else{ this.pencilbtnStyle.background = this.brushwork.color } if(this.brushwork.value == 'Marking'){ MyCanvas.canvas.freeDrawingBrush.color = this.hexToRgba(this.brushwork.color,.5); }else if(this.brushwork.value == 'InkBrush'){ MyCanvas.canvas.freeDrawingBrush.color = this.hexToRgba(this.brushwork.color,.2); }else{ MyCanvas.canvas.freeDrawingBrush.color = this.hexToRgba(this.brushwork.color,1); } } setFontFamily=()=>{ if(this.createText.set){ this.createText.set({ fontFamily:this.fontFamily, fontSize:this.brushwork.width[this.operation]?this.brushwork.width[this.operation]:20, fill:this.brushwork.color, }) MyCanvas.canvas.renderAll(); } } setPencilColor(){//切换颜色给铅笔设置颜色 clearTimeout(this.setTimeOut.color) clearTimeout(this.setTimeOut.colorHistory) this.setTimeOut.color = setTimeout(()=>{ this.pencilColor() },200) this.setTimeOut.colorHistory = setTimeout(()=>{ this.colorHistoryList.push(this.brushwork.color) },1000) } setCanvasWH = (str)=>{ clearTimeout(this.setTimeOut.canvasWH) this.setTimeOut.canvasWH = setTimeout(()=>{ MyCanvas.canvas.setWidth(this.canvasWH.width); MyCanvas.canvas.setHeight(this.canvasWH.height); // MyCanvas.canvas.width = this.canvasWH.width // MyCanvas.canvas.height = this.canvasWH.height this.initCanvasWH('updata') },1000) } setColorHistory = (value)=>{ this.brushwork.color = value this.pencilColor() } setPencilWidth(){//切换颜色给铅笔设置颜色 let zoom = MyCanvas.canvas.getZoom() // 获取画布当前缩放值 clearTimeout(this.setTimeOut.width) this.setTimeOut.width = setTimeout(()=>{ if(!this.brushwork.width[this.operation])this.brushwork.width[this.operation]=20 // MyCanvas.canvas.freeDrawingBrush.width = Number(this.brushwork.width[this.operation]); let arr = ['PencilBrush','Marking'] this.pencilbtnStyle.height = this.brushwork.width[this.operation]+'px' this.pencilbtnStyle.width = this.brushwork.width[this.operation]+'px' if(arr.indexOf(this.brushwork.value) > -1){ MyCanvas.canvas.freeDrawingBrush.width = Number(this.brushwork.width[this.operation]) / zoom; }else{ MyCanvas.canvas.freeDrawingBrush.width = Number(this.brushwork.width[this.operation]); } if((this.brushwork.value == 'LongfurBrush' || this.brushwork.value == 'RibbonBrush') && this.operation == 'pencil'){ MyCanvas.canvas.freeDrawingBrush.width = 1; this.pencilbtnStyle.height = 1+'px' this.pencilbtnStyle.width = 1+'px' } },100) } //关闭u或者创建文字 setTextFun(e){ if(this.operation != 'text'){ return } MyCanvas.canvas.forEachObject((obj) =>{ if(obj.type == 'textbox' && !obj.text){ MyCanvas.canvas.remove(obj); } }); var clickedObject = e.target; if (clickedObject instanceof fabric.Textbox) { this.createText = clickedObject this.setTextData(this.createText) }else{ var pointer = MyCanvas.canvas.getPointer(e.pointer); var x = pointer.x; var y = pointer.y; this.createText = new fabric.Textbox('', { left: x, top: y, width: 50, fontSize: this.brushwork.width[this.operation]?this.brushwork.width[this.operation]:20, fontFamily:this.fontFamily, fill:this.canvasPencilColor, }) MyCanvas.canvas.add(this.createText) // this.layer.selectLayer.group.add(this.createText) this.createText.enterEditing(); MyCanvas.canvas.setActiveObject(this.createText).renderAll(); this.removeSetText() } } setText(){ MyCanvas.canvas.on('mouse:down',(event)=>{ if(this.operation == 'zoomIn' || this.operation == 'zoomOut'){ }else{ this.setTextFun(event) } }) } removeSetText(){ MyCanvas.canvas.off('mouse:down',this.setTextFun.bind(this)) } // async createBg(){// // await new Promise((resolve, reject) => { // let url = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC' // fabric.Image.fromURL(url, (backgroundImage)=>{ // backgroundImage.set({ // }) // const rect = new fabric.Rect({ // left: 0, // top: 0, // width: MyCanvas.canvas.width, // height: MyCanvas.canvas.height, // fill:'#FFF', // // fill: new fabric.Pattern({ // // source: backgroundImage.getElement(), // 使用图像元素作为填充 // // repeat: 'repeat', // 设置为重复 // // patternTransform: [this.canvasWH.width/this.canvasDomParent.offsetWidth, 0, 0, this.canvasWH.width/this.canvasDomParent.offsetWidth, 0, 0] // // }), // }); // const group = new fabric.Group([rect], { // width: MyCanvas.canvas.width, // height: MyCanvas.canvas.height, // erasable:false, // selectable:false, // }); // group.custom.layerId = -1 // MyCanvas.canvas.add(group) // MyCanvas.canvas.clipPath = rect // resolve() // }); // }) // } createLayer(data){ let {str,id} = data let canvasData = MyCanvas.canvas.toJSON(['selectable','minioUrl','custom']); return new Promise(async (resolve, reject) => { MyCanvas.canvas.discardActiveObject(); if(!id)id = new Date().getTime() let index = this.layer.currentIndex += 1 const rect = new fabric.Rect({ left: -MyCanvas.canvas.width / 2, top: -MyCanvas.canvas.height / 2, // left:0, // top:0, width: MyCanvas.canvas.width, height: MyCanvas.canvas.height, strokeWidth:0, fill:'#FFF', custom:{ layerId:id, isSelectable:true, }, // erasable:false, selectable:false, }); rect.clone((cloned)=>{ rect.clipPath = cloned }) const group = new fabric.Group([], { width: MyCanvas.canvas.width, height: MyCanvas.canvas.height, left:0, top:0, selectable:false, erasable:false, subTargetCheck:true, evented:false, // originX:'left', // originY:'top', custom:{ layerId:id, type:'layer' , groupType:'Object', layerIndex:index, selectLayerIndex:(index) * 10000, } }); if(str == 'init'){ group.moveTo(group.custom.selectLayerIndex) group.add(rect) group.clone((cloned)=>{ cloned.set({left:0,top:0}) MyCanvas.canvas.clipPath = cloned }) } MyCanvas.canvas.add(group) let img = group.toDataURL({ width: MyCanvas.canvas.width, height: MyCanvas.canvas.height, }) this.layer.list.unshift({ name:`Layer${index}`, id,img,isShow:true, groupType:'Object', index,}) await this.selectLayer(id) MyCanvas.canvas.renderAll(); this.updateCanvasState() resolve(group) }) } layerAddElement(obj){ let group = this.layer.selectLayer.group obj.left = obj.left - group.width/2 obj.top = obj.top - group.height/2 group.add(obj) MyCanvas.canvas.remove(obj) this.updataLayerImg(this.layer.selectLayer.id,group.toDataURL({ width: MyCanvas.canvas.width, height: MyCanvas.canvas.height })) } async addLayer(options){ console.log(options); if(this.createPatterning.state)return this.clipPath.clipGroup = MyCanvas.canvas.getObjects().filter(obj => obj.custom?.dashed)?.[0]; if (options.target.type === 'group' && options.target.custom?.layerId) return; if(!options.target?.custom?.layerId){ if(options.target?.custom){ options.target.custom.layerId = this.layer.selectLayer.id }else{ options.target.set({custom:{layerId : this.layer.selectLayer.id}}) } } //判断是否可以点击空白地方进行选中 if(options.target?.custom?.dashed){ options.target.set({perPixelTargetFind:false,erasable:false,hasBorders: false,hasControls: false}) // MyCanvas.canvas.setActiveObject(options.target); MyCanvas.canvas.skipTargetFind = false this.setOperation('movePosition') }else if(options.target.type == 'textbox' || options.target?.custom.type == 'layer'){ options.target.set({perPixelTargetFind:false}) }else{ options.target.set({perPixelTargetFind:true}) } //设置优先级 if(options.target?.custom?.isSelectable && (options.target?.type == 'rect')){ options.target.custom.selectLayerIndex = this.layer.selectLayer.group.custom.layerIndex * 10000 }else{ options.target.custom.selectLayerIndex = this.layer.selectLayer.group.custom.selectLayerIndex += 1 if(this.clipPath.clipGroup){ this.clipPath.clipGroup.custom.selectLayerIndex = this.layer.selectLayer.group.custom.selectLayerIndex + 1 } } if(options.target.isType('path'))options.target.selectable=false let arr = MyCanvas.canvas.getObjects().sort((a, b) =>{ return a.custom.selectLayerIndex - b.custom.selectLayerIndex; }); arr.forEach((item,index)=>{ item.moveTo(index) }) if(!options?.target?.custom?.dashed && this.layer.selectLayer.group.custom.groupType == 'Grid'){ await new Promise((resolve, reject) => { console.log(this.clipPath.clipGroup); let clipPathElement = this.clipPath.clipGroup._objects.filter(obj => obj.custom?.dashed)[0] let clipPathLect = this.clipPath.clipGroup.left let clipPathTop = this.clipPath.clipGroup.top clipPathElement.clone((clipPathElementCloned)=>{ clipPathElementCloned.set({ // left:this.clipPath.clipGroup.left, // top:this.clipPath.clipGroup.top, left:clipPathLect, top:clipPathTop, absolutePositioned:true, }) let optionLect = (options.target.left - this.clipPath.clipGroup.left) - this.clipPath.clipGroup.width/2 let optionTop = (options.target.top - this.clipPath.clipGroup.top) - this.clipPath.clipGroup.height/2 options.target.clone((cloned)=>{ cloned.set({ left:optionLect, top:optionTop, clipPath:clipPathElementCloned, }) this.clipPath.clipGroup.add(cloned) console.log(this.clipPath.clipGroup); MyCanvas.canvas.remove(options.target) resolve() }) }) }) // this.setGroupGrid('all') } MyCanvas.canvas.renderAll(); this.updataLayer() } upLayerIndex(list){ list.forEach((item,index)=>{ let arr = MyCanvas.canvas.getObjects().filter(obj => obj.custom.layerId == item.id) arr.forEach((obj)=>{ if(obj.custom.type == 'layer')obj.custom.layerIndex = item.index let selectLayerIndex = obj.custom.selectLayerIndex+'' selectLayerIndex = selectLayerIndex.replace(/^\d/, item.index); obj.custom.selectLayerIndex = Number(selectLayerIndex) }) // arrList.unshift(obj) }) // this.layer.list = arrList let arr = MyCanvas.canvas.getObjects().sort((a, b) =>{ return a.custom.selectLayerIndex - b.custom.selectLayerIndex; }); arr.forEach((item,index)=>item.moveTo(index)) } updataLayer(){ clearTimeout(this.setTimeOut.updataLayer) this.setTimeOut.updataLayer = setTimeout(async()=>{ let elements elements = MyCanvas.canvas.getObjects().filter(obj => { return this.isAddToLayer(obj) }); const group = new fabric.Group([], { width: this.layer.selectLayer.group.width, height: this.layer.selectLayer.group.height, erasable:false, selectable:false, }); for (let i = 0; i < elements.length; i++) { await new Promise((resolve, reject) => { let item = elements[i] item.clone((clonedObj)=>{ this.setClone(item,clonedObj) clonedObj.left = clonedObj.left - this.layer.selectLayer.group.width/2 clonedObj.top = clonedObj.top - this.layer.selectLayer.group.height/2 if(clonedObj.custom?.dashed){ let clipPath = clonedObj._objects.filter(item => item.type != 'image')[0] clonedObj.remove(clipPath) } group.add(clonedObj) resolve('') }) }) } this.updataLayerImg(this.layer.selectLayer.id,group.toDataURL({ width: this.layer.selectLayer.group.width, height: this.layer.selectLayer.group.height }) ) },500) } updataLayerImg(id,img){ this.layer.list.forEach(v => { if (v.id === id) { v.img = img } }) } async layerDelete(index,id){ if(this.layer.list.length == 1)return for (let i = index; i < this.layer.list.length; i++) { this.layer.list[this.layer.list.length - i-1].index-- } this.layer.list.splice(index,1) if(id == this.layer.selectLayer.id){ MyCanvas.canvas.remove(this.layer.selectLayer.group) MyCanvas.canvas.forEachObject((obj) =>{ if(obj.type != 'group' && obj.custom?.layerId == id){ MyCanvas.canvas.remove(obj) } }); this.layer.selectLayer.id = -1 this.selectLayer(this.layer.list[this.layer.list.length - 1].id) }else{ let group = await this.lookingLayer(id,'setLayerIndex') MyCanvas.canvas.remove(group) } this.updateCanvasState() } async selectLayer(id){ await new Promise(async (resolve, reject) => { if(this.layer.selectLayer.id != -1){ await this.mergeGroup() } let group = await this.lookingLayer(id,'setRubber') group.custom.selectLayerIndex = group.custom.layerIndex*10000 this.layer.selectLayer.group = group this.layer.selectLayer.id = id await this.expandGroup() MyCanvas.canvas.renderAll() // 刷新画布,改变group的visible属性,必须通过刷新画布,才能应用新属性值 this.setOperation('movePosition') resolve('') }) } async mergeGroup(){ const elements = MyCanvas.canvas.getObjects().filter(obj => this.isAddToLayer(obj)); let isClipPath = MyCanvas.canvas.getObjects().filter(obj => obj.custom.dashed); if(isClipPath.length>0)await this.setGroupGrid('all') let group = await this.lookingLayer(this.layer.selectLayer.id,'setlayerIndex') group.set({visible:true}) for (let index = 0; index < elements.length; index++) { const item = elements[index]; await new Promise((resolve, reject) => { item.clone((v)=>{ this.setClone(item,v) v.left = v.left - group.width/2 v.top = v.top - group.height/2 if(v.custom?.dashed){ // MyCanvas.canvas.remove(item) // let img = v._objects.filter(item => item.type == 'image')[0] // if(img){ // img.set({ // left:v.left, // top:v.top, // custom:{ // layerId:v.custom.layerId // } // }) // group.add(img) // } }else{ group.add(v) } resolve('') }) }) } elements.forEach(v => MyCanvas.canvas.remove(v)) } async expandGroup(){ const elements = this.layer.selectLayer.group._objects.filter(obj => this.isAddToLayer(obj)); // fabric.util.enlivenObjects(elements, (clonedObjects) => { this.layer.selectLayer.group.set({visible:false}) for (let index = 0; index < elements.length; index++) { const item = elements[index]; await new Promise((resolve, reject) => { item.clone((v)=>{ this.setClone(item,v) v.left = v.left + this.layer.selectLayer.group.width/2 v.top = v.top + this.layer.selectLayer.group.height/2 if(v.custom?.dashed){ // this.layer.selectLayer.group._objects.remove(item) // let img = v._objects.filter(item => item.type == 'image')[0] // if(img){ // img.set({ // left:v.left, // top:v.top, // custom:{ // layerId:v.custom.layerId // } // }) // MyCanvas.canvas.add(img) // } }else{ MyCanvas.canvas.add(v) } if(index == elements.length - 1){ elements.forEach(v => this.layer.selectLayer.group.remove(v)) } resolve() }) }) } } lookingLayer(id,str){ return new Promise((resolve, reject) => { const groups = MyCanvas.canvas.getObjects() let group = null groups.forEach(v => { if(str && v?.custom?.type === 'layer' && (str == 'setlayerIndex' || str == 'setRubber')){ let layerIndex = v?.custom?.layerIndex * 10000 v.moveTo(layerIndex) } if(v?.custom?.layerId == id){ if(str && str == 'setRubber'){ v.set({erasable:'deep'}) } }else if(v?.custom?.type === 'layer'){ v.set({erasable:false}) } if (v?.custom?.layerId === id && v?.custom?.type === 'layer') { group = v } }) resolve(group) }) } canvasKeyUp(event){ this.keyDown = this.keyDown.filter((item) => { if(item.code !== 'AltLeft'){ if(this.oldOperation)this.setOperation(this.oldOperation) } return event.code !== item; }) } async layerShowHide(id,item){ // console.log(id); item.isShow = !item.isShow MyCanvas.canvas.forEachObject((obj)=>{ if(obj.custom.layerId == id)obj.visible = item.isShow }) MyCanvas.canvas.renderAll() // 刷新画布,改变group的visible属性,必须通过刷新画布,才能应用新属性值 } canvasKeyDown(event){ if(this.keyDown.indexOf(event.key)>-1){ }else{ this.keyDown.push(event.code) if(event.key === 'Enter' && this.operation == 'fold'){ this.foldEnd('Enter') }else if(event.key === 'Delete'){ this.deleteObject() }else if(this.keyDown.indexOf('ControlLeft') > -1 && this.keyDown.indexOf('KeyZ') > -1 && this.keyDown.indexOf('ShiftLeft') > -1){ this.historyState('reverse') }else if(this.keyDown.indexOf('ControlLeft') > -1 && this.keyDown.indexOf('KeyZ') > -1){ this.historyState('') }else if(this.keyDown.indexOf('ControlLeft') > -1 && this.keyDown.indexOf('KeyC') > -1){ this.copy() }else if(this.keyDown.indexOf('ControlLeft') > -1 && this.keyDown.indexOf('KeyV') > -1){ this.paste() }else if(this.keyDown.indexOf('AltLeft') > -1 && this.keyDown.length == 1 && this.operation != 'move'){ this.oldOperation = this.operation this.setOperation('move') } } } canvasClear(){ if(MyCanvas.canvas?.dispose)MyCanvas.canvas.dispose() let oldCanvasDom = this.canvasDomParent?.querySelector('.canvas-container') let oldCanvasDom1 = this.canvasDomParent?.querySelector('canvas') if(oldCanvasDom)oldCanvasDom.remove() if(oldCanvasDom1)oldCanvasDom1.remove() this.reverseCanvasState=[];//撤回 this.normalCanvasState=[];//反撤回 MyCanvas.canvas = null this.canvas = null this.layer = { list:[], selectLayer:{ group:null, id:-1, }, currentIndex: 0, } this.mouse = { point:null, upPoint:null, isMovePostion:false, lastPosX:0, lastPosY:0, } this.brushwork = { value:'PencilBrush', width:{}, color:'#000000', texture:0, } this.operation = 'movePosition' document.removeEventListener("keydown", this.canvasKeyDown); document.removeEventListener("keyup", this.canvasKeyUp); document.removeEventListener('mousemove', this.mouseMove); document.removeEventListener('touchmove', this.touchmove); } //删除选中元素 deleteObject(){ if(!MyCanvas.canvas.getActiveObjects()){ return } let target = MyCanvas.canvas.getActiveObjects() target.forEach((item)=>{ MyCanvas.canvas.fxRemove(item, { onComplete:()=>{ MyCanvas.canvas.discardActiveObject(); // 丢弃当前选中的对象 MyCanvas.canvas.renderAll(); // 重新渲染 Canvas } }) MyCanvas.canvas.FX_DURATION = 200 // MyCanvas.canvas.remove(item) }) this.updataLayer() this.updateCanvasState('remove') } async getDashedImg(cloned){ let position = { left:cloned?.left, top:cloned?.top, width:cloned?.width + cloned?.strokeWidth*2, height:cloned?.height + cloned?.strokeWidth*2, } let elements = MyCanvas.canvas.getObjects().filter(obj => this.isAddToLayer(obj)); let imgData = await this.groupToImg(elements,position,'clipPath')//矩形裁剪图片 let img = await this.createImage({minioUrl:imgData}) // cloned._objects = cloned._objects.filter(item => item.type == 'image') let clipPathElement = cloned._objects.filter(obj => obj.type != 'image')[0] cloned.set({ left:0, top:0, visible:true }) img.set({ left:position.left, top:position.top, clipPath:clipPathElement, }) return img } //复制粘贴 async copy(canvas){ var activeObject=null; if(MyCanvas.canvas.getActiveObject()){ activeObject = MyCanvas.canvas.getActiveObject() }else{ activeObject = MyCanvas.canvas.getObjects().filter(obj => obj.custom.dashed)[0]; } if(!activeObject){ return } let copyObj = null await new Promise((resolve, reject) => { activeObject.clone(async (cloned)=>{ this.setClone(activeObject,cloned) if(cloned.custom.dashed){ copyObj = await this.getDashedImg(cloned) }else{ copyObj = cloned } resolve() }) }) this._clipboard = copyObj; } async paste(){//粘贴 if(!this._clipboard){ return } await this.createLayer({}) this._clipboard.clone(clonedObj => { // this.setClone(this._clipboard,clonedObj) MyCanvas.canvas.discardActiveObject() // 取消选择 clonedObj.set({ left: clonedObj.left, top: clonedObj.top, selectable: true }) if (clonedObj.type === 'activeSelection') { // 活动选择需要一个对画布的引用 clonedObj.forEachObject((obj)=>{ MyCanvas.canvas.add(obj) }) clonedObj.setCoords() } else { MyCanvas.canvas.add(clonedObj) // this.layer.selectLayer.group.add(clonedObj) } MyCanvas.canvas.setActiveObject(clonedObj) MyCanvas.canvas.requestRenderAll() }) this._clipboard = null this.updateCanvasState() } isSelectable(obj){//判断元素是否允许选中 return (obj.custom?.type != 'layer' && (!obj.custom?.isSelectable || this.layer.selectLayer.group.custom.groupType == 'Grid') && !obj.isType('path')) //isSelectable已废弃,现在isSelectable作为判断是否是背景来使用 return (obj.custom?.type != 'layer' && !obj.isType('path')) } isAddToLayer(obj){//判断元素是否允许添加 return(obj.custom?.type != 'layer' && obj.custom?.layerId == this.layer.selectLayer.id) } //撤回反撤回 //设置画布监听修改添加事件,用来做撤回功能 updateCanvasState(str){ let canvasData = MyCanvas.canvas.toJSON(['custom','selectable','minioUrl','perPixelTargetFind','subTargetCheck','evented']); if(canvasData.clipPath){ canvasData.clipPath.left = 0 canvasData.clipPath.top = 0 } let canvasAsJson = JSON.stringify(canvasData) let layerList = JSON.stringify(this.layer.list) let layerId = this.layer.selectLayer.id if(str == 'loadingCompleted'){ // this.reverseCanvasState.push(canvasAsJson); } this.normalCanvasState.push({canvasAsJson,layerList,layerId}); if (this.isLoadCanvas) { this.reverseCanvasState = [] this.isLoadCanvas = false; this.canvasState = canvasAsJson } } //撤回 async historyState(str){ if(str == 'reverse' && this.reverseCanvasState.length > 0){//反撤回 let obj = this.reverseCanvasState.pop() let layerList = JSON.parse(obj.layerList) this.layer.list = layerList this.layer.selectLayer.id = obj.layerId this.canvasState = obj.canvasAsJson this.normalCanvasState.push(obj); }else if(str == '' && this.normalCanvasState.length > 1){ let obj = this.normalCanvasState.pop() let select = this.normalCanvasState[this.normalCanvasState.length-1] this.canvasState = select.canvasAsJson this.layer.list = JSON.parse(select.layerList) this.layer.selectLayer.id = select.layerId this.reverseCanvasState.push(obj); this.isLoadCanvas = true; }else{ return } MyCanvas.canvas.loadFromJSON(this.canvasState, () => {}); MyCanvas.canvas.setBackgroundColor({ source:'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC', repeat: 'repeat', },MyCanvas.canvas.renderAll.bind(MyCanvas.canvas), ) } temporarilyExport(data){ let scaleXY = { scaleX:data?.scaleX?data?.scaleX:1, scaleY:data?.scaleY?data?.scaleY:1, } let width = data?.width || MyCanvas.canvas.width let height = data?.height || MyCanvas.canvas.height var canvasDom = document.createElement("canvas"); let exportCanvas = new fabric.Canvas(canvasDom, { width:width*scaleXY.scaleX, height:height*scaleXY.scaleY, scaleX:scaleXY.scaleX, scaleY:scaleXY.scaleY, isDrawingMode: false, // 开启绘图模式 }); return exportCanvas } closeTemporarilyExport(temporar){ if(temporar){ temporar.wrapperEl.remove() temporar.dispose() } } setClone(obj,goal){ if(obj.custom){ goal.set({custom:JSON.parse(JSON.stringify(obj.custom))}) } goal.perPixelTargetFind = obj.perPixelTargetFind goal.hasBorders = obj.hasBorders goal.hasControls = obj.hasControls goal.selectable = obj.selectable goal.erasable = obj.erasable if(obj.minioUrl)goal.minioUrl = obj.minioUrl } //导出 selectExport(){ let layer = this.layer.list.filter(item=>item.id == this.layer.selectLayer.id)[0] return layer.img } allExport(){ let exportCanvas = this.temporarilyExport() MyCanvas.canvas.forEachObject((obj) =>{ if(obj.custom?.layerId != -1){ obj.clone((cloned)=>{ this.setClone(obj,cloned) exportCanvas.add(cloned) }) } }); let data = exportCanvas.toDataURL("image/png"); this.closeTemporarilyExport(exportCanvas) return data } } export default new MyCanvas()