fabric-guideline-plugin
Version:
🤩 Help you easily append guidelines and auto-snap to your fabric.js canvas.
2 lines (1 loc) • 5.54 kB
JavaScript
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const f=require("fabric"),x=d=>Object.keys(d);class C{aligningLineMargin=4;aligningLineWidth=.75;aligningLineColor="#F68066";ignoreObjTypes=[];pickObjTypes=[];canvas;ctx;viewportTransform;verticalLines=[];horizontalLines=[];activeObj=new f.fabric.Object;constructor({canvas:t,aligningOptions:i,ignoreObjTypes:n,pickObjTypes:s}){this.canvas=t,this.ctx=t.getSelectionContext(),this.ignoreObjTypes=n||[],this.pickObjTypes=s||[],i&&(this.aligningLineMargin=i.lineMargin||this.aligningLineMargin,this.aligningLineWidth=i.lineWidth||this.aligningLineWidth,this.aligningLineColor=i.lineColor||this.aligningLineColor)}drawSign(t,i){const n=this.ctx;n.lineWidth=.5,n.strokeStyle=this.aligningLineColor,n.beginPath();const s=2;n.moveTo(t-s,i-s),n.lineTo(t+s,i+s),n.moveTo(t+s,i-s),n.lineTo(t-s,i+s),n.stroke()}drawLine(t,i,n,s){const a=this.ctx,e=f.fabric.util.transformPoint(new f.fabric.Point(t,i),this.canvas.viewportTransform),r=f.fabric.util.transformPoint(new f.fabric.Point(n,s),this.canvas.viewportTransform);a.save(),a.lineWidth=this.aligningLineWidth,a.strokeStyle=this.aligningLineColor,a.beginPath(),a.moveTo(e.x,e.y),a.lineTo(r.x,r.y),a.stroke(),this.drawSign(e.x,e.y),this.drawSign(r.x,r.y),a.restore()}drawVerticalLine(t){const i=this.getObjDraggingObjCoords(this.activeObj);!x(i).some(n=>Math.abs(i[n].x-t.x)<1e-4)||this.drawLine(t.x,Math.min(t.y1,t.y2),t.x,Math.max(t.y1,t.y2))}drawHorizontalLine(t){const i=this.getObjDraggingObjCoords(this.activeObj);!x(i).some(n=>Math.abs(i[n].y-t.y)<1e-4)||this.drawLine(Math.min(t.x1,t.x2),t.y,Math.max(t.x1,t.x2),t.y)}isInRange(t,i){return Math.abs(Math.round(t)-Math.round(i))<=this.aligningLineMargin/this.canvas.getZoom()}watchMouseDown(){this.canvas.on("mouse:down",()=>{this.clearLinesMeta(),this.viewportTransform=this.canvas.viewportTransform})}watchMouseUp(){this.canvas.on("mouse:up",()=>{this.clearLinesMeta(),this.canvas.renderAll()})}watchMouseWheel(){this.canvas.on("mouse:wheel",()=>{this.clearLinesMeta()})}clearLinesMeta(){this.verticalLines.length=this.horizontalLines.length=0}watchObjectMoving(){this.canvas.on("object:moving",t=>{this.clearLinesMeta();const i=t.target;this.activeObj=i;const n=this.canvas.getObjects().filter(a=>this.ignoreObjTypes.length?!this.ignoreObjTypes.some(e=>a[e.key]===e.value):this.pickObjTypes.length?this.pickObjTypes.some(e=>a[e.key]===e.value):!0);!this.canvas._currentTransform||this.traversAllObjects(i,n)})}getObjDraggingObjCoords(t){const i=t.aCoords,n=new f.fabric.Point((i.tl.x+i.br.x)/2,(i.tl.y+i.br.y)/2),s=n.x-t.getCenterPoint().x,a=n.y-t.getCenterPoint().y;return x(i).reduce((e,r)=>({...e,[r]:{x:i[r].x-s,y:i[r].y-a}}),{c:t.getCenterPoint()})}omitCoords(t,i){let n;if(i==="vertical"){let s=["tl",t.tl],a=["tl",t.tl];x(t).forEach(e=>{t[e].x<s[1].x&&(s=[e,t[e]]),t[e].x>a[1].x&&(a=[e,t[e]])}),n={[s[0]]:s[1],[a[0]]:a[1],c:t.c}}else{let s=["tl",t.tl],a=["tl",t.tl];x(t).forEach(e=>{t[e].y<s[1].y&&(s=[e,t[e]]),t[e].y>a[1].y&&(a=[e,t[e]])}),n={[s[0]]:s[1],[a[0]]:a[1],c:t.c}}return n}getObjMaxWidthHeightByCoords(t){const i=Math.max(Math.abs(t.c.y-t.tl.y),Math.abs(t.c.y-t.tr.y))*2,n=Math.max(Math.abs(t.c.x-t.tl.x),Math.abs(t.c.x-t.tr.x))*2;return{objHeight:i,objWidth:n}}calcCenterPointByACoords(t){return new f.fabric.Point((t.tl.x+t.br.x)/2,(t.tl.y+t.br.y)/2)}traversAllObjects(t,i){const n=this.getObjDraggingObjCoords(t),s=[],a=[];for(let e=i.length;e--;){if(i[e]===t)continue;const r={...i[e].aCoords,c:i[e].getCenterPoint()},{objHeight:y,objWidth:m}=this.getObjMaxWidthHeightByCoords(r);x(n).forEach(o=>{const u=i[e].angle!==0?this.omitCoords(r,"horizontal"):r;function M(h,l){let c,g;return h==="c"?(c=Math.min(r.c.x-m/2,l[o].x),g=Math.max(r.c.x+m/2,l[o].x)):(c=Math.min(r[h].x,l[o].x),g=Math.max(r[h].x,l[o].x)),{x1:c,x2:g}}x(u).forEach(h=>{if(this.isInRange(n[o].y,r[h].y)){const l=r[h].y;let{x1:c,x2:g}=M(h,n);const L=n[o].y-l;if(a.push(n.c.y-L),t.aCoords){let{x1:w,x2:p}=M(h,{...t.aCoords,c:this.calcCenterPointByACoords(t.aCoords)});this.horizontalLines.push({y:l,x1:w,x2:p})}else this.horizontalLines.push({y:l,x1:c,x2:g})}})}),x(n).forEach(o=>{const u=i[e].angle!==0?this.omitCoords(r,"vertical"):r;function M(h,l){let c,g;return h==="c"?(c=Math.min(u.c.y-y/2,l[o].y),g=Math.max(u.c.y+y/2,l[o].y)):(c=Math.min(r[h].y,l[o].y),g=Math.max(r[h].y,l[o].y)),{y1:c,y2:g}}x(u).forEach(h=>{if(this.isInRange(n[o].x,r[h].x)){const l=r[h].x;let{y1:c,y2:g}=M(h,n);const L=n[o].x-l;if(s.push(n.c.x-L),t.aCoords){let{y1:w,y2:p}=M(h,{...t.aCoords,c:this.calcCenterPointByACoords(t.aCoords)});this.verticalLines.push({x:l,y1:w,y2:p})}else this.verticalLines.push({x:l,y1:c,y2:g})}})}),this.snap({activeObject:t,draggingObjCoords:n,snapXPoints:s,snapYPoints:a})}}snap({activeObject:t,snapXPoints:i,draggingObjCoords:n,snapYPoints:s}){const a=(e,r)=>e.length?e.map(y=>({abs:Math.abs(r-y),val:y})).sort((y,m)=>y.abs-m.abs)[0].val:r;t.setPositionByOrigin(new f.fabric.Point(a(i,n.c.x),a(s,n.c.y)),"center","center")}clearGuideline(){this.canvas.clearContext(this.ctx)}watchRender(){this.canvas.on("before:render",()=>{this.clearGuideline()}),this.canvas.on("after:render",()=>{for(let t=this.verticalLines.length;t--;)this.drawVerticalLine(this.verticalLines[t]);for(let t=this.horizontalLines.length;t--;)this.drawHorizontalLine(this.horizontalLines[t]);this.canvas.calcOffset()})}init(){this.watchObjectMoving(),this.watchRender(),this.watchMouseDown(),this.watchMouseUp(),this.watchMouseWheel()}}exports.AlignGuidelines=C;