@sunspots/core 0.0.8 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -5,6 +5,7 @@ declare const Color: {
5
5
  readonly Transparent: "rgba(0,0,0,0)";
6
6
  readonly Black: "#000000";
7
7
  readonly White: "#ffffff";
8
+ readonly Primary: "#3b82f6";
8
9
  };
9
10
 
10
11
  interface Vector2 {
@@ -39,6 +40,59 @@ declare enum ResizeDirection {
39
40
  TopLeft = 7
40
41
  }
41
42
 
43
+ declare function pixelToDot(px: number): number;
44
+ declare function dotToPixel(dot: number): number;
45
+ declare function millimeterToPixel(mm: number): number;
46
+ declare function pixelToMillimeter(px: number): number;
47
+ type RightAngleRotation = 0 | 90 | 180 | 270;
48
+ declare function withDpr(value: number): number;
49
+ declare function withPixel(value: number): string;
50
+ declare function withHash(value: string): string;
51
+ declare function randomColor(): string;
52
+ declare function rgbToHex(r: number, g: number, b: number): string;
53
+ declare function rgba<R extends number, G extends number, B extends number, A extends number>(r: R, g: G, b: B, a?: A): `rgba(${R},${G},${B},${A})`;
54
+ declare function angleToRadian(angle: number): number;
55
+ declare function radianToAngle(radian: number): number;
56
+ /**
57
+ * 将点按直角旋转
58
+ * @param origin
59
+ * @param rotation
60
+ * @returns
61
+ */
62
+ declare function rotatePointRightAngle(origin: Vector2, rotation: RightAngleRotation): Vector2;
63
+ /**
64
+ * 将 world 坐标系中的位移(dx, dy)转换为 local 坐标系中的位移。
65
+ *
66
+ * world 指 canvas 坐标系。
67
+ * local 指以图形左上角为原点,沿图形局部 X/Y 轴方向的坐标系。
68
+ *
69
+ * 由于拖拽/鼠标移动发生在 world 上,做 resize 等操作时常需要转回 local 才能得到沿图形局部轴向的真实位移。
70
+ *
71
+ * @param dx world X
72
+ * @param dy world Y
73
+ * @param rotation
74
+ * @returns
75
+ */
76
+ declare function worldDeltaToLocalRightAngle(dx: number, dy: number, rotation: RightAngleRotation): Vector2;
77
+ /**
78
+ * 将 local 坐标系中的位移转换为 world 坐标系中的位移。
79
+ *
80
+ * @param dx local X
81
+ * @param dy local Y
82
+ * @param rotation
83
+ * @returns
84
+ */
85
+ declare function localDeltaToWorldRightAngle(dx: number, dy: number, rotation: RightAngleRotation): Vector2;
86
+ /**
87
+ * 将一个 box 围绕 origin 进行直角旋转后,返回新的 box
88
+ *
89
+ * @param box
90
+ * @param origin
91
+ * @param rotation
92
+ * @returns
93
+ */
94
+ declare function rotateBoxRightAngle(box: Box, origin: Vector2, rotation: RightAngleRotation): Box;
95
+
42
96
  interface StageConfig {
43
97
  readonly sceneCanvas: HTMLCanvasElement;
44
98
  readonly draggableCanvas: HTMLCanvasElement;
@@ -60,6 +114,7 @@ declare class Stage {
60
114
  private draggableContext;
61
115
  readonly draftContext: CanvasRenderingContext2D;
62
116
  scale: number;
117
+ private snapLines;
63
118
  private readonly events;
64
119
  private readonly destroyed$;
65
120
  constructor(config: StageConfig);
@@ -78,9 +133,10 @@ declare class Stage {
78
133
  private drawScene;
79
134
  private draw;
80
135
  private drawDraggable;
136
+ private drawSnapLines;
81
137
  private drawTransformer;
82
138
  private drawShadow;
83
- private getColor;
139
+ private getShadowColor;
84
140
  private clear;
85
141
  private emit;
86
142
  on(type: StageEventName): Observable<StageEvent>;
@@ -98,6 +154,7 @@ declare class Stage {
98
154
  fill: string | undefined;
99
155
  stroke: string | undefined;
100
156
  strokeWidth: number;
157
+ rotation: RightAngleRotation;
101
158
  } & Record<string, unknown>)[];
102
159
  };
103
160
  toBlob(quality?: number): Promise<Blob>;
@@ -111,6 +168,7 @@ interface ShapeConfig {
111
168
  fill?: string;
112
169
  stroke?: string;
113
170
  strokeWidth?: number;
171
+ rotation?: number;
114
172
  }
115
173
  interface ResizableConfig {
116
174
  /** 横纵比,输入即代表缩放需要保持横纵比 */
@@ -137,10 +195,13 @@ declare abstract class Shape<T extends ShapeConfig = ShapeConfig> implements Sha
137
195
  fill?: string;
138
196
  stroke?: string;
139
197
  strokeWidth: number;
198
+ rotation: RightAngleRotation;
140
199
  constructor(config: T);
141
200
  abstract render(context: CanvasRenderingContext2D): void;
142
201
  abstract boundingBox(): Box;
143
202
  abstract innerBox(): Box;
203
+ worldInnerBox(): Box;
204
+ worldBoundingBox(): Box;
144
205
  renderShadow?(context: CanvasRenderingContext2D): void;
145
206
  setWidth?(width: number): void;
146
207
  setHeight?(height: number): void;
@@ -159,6 +220,7 @@ declare abstract class Shape<T extends ShapeConfig = ShapeConfig> implements Sha
159
220
  fill: string | undefined;
160
221
  stroke: string | undefined;
161
222
  strokeWidth: number;
223
+ rotation: RightAngleRotation;
162
224
  } & Record<string, unknown>;
163
225
  }
164
226
  declare function isLockAspectRatioResize(shape: Shape): shape is Shape & {
@@ -197,11 +259,4 @@ interface PointerEvent {
197
259
  offsetY: number;
198
260
  }
199
261
 
200
- declare function withDpr(value: number): number;
201
- declare function withPixel(value: number): string;
202
- declare function withHash(value: string): string;
203
- declare function randomColor(): string;
204
- declare function rgbToHex(r: number, g: number, b: number): string;
205
- declare function rgba<R extends number, G extends number, B extends number, A extends number>(r: R, g: G, b: B, a?: A): `rgba(${R},${G},${B},${A})`;
206
-
207
- export { type Box, Color, Placement, type PointerEvent, type PointerEventName, ReferenceLine, type ResizableConfig, ResizeDirection, Shape, type ShapeConfig, type ShapeEvent, Stage, type StageConfig, type StageEvent, type StageEventName, type Vector2, isLockAspectRatioResize, randomColor, rgbToHex, rgba, withDpr, withHash, withPixel };
262
+ export { type Box, Color, Placement, type PointerEvent, type PointerEventName, ReferenceLine, type ResizableConfig, ResizeDirection, type RightAngleRotation, Shape, type ShapeConfig, type ShapeEvent, Stage, type StageConfig, type StageEvent, type StageEventName, type Vector2, angleToRadian, dotToPixel, isLockAspectRatioResize, localDeltaToWorldRightAngle, millimeterToPixel, pixelToDot, pixelToMillimeter, radianToAngle, randomColor, rgbToHex, rgba, rotateBoxRightAngle, rotatePointRightAngle, withDpr, withHash, withPixel, worldDeltaToLocalRightAngle };
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- var U=Object.defineProperty;var f=(c,t)=>U(c,"name",{value:t,configurable:!0});var X="#",J="px";function k(c){return c*devicePixelRatio}f(k,"withDpr");function H(c){return c+J}f(H,"withPixel");function Y(c){return X+c}f(Y,"withHash");function D(){let c=(Math.random()*16777215<<0).toString(16);for(;c.length<6;)c=0+c;return X+c}f(D,"randomColor");function I(c,t,e){return((1<<24)+(c<<16)+(t<<8)+e).toString(16).slice(1)}f(I,"rgbToHex");function N(c,t,e,a=1){return`rgba(${c},${t},${e},${a})`}f(N,"rgba");var E={Transparent:N(0,0,0,0),Black:"#000000",White:"#ffffff"};var F=(e=>(e[e.Horizontal=0]="Horizontal",e[e.Vertical=1]="Vertical",e))(F||{}),W=class W{constructor(t,e){this.placement=t;this.position=e}};f(W,"ReferenceLine");var r=W;var K=(n=>(n[n.Top=0]="Top",n[n.Right=1]="Right",n[n.Bottom=2]="Bottom",n[n.Left=3]="Left",n[n.TopRight=4]="TopRight",n[n.BottomRight=5]="BottomRight",n[n.BottomLeft=6]="BottomLeft",n[n.TopLeft=7]="TopLeft",n))(K||{});import{Subject as Q,filter as Z}from"rxjs";var T=new Map;var O=class O{constructor(t){this.config=t;this.index=0;this.resizable=!0;this.events=new Q;this.strokeWidth=0;for(;;){let e=D();if(e&&!T.has(e)){this.key=e,T.set(e,this);break}}Object.assign(this,t)}get x(){var t;return(t=this.config.x)!=null?t:0}set x(t){this.config.x=t}get y(){var t;return(t=this.config.y)!=null?t:0}set y(t){this.config.y=t}get width(){var t;return(t=this.config.width)!=null?t:0}set width(t){this.config.width=t}get height(){var t;return(t=this.config.height)!=null?t:0}set height(t){this.config.height=t}coord(){return{x:this.x,y:this.y}}emit(t){this.events.next(t)}on(t){return this.events.pipe(Z(e=>e.type===t))}destroy(){this.events.complete()}setAttrs(t){Object.assign(this,t)}toObject(t={}){return Object.assign({type:this.type,index:this.index,x:this.x,y:this.y,width:this.width,height:this.height,fill:this.fill,stroke:this.stroke,strokeWidth:this.strokeWidth},t)}};f(O,"Shape");var L=O;function z(c){return typeof c.resizable=="object"&&!!c.resizable.aspectRatio}f(z,"isLockAspectRatioResize");import{animationFrames as _,filter as R,Subject as G,takeUntil as M}from"rxjs";var j=class j extends L{constructor(e){super({fill:"#ffffff",stroke:"#3b82f6",strokeWidth:2});this.direction=e;this.type="anchor";this.radius=5}getRadius(){return this.radius/this.stage.scale}render(e){let{x:a,y:h,stroke:w,strokeWidth:u}=this;e.beginPath(),e.arc(a,h,this.getRadius(),0,Math.PI*2,!1),e.fill(),w&&(e.lineWidth=(u!=null?u:1)/this.stage.scale,e.stroke()),e.closePath()}innerBox(){return{x:this.x-this.getRadius(),y:this.y-this.getRadius(),width:this.getRadius()*2,height:this.getRadius()*2}}boundingBox(){var w;let e=this.innerBox(),a=(w=this.strokeWidth)!=null?w:0,h=a/2;return e.x-=h,e.y-=h,e.width+=a,e.height+=a,e}setWidth(e){this.width=e,this.radius=e/2}setHeight(e){this.height=e,this.radius=e/2}get x(){let e=this.target.boundingBox();switch(this.direction){case 0:case 2:return e.x+e.width/2;case 4:case 1:case 5:return e.x+e.width;case 7:case 6:case 3:return e.x}}set x(e){}get y(){let e=this.target.boundingBox();switch(this.direction){case 7:case 0:case 4:return e.y;case 1:case 3:return e.y+e.height/2;case 5:case 2:case 6:return e.y+e.height}}set y(e){}setTarget(e){this.target=e}};f(j,"Anchor");var b=j;var $=class ${constructor(t){this.config=t;this.anchors=[new b(7),new b(0),new b(4),new b(1),new b(5),new b(2),new b(6),new b(3)];this.shapes=[];this.activatedShape=null;this.scale=1;this.events=new G;this.destroyed$=new G;let{sceneCanvas:e,shadowCanvas:a,draggableCanvas:h,draftCanvas:w,canvasEvents:u,clientEvents:y}=t;this.context=e.getContext("2d"),this.shadowContext=a.getContext("2d",{willReadFrequently:!0}),this.draggableContext=h.getContext("2d"),this.draftContext=w.getContext("2d");for(let g of this.anchors)g.stage=this;this.resize();let n,l,s,x,A=!1;u.pipe(R(g=>g.type==="pointerdown"),M(this.destroyed$)).subscribe(g=>{if(g.button!==void 0&&g.button!==0)return;A=!0,n=g.offsetX,l=g.offsetY;let P=k(g.offsetX),V=k(g.offsetY),m=this.getColor(P,V),o=T.get(m);if(!o){this.emit({type:"pointerdown"}),this.setActivatedShape(null);return}if(this.activatedShape=o,o.emit({type:"pointerdown"}),o instanceof b){s=o.target.innerBox(),x=o.target.coord(),this.drawScene();return}this.setActivatedShape(o),this.emit({type:"pointerdown",target:o}),s=o.innerBox(),x=o.coord(),this.drawDraggable(),this.drawScene()}),y.pipe(R(g=>g.type==="pointermove"),R(()=>A),M(this.destroyed$)).subscribe(g=>{let P=g.offsetX,V=g.offsetY,m=(P-n)/this.scale,o=(V-l)/this.scale;if(this.activatedShape instanceof b){let B=this.activatedShape,d=B.target,v=x.x,S=x.y,p=s.width,i=s.height;switch(B.direction){case 7:v=x.x+m,S=x.y+o,i=s.height-o,p=s.width-m;break;case 0:S=x.y+o,i=s.height-o,z(d)&&(p=i*d.resizable.aspectRatio);break;case 4:S=x.y+o,i=s.height-o,p=s.width+m;break;case 1:p=s.width+m,z(d)&&(i=p/d.resizable.aspectRatio);break;case 5:p=s.width+m,i=s.height+o;break;case 2:i=s.height+o,z(d)&&(p=i*d.resizable.aspectRatio);break;case 6:i=s.height+o,v=x.x+m,p=s.width-m;break;case 3:v=x.x+m,p=s.width-m,z(d)&&(i=p/d.resizable.aspectRatio);break}v=Math.round(v),S=Math.round(S),p=Math.round(p),i=Math.round(i),d.x=Math.min(x.x+s.width,v),d.y=Math.min(x.y+s.height,S),d.setWidth?d.setWidth(Math.max(1,p)):d.width=Math.max(1,p),d.setHeight?d.setHeight(Math.max(1,i)):d.height=Math.max(1,i)}else if(this.activatedShape){let B=Math.round(x.x+m),d=Math.round(x.y+o),v=1/0,S=1/0,p=[new r(0,0),new r(1,0),new r(0,this.config.height/2),new r(1,this.config.width/2),new r(0,this.config.height/2-this.activatedShape.height/2),new r(1,this.config.width/2-this.activatedShape.width/2),new r(0,this.config.height-this.activatedShape.height),new r(1,this.config.width-this.activatedShape.width)];for(let i of this.shapes)i!==this.activatedShape&&p.push(new r(0,i.y-this.activatedShape.height),i.height>this.activatedShape.height?new r(0,i.y-this.activatedShape.height/2):new r(0,i.y-this.activatedShape.height+i.height/2),new r(0,i.y),i.height>this.activatedShape.height?new r(0,i.y+i.height-this.activatedShape.height/2):new r(0,i.y-this.activatedShape.height+i.height),new r(0,i.y/2),new r(0,i.y+i.height),new r(1,i.x-this.activatedShape.width),i.width>this.activatedShape.width?new r(1,i.x-this.activatedShape.width/2):new r(1,i.x-this.activatedShape.width+i.width/2),new r(1,i.x),i.width>this.activatedShape.width?new r(1,i.x+i.width-this.activatedShape.width/2):new r(1,i.x-this.activatedShape.width+i.width),new r(1,i.x/2),new r(1,i.x+i.width));for(let i of p)switch(i.placement){case 0:{let C=Math.abs(i.position-d);C<=4&&C<S&&(d=i.position,S=C);break}case 1:{let C=Math.abs(i.position-B);C<=4&&C<v&&(B=i.position,v=C);break}}this.activatedShape.x=B,this.activatedShape.y=d}}),y.pipe(R(g=>g.type==="pointerup"||g.type==="pointercancel"),M(this.destroyed$)).subscribe(()=>{this.render(),A=!1}),_().pipe(R(()=>A&&!!this.activatedShape),M(this.destroyed$)).subscribe(()=>{this.drawDraggable()})}resize(t){Object.assign(this.config,t);let{sceneCanvas:e,shadowCanvas:a,draggableCanvas:h,draftCanvas:w,width:u,height:y,dpr:n}=this.config;for(let l of[e,a,h,w]){let s=l.getContext("2d");l.style.width=H(u),l.style.height=H(y),l.width=k(u),l.height=k(y),s.scale(n,n)}}zoom(t){let{sceneCanvas:e,shadowCanvas:a,draggableCanvas:h,draftCanvas:w,dpr:u,width:y,height:n}=this.config;for(let l of[e,a,h,w]){let s=l.getContext("2d");l.style.width=H(y*t),l.style.height=H(n*t),l.width=k(y*t),l.height=k(n*t),s.scale(u*t,u*t)}this.scale=t}add(...t){for(let e of t)e.index=this.shapes.length,e.stage=this,this.shapes.push(e),this.draw(e),this.emit({type:"add",target:e})}remove(...t){let e=this.getActivatedShape();for(let a of t){this.shapes.splice(this.shapes.indexOf(a),1);for(let h of this.shapes)h.index>a.index&&h.index--;e===a&&this.setActivatedShape(null),this.emit({type:"remove",target:a})}for(let a=0;a<this.shapes.length;a++){let h=this.shapes[a];h.index=a}}setActivatedShape(t){if(!t){this.activatedShape=null;return}for(let e of this.shapes)e.index>t.index&&e.index--;t.index=this.shapes.length;for(let e of this.anchors)e.target=t;this.activatedShape=t}getActivatedShape(){return this.activatedShape instanceof b?this.activatedShape.target:this.activatedShape}render(){this.activatedShape?this.drawDraggable():this.clear(this.draggableContext),this.drawScene()}isActivatedShape(t){let e=this.getActivatedShape();return t===e}drawScene(){this.clear(this.context),this.clear(this.shadowContext);for(let e of this.shapes.slice().sort((a,h)=>a.index-h.index))this.draw(e);let t=this.getActivatedShape();if(t&&t.resizable)for(let e of this.anchors)this.drawShadow(e)}draw(t){var e;this.drawShadow(t),!this.isActivatedShape(t)&&(this.context.save(),this.context.fillStyle=(e=t.fill)!=null?e:E.Transparent,t.stroke&&(this.context.lineWidth=t.strokeWidth,this.context.strokeStyle=t.stroke),this.context.beginPath(),t.render(this.context),this.context.stroke(),this.context.restore())}drawDraggable(){var e;let t=this.getActivatedShape();t&&(this.clear(this.draggableContext),this.draggableContext.save(),this.draggableContext.fillStyle=(e=t.fill)!=null?e:E.Transparent,t.stroke&&(this.draggableContext.lineWidth=t.strokeWidth,this.draggableContext.strokeStyle=t.stroke),this.draggableContext.beginPath(),t.render(this.draggableContext),this.draggableContext.stroke(),this.draggableContext.restore(),this.drawTransformer(t))}drawTransformer(t){let e=t.boundingBox();if(this.draggableContext.fillStyle="#ffffff",this.draggableContext.strokeStyle="#3b82f6",this.draggableContext.lineWidth=2/this.scale,this.draggableContext.strokeRect(e.x,e.y,e.width,e.height),!t.resizable)return;let a=[];typeof t.resizable=="object"&&(t.resizable.aspectRatio?a=[0,1,2,3]:t.resizable.directions&&(a=t.resizable.directions));for(let h of this.anchors)a.length&&!a.includes(h.direction)||h.render(this.draggableContext)}drawShadow(t){this.shadowContext.save(),this.shadowContext.fillStyle=t.fill?t.key:E.Transparent,this.shadowContext.strokeStyle=t.key,t.stroke&&(this.shadowContext.lineWidth=t.strokeWidth),this.shadowContext.beginPath(),t.renderShadow?t.renderShadow(this.shadowContext):t.render(this.shadowContext),this.shadowContext.stroke(),this.shadowContext.restore()}getColor(t,e){let{data:a}=this.shadowContext.getImageData(t,e,1,1);return Y(I(a[0],a[1],a[2]))}clear(t){t.clearRect(0,0,this.config.width,this.config.height)}emit(t){this.events.next(t)}on(t){return this.events.pipe(R(e=>e.type===t))}destroy(){this.destroyed$.next(),this.destroyed$.complete(),this.events.complete();for(let t of this.shapes)t.destroy()}toObject(){return{width:this.config.width,height:this.config.height,children:this.shapes.map(t=>t.toObject())}}toBlob(t=1){return this.setActivatedShape(null),this.render(),new Promise(e=>{this.config.sceneCanvas.toBlob(a=>e(a),"image/webp",t)})}};f($,"Stage");var q=$;export{E as Color,F as Placement,r as ReferenceLine,K as ResizeDirection,L as Shape,q as Stage,z as isLockAspectRatioResize,D as randomColor,I as rgbToHex,N as rgba,k as withDpr,Y as withHash,H as withPixel};
1
+ var it=Object.defineProperty;var tt=Object.getOwnPropertySymbols;var pt=Object.prototype.hasOwnProperty,ut=Object.prototype.propertyIsEnumerable;var et=(i,t,e)=>t in i?it(i,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):i[t]=e,nt=(i,t)=>{for(var e in t||(t={}))pt.call(t,e)&&et(i,e,t[e]);if(tt)for(var e of tt(t))ut.call(t,e)&&et(i,e,t[e]);return i};var c=(i,t)=>it(i,"name",{value:t,configurable:!0});var ot="#",xt="px";function St(i){return Math.round(i*.8)}c(St,"pixelToDot");function Ct(i){return Math.round(i/.8)}c(Ct,"dotToPixel");function Rt(i){return i*10}c(Rt,"millimeterToPixel");function Bt(i){return i/10}c(Bt,"pixelToMillimeter");function L(i){return i*devicePixelRatio}c(L,"withDpr");function z(i){return i+xt}c(z,"withPixel");function rt(i){return ot+i}c(rt,"withHash");function at(){let i=(Math.random()*16777215<<0).toString(16);for(;i.length<6;)i=0+i;return ot+i}c(at,"randomColor");function st(i,t,e){return((1<<24)+(i<<16)+(t<<8)+e).toString(16).slice(1)}c(st,"rgbToHex");function ht(i,t,e,n=1){return`rgba(${i},${t},${e},${n})`}c(ht,"rgba");function X(i){return i*Math.PI/180}c(X,"angleToRadian");function kt(i){return i*180/Math.PI}c(kt,"radianToAngle");function U(i,t){switch(t){case 90:return{x:-i.y,y:i.x};case 180:return{x:-i.x,y:-i.y};case 270:return{x:i.y,y:-i.x};default:return i}}c(U,"rotatePointRightAngle");function ct(i,t,e){switch(e){case 90:return{x:t,y:-i};case 180:return{x:-i,y:-t};case 270:return{x:-t,y:i};default:return{x:i,y:t}}}c(ct,"worldDeltaToLocalRightAngle");function dt(i,t,e){switch(e){case 90:return{x:-t,y:i};case 180:return{x:-i,y:-t};case 270:return{x:t,y:-i};default:return{x:i,y:t}}}c(dt,"localDeltaToWorldRightAngle");function J(i,t,e){if(e===0)return nt({},i);let n=[{x:i.x,y:i.y},{x:i.x+i.width,y:i.y},{x:i.x,y:i.y+i.height},{x:i.x+i.width,y:i.y+i.height}],s=1/0,d=1/0,h=-1/0,l=-1/0;for(let o of n){let a={x:o.x-t.x,y:o.y-t.y},r=U(a,e),B={x:t.x+r.x,y:t.y+r.y};s=Math.min(s,B.x),d=Math.min(d,B.y),h=Math.max(h,B.x),l=Math.max(l,B.y)}return{x:s,y:d,width:h-s,height:l-d}}c(J,"rotateBoxRightAngle");var T={Transparent:ht(0,0,0,0),Black:"#000000",White:"#ffffff",Primary:"#3b82f6"};var lt=(e=>(e[e.Horizontal=0]="Horizontal",e[e.Vertical=1]="Vertical",e))(lt||{}),K=class K{constructor(t,e){this.placement=t;this.position=e}};c(K,"ReferenceLine");var y=K;var mt=(o=>(o[o.Top=0]="Top",o[o.Right=1]="Right",o[o.Bottom=2]="Bottom",o[o.Left=3]="Left",o[o.TopRight=4]="TopRight",o[o.BottomRight=5]="BottomRight",o[o.BottomLeft=6]="BottomLeft",o[o.TopLeft=7]="TopLeft",o))(mt||{});import{filter as bt,Subject as yt}from"rxjs";var P=new Map;var Q=class Q{constructor(t){this.config=t;this.index=0;this.resizable=!0;this.events=new yt;this.strokeWidth=0;this.rotation=0;for(;;){let e=at();if(e&&!P.has(e)){this.key=e,P.set(e,this);break}}Object.assign(this,t)}get x(){var t;return(t=this.config.x)!=null?t:0}set x(t){this.config.x=t}get y(){var t;return(t=this.config.y)!=null?t:0}set y(t){this.config.y=t}get width(){var t;return(t=this.config.width)!=null?t:0}set width(t){this.config.width=t}get height(){var t;return(t=this.config.height)!=null?t:0}set height(t){this.config.height=t}worldInnerBox(){return J(this.innerBox(),{x:this.x,y:this.y},this.rotation)}worldBoundingBox(){return J(this.boundingBox(),{x:this.x,y:this.y},this.rotation)}coord(){return{x:this.x,y:this.y}}emit(t){this.events.next(t)}on(t){return this.events.pipe(bt(e=>e.type===t))}destroy(){P.delete(this.key),this.events.complete()}setAttrs(t){Object.assign(this,t)}toObject(t={}){return Object.assign({type:this.type,index:this.index,x:this.x,y:this.y,width:this.width,height:this.height,fill:this.fill,stroke:this.stroke,strokeWidth:this.strokeWidth,rotation:this.rotation},t)}};c(Q,"Shape");var Y=Q;function O(i){return typeof i.resizable=="object"&&!!i.resizable.aspectRatio}c(O,"isLockAspectRatioResize");import{animationFrames as wt,filter as W,Subject as gt,takeUntil as j}from"rxjs";var Z=class Z extends Y{constructor(e){super({fill:T.White,stroke:T.Primary,strokeWidth:2});this.direction=e;this.type="anchor";this.radius=5}getAnchorLocalPoint(e,n){switch(this.direction){case 7:return{x:0,y:0};case 0:return{x:e/2,y:0};case 4:return{x:e,y:0};case 1:return{x:e,y:n/2};case 5:return{x:e,y:n};case 2:return{x:e/2,y:n};case 6:return{x:0,y:n};case 3:return{x:0,y:n/2}}}getAnchorWorldPoint(){let e=this.target.boundingBox(),{rotation:n}=this.target;if(!n){let a,r;switch(this.direction){case 0:case 2:a=e.x+e.width/2;break;case 4:case 1:case 5:a=e.x+e.width;break;case 7:case 6:case 3:a=e.x;break}switch(this.direction){case 7:case 0:case 4:r=e.y;break;case 1:case 3:r=e.y+e.height/2;break;case 5:case 2:case 6:r=e.y+e.height;break}return{x:a,y:r}}let s=e.x-this.target.x,d=e.y-this.target.y,h=this.getAnchorLocalPoint(e.width,e.height),l={x:s+h.x,y:d+h.y},o=U(l,n);return{x:this.target.x+o.x,y:this.target.y+o.y}}getRadius(){return this.radius/this.stage.scale}render(e){let{x:n,y:s,stroke:d,rotation:h,strokeWidth:l}=this;e.save(),h&&(e.translate(n,s),e.rotate(X(h))),e.beginPath(),e.arc(h?0:n,h?0:s,this.getRadius(),0,Math.PI*2,!1),e.fill(),d&&(e.lineWidth=(l!=null?l:1)/this.stage.scale,e.stroke()),e.closePath(),e.restore()}innerBox(){return{x:this.x-this.getRadius(),y:this.y-this.getRadius(),width:this.getRadius()*2,height:this.getRadius()*2}}boundingBox(){var d;let e=this.innerBox(),n=(d=this.strokeWidth)!=null?d:0,s=n/2;return e.x-=s,e.y-=s,e.width+=n,e.height+=n,e}setWidth(e){this.width=e,this.radius=e/2}setHeight(e){this.height=e,this.radius=e/2}get x(){return this.getAnchorWorldPoint().x}set x(e){}get y(){return this.getAnchorWorldPoint().y}set y(e){}setTarget(e){this.target=e}};c(Z,"Anchor");var v=Z;var _=class _{constructor(t){this.config=t;this.anchors=[new v(7),new v(0),new v(4),new v(1),new v(5),new v(2),new v(6),new v(3)];this.shapes=[];this.activatedShape=null;this.scale=1;this.snapLines=null;this.events=new gt;this.destroyed$=new gt;let{sceneCanvas:e,shadowCanvas:n,draggableCanvas:s,draftCanvas:d,canvasEvents:h,clientEvents:l}=t;this.context=e.getContext("2d"),this.shadowContext=n.getContext("2d",{willReadFrequently:!0}),this.draggableContext=s.getContext("2d"),this.draftContext=d.getContext("2d");for(let x of this.anchors)x.stage=this;this.resize();let o,a,r,B,I=!1;h.pipe(W(x=>x.type==="pointerdown"),j(this.destroyed$)).subscribe(x=>{if(x.button!==void 0&&x.button!==0)return;I=!0,o=x.offsetX,a=x.offsetY;let $=L(x.offsetX),N=L(x.offsetY),D=this.getShadowColor($,N),S=P.get(D);if(!S){this.emit({type:"pointerdown"}),this.setActivatedShape(null);return}if(this.activatedShape=S,S.emit({type:"pointerdown"}),S instanceof v){r=S.target.innerBox(),B=S.target.coord(),this.drawScene();return}this.setActivatedShape(S),this.emit({type:"pointerdown",target:S}),r=S.innerBox(),B=S.coord(),this.drawDraggable(),this.drawScene()}),l.pipe(W(x=>x.type==="pointermove"),W(()=>I),j(this.destroyed$)).subscribe(x=>{let $=x.offsetX,N=x.offsetY,D=($-o)/this.scale,S=(N-a)/this.scale;if(this.activatedShape instanceof v){this.snapLines=null;let C=this.activatedShape,g=C.target,{rotation:H}=g,E=ct(D,S,H),p=E.x,R=E.y,k=0,w=0,f=r.width,u=r.height;switch(C.direction){case 7:k=p,w=R,u=r.height-R,f=r.width-p;break;case 0:w=R,u=r.height-R,O(g)&&(f=u*g.resizable.aspectRatio);break;case 4:w=R,u=r.height-R,f=r.width+p;break;case 1:f=r.width+p,O(g)&&(u=f/g.resizable.aspectRatio);break;case 5:f=r.width+p,u=r.height+R;break;case 2:u=r.height+R,O(g)&&(f=u*g.resizable.aspectRatio);break;case 6:u=r.height+R,k=p,f=r.width-p;break;case 3:k=p,f=r.width-p,O(g)&&(u=f/g.resizable.aspectRatio);break}let M=1,b=1;(C.direction===7||C.direction===3||C.direction===6)&&(k=Math.min(k,r.width-M)),(C.direction===7||C.direction===0||C.direction===4)&&(w=Math.min(w,r.height-b)),f=Math.max(M,f),u=Math.max(b,u),k=Math.round(k),w=Math.round(w),f=Math.round(f),u=Math.round(u);let m=dt(k,w,H);g.x=Math.round(B.x+m.x),g.y=Math.round(B.y+m.y),g.setWidth?g.setWidth(Math.max(1,f)):g.width=Math.max(1,f),g.setHeight?g.setHeight(Math.max(1,u)):g.height=Math.max(1,u)}else if(this.activatedShape){let C=Math.round(B.x+D),g=Math.round(B.y+S),H=1/0,E=1/0;this.activatedShape.x=C,this.activatedShape.y=g;let p=this.activatedShape.worldBoundingBox(),R=[new y(0,0),new y(0,this.config.height/2),new y(0,this.config.height),new y(1,0),new y(1,this.config.width/2),new y(1,this.config.width)];for(let b of this.shapes){if(b===this.activatedShape)continue;let m=b.worldBoundingBox();R.push(new y(0,m.y),new y(0,m.y+m.height/2),new y(0,m.y+m.height),new y(1,m.x),new y(1,m.x+m.width/2),new y(1,m.x+m.width))}let k=2/this.scale,w=0,f=0,u,M;for(let b of R)switch(b.placement){case 0:{let m=p.y+f,F=p.y+p.height/2+f,G=p.y+p.height+f,q=[b.position-m,b.position-F,b.position-G];for(let V of q){let A=Math.abs(V);A<=k&&A<E&&(f=V,E=A,M=b.position)}break}case 1:{let m=p.x+w,F=p.x+p.width/2+w,G=p.x+p.width+w,q=[b.position-m,b.position-F,b.position-G];for(let V of q){let A=Math.abs(V);A<=k&&A<H&&(w=V,H=A,u=b.position)}break}}C=Math.round(C+w),g=Math.round(g+f),this.snapLines=u!==void 0||M!==void 0?{x:u,y:M}:null,this.activatedShape.x=C,this.activatedShape.y=g}}),l.pipe(W(x=>x.type==="pointerup"||x.type==="pointercancel"),j(this.destroyed$)).subscribe(()=>{I=!1,this.snapLines=null,this.render()}),wt().pipe(W(()=>I&&!!this.activatedShape),j(this.destroyed$)).subscribe(()=>{this.drawDraggable()})}resize(t){Object.assign(this.config,t);let{sceneCanvas:e,shadowCanvas:n,draggableCanvas:s,draftCanvas:d,width:h,height:l,dpr:o}=this.config;for(let a of[e,n,s,d]){let r=a.getContext("2d");a.style.width=z(h),a.style.height=z(l),a.width=L(h),a.height=L(l),r.scale(o,o)}}zoom(t){let{sceneCanvas:e,shadowCanvas:n,draggableCanvas:s,draftCanvas:d,dpr:h,width:l,height:o}=this.config;for(let a of[e,n,s,d]){let r=a.getContext("2d");a.style.width=z(l*t),a.style.height=z(o*t),a.width=L(l*t),a.height=L(o*t),r.scale(h*t,h*t)}this.scale=t}add(...t){for(let e of t)e.index=this.shapes.length,e.stage=this,this.shapes.push(e),this.draw(e),this.emit({type:"add",target:e})}remove(...t){let e=this.getActivatedShape();for(let n of t){let s=this.shapes.indexOf(n);s!==-1&&(n.destroy(),this.shapes.splice(s,1),e===n&&this.setActivatedShape(null),this.emit({type:"remove",target:n}))}for(let n=0;n<this.shapes.length;n++)this.shapes[n].index=n}setActivatedShape(t){if(!t){this.activatedShape=null;return}for(let e of this.shapes)e.index>t.index&&e.index--;t.index=this.shapes.length;for(let e of this.anchors)e.target=t;this.activatedShape=t}getActivatedShape(){return this.activatedShape instanceof v?this.activatedShape.target:this.activatedShape}render(){this.activatedShape?this.drawDraggable():this.clear(this.draggableContext),this.drawScene()}isActivatedShape(t){let e=this.getActivatedShape();return t===e}drawScene(){this.clear(this.context),this.clear(this.shadowContext);for(let e of this.shapes.slice().sort((n,s)=>n.index-s.index))this.draw(e);let t=this.getActivatedShape();if(t&&t.resizable)for(let e of this.anchors)this.drawShadow(e)}draw(t){var e;this.drawShadow(t),!this.isActivatedShape(t)&&(this.context.save(),this.context.fillStyle=(e=t.fill)!=null?e:T.Transparent,t.stroke&&(this.context.lineWidth=t.strokeWidth,this.context.strokeStyle=t.stroke),this.context.beginPath(),t.render(this.context),this.context.stroke(),this.context.restore())}drawDraggable(){var n;let t=this.getActivatedShape(),e=this.draggableContext;t&&(this.clear(e),e.save(),e.fillStyle=(n=t.fill)!=null?n:T.Transparent,t.stroke&&(e.lineWidth=t.strokeWidth,e.strokeStyle=t.stroke),e.beginPath(),t.render(e),e.stroke(),e.restore(),this.drawTransformer(t),this.drawSnapLines())}drawSnapLines(){if(!this.snapLines)return;let t=this.draggableContext,{x:e,y:n}=this.snapLines;t.save(),t.strokeStyle=T.Primary,t.lineWidth=.5/this.scale,n!==void 0&&(t.beginPath(),t.moveTo(0,n),t.lineTo(this.config.width,n),t.stroke()),e!==void 0&&(t.beginPath(),t.moveTo(e,0),t.lineTo(e,this.config.height),t.stroke()),t.restore()}drawTransformer(t){var o;let e=this.draggableContext,{x:n,y:s,rotation:d}=t,h=t.boundingBox();if(e.save(),d&&(e.translate(n,s),e.rotate(X(d))),e.fillStyle=T.White,e.strokeStyle=T.Primary,e.lineWidth=2/this.scale,e.strokeRect(d?h.x-n:h.x,d?h.y-s:h.y,h.width,h.height),e.restore(),!t.resizable)return;let l=[];typeof t.resizable=="object"&&(t.resizable.aspectRatio?l=[0,1,2,3]:t.resizable.directions&&(l=t.resizable.directions));for(let a of this.anchors)l.length&&!l.includes(a.direction)||(e.fillStyle=(o=a.fill)!=null?o:T.Transparent,e.strokeStyle=a.stroke,a.rotation=d,a.render(e))}drawShadow(t){var n;let e=this.shadowContext;e.fillStyle=t.fill?t.key:T.Transparent,e.strokeStyle=t.key,t.stroke&&(e.lineWidth=t.strokeWidth),e.beginPath(),((n=t.renderShadow)!=null?n:t.render).call(t,e),e.stroke()}getShadowColor(t,e){let{data:n}=this.shadowContext.getImageData(t,e,1,1);return rt(st(n[0],n[1],n[2]))}clear(t){t.clearRect(0,0,this.config.width,this.config.height)}emit(t){this.events.next(t)}on(t){return this.events.pipe(W(e=>e.type===t))}destroy(){this.destroyed$.next(),this.destroyed$.complete(),this.events.complete();for(let t of this.shapes)t.destroy()}toObject(){return{width:this.config.width,height:this.config.height,children:this.shapes.map(t=>t.toObject())}}toBlob(t=1){return this.setActivatedShape(null),this.render(),new Promise(e=>{this.config.sceneCanvas.toBlob(n=>e(n),"image/webp",t)})}};c(_,"Stage");var ft=_;export{T as Color,lt as Placement,y as ReferenceLine,mt as ResizeDirection,Y as Shape,ft as Stage,X as angleToRadian,Ct as dotToPixel,O as isLockAspectRatioResize,dt as localDeltaToWorldRightAngle,Rt as millimeterToPixel,St as pixelToDot,Bt as pixelToMillimeter,kt as radianToAngle,at as randomColor,st as rgbToHex,ht as rgba,J as rotateBoxRightAngle,U as rotatePointRightAngle,L as withDpr,rt as withHash,z as withPixel,ct as worldDeltaToLocalRightAngle};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sunspots/core",
3
- "version": "0.0.8",
3
+ "version": "0.1.1",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.mjs",
6
6
  "types": "./dist/index.d.mts",
@@ -28,5 +28,5 @@
28
28
  "peerDependencies": {
29
29
  "rxjs": "^7.8.1"
30
30
  },
31
- "gitHead": "dfa0060059aab5558a0efa98ec46d6362fc144bb"
31
+ "gitHead": "8c984a1f9793fade14eb872f87a063ae8bfbded1"
32
32
  }