|
| 1 | +fabric.DoubleOutlineBrush=fabric.util.createClass(fabric.BaseBrush,{ |
| 2 | +type: "DoubleOutlineBrush", |
| 3 | +initialize: function(canvas){ |
| 4 | +this.canvas=canvas; |
| 5 | +this.color="#FFFFFF"; |
| 6 | +this.width=10; |
| 7 | +this.outline1Color="#000000"; |
| 8 | +this.outline1Width=2; |
| 9 | +this.outline1Opacity=1; |
| 10 | +this.outline2Color="#FF0000"; |
| 11 | +this.outline2Width=1; |
| 12 | +this.outline2Opacity=1; |
| 13 | +this.isDrawing=false; |
| 14 | +this.currentPathData=null; |
| 15 | +this.points=[]; |
| 16 | +this.pathDataArray=[]; |
| 17 | +this.outlineImage=null; |
| 18 | +this.offscreenCanvas=null; |
| 19 | +this.offscreenCtx=null; |
| 20 | +}, |
| 21 | +_initOffscreen: function(){ |
| 22 | +if(!this.offscreenCanvas){ |
| 23 | +this.offscreenCanvas=document.createElement("canvas"); |
| 24 | +this.offscreenCanvas.width=this.canvas.width; |
| 25 | +this.offscreenCanvas.height=this.canvas.height; |
| 26 | +this.offscreenCtx=this.offscreenCanvas.getContext("2d"); |
| 27 | +} |
| 28 | +}, |
| 29 | +onMouseDown: function(pointer){ |
| 30 | +this._initOffscreen(); |
| 31 | +this.isDrawing=true; |
| 32 | +this.points=[pointer]; |
| 33 | +this.currentPathData={ |
| 34 | +path:[["M",pointer.x,pointer.y],["L",pointer.x,pointer.y]], |
| 35 | +stroke: this.color, |
| 36 | +strokeWidth: this.width, |
| 37 | +outline1Color: this.outline1Color, |
| 38 | +outline1Width: this.outline1Width, |
| 39 | +outline1Opacity: this.outline1Opacity, |
| 40 | +outline2Color: this.outline2Color, |
| 41 | +outline2Width: this.outline2Width, |
| 42 | +outline2Opacity: this.outline2Opacity, |
| 43 | +}; |
| 44 | +this._render(); |
| 45 | +}, |
| 46 | +onMouseMove: function(pointer){ |
| 47 | +if(this.isDrawing&&this.currentPathData){ |
| 48 | +this.points.push(pointer); |
| 49 | +if(this.points.length>3){ |
| 50 | +var lastIndex=this.points.length-1; |
| 51 | +var controlX=(this.points[lastIndex].x+this.points[lastIndex-1].x)/2; |
| 52 | +var controlY=(this.points[lastIndex].y+this.points[lastIndex-1].y)/2; |
| 53 | +this.currentPathData.path.push(["Q",this.points[lastIndex-1].x,this.points[lastIndex-1].y,controlX,controlY]); |
| 54 | +this.points.shift(); |
| 55 | +}else{ |
| 56 | +this.currentPathData.path[1]=["L",pointer.x,pointer.y]; |
| 57 | +} |
| 58 | +this._render(); |
| 59 | +} |
| 60 | +}, |
| 61 | +onMouseUp: function(){ |
| 62 | +if(this.isDrawing){ |
| 63 | +this.isDrawing=false; |
| 64 | +this.pathDataArray.push(this.currentPathData); |
| 65 | +this.currentPathData=null; |
| 66 | +this.points=[]; |
| 67 | +this._processOutlines(); |
| 68 | +} |
| 69 | +}, |
| 70 | +_render: function(){ |
| 71 | +this.offscreenCtx.clearRect(0,0,this.offscreenCanvas.width,this.offscreenCanvas.height); |
| 72 | +var allPaths=this.pathDataArray.slice(); |
| 73 | +if(this.currentPathData){ |
| 74 | +allPaths.push(this.currentPathData); |
| 75 | +} |
| 76 | +var self=this; |
| 77 | +allPaths.forEach(function(pathData){ |
| 78 | +if(pathData.outline2Width>0){ |
| 79 | +self._drawStroke(pathData,self.offscreenCtx,pathData.strokeWidth+2*pathData.outline1Width+2*pathData.outline2Width,pathData.outline2Color,pathData.outline2Opacity); |
| 80 | +} |
| 81 | +}); |
| 82 | +allPaths.forEach(function(pathData){ |
| 83 | +if(pathData.outline1Width>0){ |
| 84 | +self._drawStroke(pathData,self.offscreenCtx,pathData.strokeWidth+2*pathData.outline1Width,pathData.outline1Color,pathData.outline1Opacity); |
| 85 | +} |
| 86 | +}); |
| 87 | +allPaths.forEach(function(pathData){ |
| 88 | +self._drawStroke(pathData,self.offscreenCtx,pathData.strokeWidth,pathData.stroke,1); |
| 89 | +}); |
| 90 | +var ctx=this.canvas.contextTop; |
| 91 | +ctx.clearRect(0,0,this.canvas.width,this.canvas.height); |
| 92 | +ctx.drawImage(this.offscreenCanvas,0,0); |
| 93 | +}, |
| 94 | +_drawStroke: function(pathData,ctx,width,color,opacity){ |
| 95 | +ctx.save(); |
| 96 | +ctx.beginPath(); |
| 97 | +ctx.strokeStyle=color; |
| 98 | +ctx.globalAlpha=opacity; |
| 99 | +ctx.lineWidth=width; |
| 100 | +ctx.lineCap="round"; |
| 101 | +ctx.lineJoin="round"; |
| 102 | +pathData.path.forEach(function(segment){ |
| 103 | +if(segment[0]==="M"){ |
| 104 | +ctx.moveTo(segment[1],segment[2]); |
| 105 | +}else if(segment[0]==="L"){ |
| 106 | +ctx.lineTo(segment[1],segment[2]); |
| 107 | +}else if(segment[0]==="Q"){ |
| 108 | +ctx.quadraticCurveTo(segment[1],segment[2],segment[3],segment[4]); |
| 109 | +} |
| 110 | +}); |
| 111 | +if(pathData.path.length===1){ |
| 112 | +ctx.lineTo(pathData.path[0][1],pathData.path[0][2]); |
| 113 | +} |
| 114 | +ctx.stroke(); |
| 115 | +ctx.restore(); |
| 116 | +}, |
| 117 | +_processOutlines: function(){ |
| 118 | +this.offscreenCtx.clearRect(0,0,this.offscreenCanvas.width,this.offscreenCanvas.height); |
| 119 | +var self=this; |
| 120 | +this.pathDataArray.forEach(function(pathData){ |
| 121 | +if(pathData.outline2Width>0){ |
| 122 | +self._drawStroke(pathData,self.offscreenCtx,pathData.strokeWidth+2*pathData.outline1Width+2*pathData.outline2Width,pathData.outline2Color,pathData.outline2Opacity); |
| 123 | +} |
| 124 | +}); |
| 125 | +this.pathDataArray.forEach(function(pathData){ |
| 126 | +if(pathData.outline1Width>0){ |
| 127 | +self._drawStroke(pathData,self.offscreenCtx,pathData.strokeWidth+2*pathData.outline1Width,pathData.outline1Color,pathData.outline1Opacity); |
| 128 | +} |
| 129 | +}); |
| 130 | +this.pathDataArray.forEach(function(pathData){ |
| 131 | +self._drawStroke(pathData,self.offscreenCtx,pathData.strokeWidth,pathData.stroke,1); |
| 132 | +}); |
| 133 | +if(this.outlineImage){ |
| 134 | +this.canvas.remove(this.outlineImage); |
| 135 | +} |
| 136 | +var tempCanvas=document.createElement("canvas"); |
| 137 | +tempCanvas.width=this.offscreenCanvas.width; |
| 138 | +tempCanvas.height=this.offscreenCanvas.height; |
| 139 | +tempCanvas.getContext("2d").drawImage(this.offscreenCanvas,0,0); |
| 140 | +this.outlineImage=new fabric.Image(tempCanvas,{ |
| 141 | +left: 0, |
| 142 | +top: 0, |
| 143 | +selectable: false, |
| 144 | +evented: false, |
| 145 | +}); |
| 146 | +this.canvas.add(this.outlineImage); |
| 147 | +this.canvas.contextTop.clearRect(0,0,this.canvas.width,this.canvas.height); |
| 148 | +this.canvas.renderAll(); |
| 149 | +}, |
| 150 | +mergeDrawings: function(){ |
| 151 | +if(this.pathDataArray.length===0){ |
| 152 | +if(this.outlineImage){ |
| 153 | +this.canvas.remove(this.outlineImage); |
| 154 | +this.outlineImage=null; |
| 155 | +} |
| 156 | +return; |
| 157 | +} |
| 158 | +this._initOffscreen(); |
| 159 | +this.offscreenCtx.clearRect(0,0,this.offscreenCanvas.width,this.offscreenCanvas.height); |
| 160 | +var self=this; |
| 161 | +this.pathDataArray.forEach(function(pathData){ |
| 162 | +if(pathData.outline2Width>0){ |
| 163 | +self._drawStroke(pathData,self.offscreenCtx,pathData.strokeWidth+2*pathData.outline1Width+2*pathData.outline2Width,pathData.outline2Color,pathData.outline2Opacity); |
| 164 | +} |
| 165 | +}); |
| 166 | +this.pathDataArray.forEach(function(pathData){ |
| 167 | +if(pathData.outline1Width>0){ |
| 168 | +self._drawStroke(pathData,self.offscreenCtx,pathData.strokeWidth+2*pathData.outline1Width,pathData.outline1Color,pathData.outline1Opacity); |
| 169 | +} |
| 170 | +}); |
| 171 | +this.pathDataArray.forEach(function(pathData){ |
| 172 | +self._drawStroke(pathData,self.offscreenCtx,pathData.strokeWidth,pathData.stroke,1); |
| 173 | +}); |
| 174 | +var imageData=this.offscreenCtx.getImageData(0,0,this.offscreenCanvas.width,this.offscreenCanvas.height); |
| 175 | +var data=imageData.data; |
| 176 | +var minX=this.offscreenCanvas.width; |
| 177 | +var minY=this.offscreenCanvas.height; |
| 178 | +var maxX=0; |
| 179 | +var maxY=0; |
| 180 | +for(var y=0;y<this.offscreenCanvas.height;y++){ |
| 181 | +for(var x=0;x<this.offscreenCanvas.width;x++){ |
| 182 | +var alpha=data[(y*this.offscreenCanvas.width+x)*4+3]; |
| 183 | +if(alpha>0){ |
| 184 | +minX=Math.min(minX,x); |
| 185 | +minY=Math.min(minY,y); |
| 186 | +maxX=Math.max(maxX,x); |
| 187 | +maxY=Math.max(maxY,y); |
| 188 | +} |
| 189 | +} |
| 190 | +} |
| 191 | +if(maxX<minX||maxY<minY){ |
| 192 | +changeDoNotSaveHistory(); |
| 193 | +if(this.outlineImage){ |
| 194 | +this.canvas.remove(this.outlineImage); |
| 195 | +this.outlineImage=null; |
| 196 | +} |
| 197 | +this.pathDataArray=[]; |
| 198 | +changeDoSaveHistory(); |
| 199 | +return; |
| 200 | +} |
| 201 | +var width=maxX-minX+1; |
| 202 | +var height=maxY-minY+1; |
| 203 | +var croppedCanvas=document.createElement("canvas"); |
| 204 | +croppedCanvas.width=width; |
| 205 | +croppedCanvas.height=height; |
| 206 | +var croppedCtx=croppedCanvas.getContext("2d"); |
| 207 | +croppedCtx.drawImage(this.offscreenCanvas,minX,minY,width,height,0,0,width,height); |
| 208 | +var mergedImage=croppedCanvas.toDataURL(); |
| 209 | +fabric.Image.fromURL(mergedImage,function(img){ |
| 210 | +img.set({ |
| 211 | +left: minX, |
| 212 | +top: minY, |
| 213 | +selectable: false, |
| 214 | +scaleX: 1, |
| 215 | +scaleY: 1 |
| 216 | +}); |
| 217 | +changeDoNotSaveHistory(); |
| 218 | +if(self.outlineImage){ |
| 219 | +self.canvas.remove(self.outlineImage); |
| 220 | +self.outlineImage=null; |
| 221 | +} |
| 222 | +self.pathDataArray=[]; |
| 223 | +changeDoSaveHistory(); |
| 224 | +self.canvas.add(img); |
| 225 | +self.canvas.renderAll(); |
| 226 | +}); |
| 227 | +} |
| 228 | +}); |
| 229 | + |
1 | 230 | fabric.MosaicBrush=fabric.util.createClass(fabric.BaseBrush,{ |
2 | 231 | initialize: function (canvas) { |
3 | 232 | this.canvas=canvas; |
@@ -113,7 +342,9 @@ ctx.moveTo(startX,y); |
113 | 342 | ctx.lineTo(endX,y); |
114 | 343 | ctx.stroke(); |
115 | 344 | } |
116 | | -} |
| 345 | +}, |
| 346 | + |
| 347 | +_render: function(){} |
117 | 348 |
|
118 | 349 | }); |
119 | 350 |
|
|
0 commit comments