@sunspots/core 0.0.7 → 0.1.0

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,55 @@ declare enum ResizeDirection {
39
40
  TopLeft = 7
40
41
  }
41
42
 
43
+ type RightAngleRotation = 0 | 90 | 180 | 270;
44
+ declare function withDpr(value: number): number;
45
+ declare function withPixel(value: number): string;
46
+ declare function withHash(value: string): string;
47
+ declare function randomColor(): string;
48
+ declare function rgbToHex(r: number, g: number, b: number): string;
49
+ 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})`;
50
+ declare function angleToRadian(angle: number): number;
51
+ declare function radianToAngle(radian: number): number;
52
+ /**
53
+ * 将点按直角旋转
54
+ * @param origin
55
+ * @param rotation
56
+ * @returns
57
+ */
58
+ declare function rotatePointRightAngle(origin: Vector2, rotation: RightAngleRotation): Vector2;
59
+ /**
60
+ * 将 world 坐标系中的位移(dx, dy)转换为 local 坐标系中的位移。
61
+ *
62
+ * world 指 canvas 坐标系。
63
+ * local 指以图形左上角为原点,沿图形局部 X/Y 轴方向的坐标系。
64
+ *
65
+ * 由于拖拽/鼠标移动发生在 world 上,做 resize 等操作时常需要转回 local 才能得到沿图形局部轴向的真实位移。
66
+ *
67
+ * @param dx world X
68
+ * @param dy world Y
69
+ * @param rotation
70
+ * @returns
71
+ */
72
+ declare function worldDeltaToLocalRightAngle(dx: number, dy: number, rotation: RightAngleRotation): Vector2;
73
+ /**
74
+ * 将 local 坐标系中的位移转换为 world 坐标系中的位移。
75
+ *
76
+ * @param dx local X
77
+ * @param dy local Y
78
+ * @param rotation
79
+ * @returns
80
+ */
81
+ declare function localDeltaToWorldRightAngle(dx: number, dy: number, rotation: RightAngleRotation): Vector2;
82
+ /**
83
+ * 将一个 box 围绕 origin 进行直角旋转后,返回新的 box
84
+ *
85
+ * @param box
86
+ * @param origin
87
+ * @param rotation
88
+ * @returns
89
+ */
90
+ declare function rotateBoxRightAngle(box: Box, origin: Vector2, rotation: RightAngleRotation): Box;
91
+
42
92
  interface StageConfig {
43
93
  readonly sceneCanvas: HTMLCanvasElement;
44
94
  readonly draggableCanvas: HTMLCanvasElement;
@@ -60,6 +110,7 @@ declare class Stage {
60
110
  private draggableContext;
61
111
  readonly draftContext: CanvasRenderingContext2D;
62
112
  scale: number;
113
+ private snapLines;
63
114
  private readonly events;
64
115
  private readonly destroyed$;
65
116
  constructor(config: StageConfig);
@@ -78,9 +129,10 @@ declare class Stage {
78
129
  private drawScene;
79
130
  private draw;
80
131
  private drawDraggable;
132
+ private drawSnapLines;
81
133
  private drawTransformer;
82
134
  private drawShadow;
83
- private getColor;
135
+ private getShadowColor;
84
136
  private clear;
85
137
  private emit;
86
138
  on(type: StageEventName): Observable<StageEvent>;
@@ -98,6 +150,7 @@ declare class Stage {
98
150
  fill: string | undefined;
99
151
  stroke: string | undefined;
100
152
  strokeWidth: number;
153
+ rotation: RightAngleRotation;
101
154
  } & Record<string, unknown>)[];
102
155
  };
103
156
  toBlob(quality?: number): Promise<Blob>;
@@ -111,6 +164,7 @@ interface ShapeConfig {
111
164
  fill?: string;
112
165
  stroke?: string;
113
166
  strokeWidth?: number;
167
+ rotation?: number;
114
168
  }
115
169
  interface ResizableConfig {
116
170
  /** 横纵比,输入即代表缩放需要保持横纵比 */
@@ -137,10 +191,13 @@ declare abstract class Shape<T extends ShapeConfig = ShapeConfig> implements Sha
137
191
  fill?: string;
138
192
  stroke?: string;
139
193
  strokeWidth: number;
194
+ rotation: RightAngleRotation;
140
195
  constructor(config: T);
141
196
  abstract render(context: CanvasRenderingContext2D): void;
142
197
  abstract boundingBox(): Box;
143
198
  abstract innerBox(): Box;
199
+ worldInnerBox(): Box;
200
+ worldBoundingBox(): Box;
144
201
  renderShadow?(context: CanvasRenderingContext2D): void;
145
202
  setWidth?(width: number): void;
146
203
  setHeight?(height: number): void;
@@ -159,6 +216,7 @@ declare abstract class Shape<T extends ShapeConfig = ShapeConfig> implements Sha
159
216
  fill: string | undefined;
160
217
  stroke: string | undefined;
161
218
  strokeWidth: number;
219
+ rotation: RightAngleRotation;
162
220
  } & Record<string, unknown>;
163
221
  }
164
222
  declare function isLockAspectRatioResize(shape: Shape): shape is Shape & {
@@ -197,11 +255,4 @@ interface PointerEvent {
197
255
  offsetY: number;
198
256
  }
199
257
 
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 };
258
+ 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, isLockAspectRatioResize, localDeltaToWorldRightAngle, 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,xt=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))xt.call(t,e)&&et(i,e,t[e]);return i};var f=(i,t)=>it(i,"name",{value:t,configurable:!0});var ot="#",ut="px";function A(i){return i*devicePixelRatio}f(A,"withDpr");function V(i){return i+ut}f(V,"withPixel");function rt(i){return ot+i}f(rt,"withHash");function st(){let i=(Math.random()*16777215<<0).toString(16);for(;i.length<6;)i=0+i;return ot+i}f(st,"randomColor");function at(i,t,e){return((1<<24)+(i<<16)+(t<<8)+e).toString(16).slice(1)}f(at,"rgbToHex");function ht(i,t,e,n=1){return`rgba(${i},${t},${e},${n})`}f(ht,"rgba");function Y(i){return i*Math.PI/180}f(Y,"angleToRadian");function St(i){return i*180/Math.PI}f(St,"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}}f(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}}}f(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}}}f(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,c=1/0,h=-1/0,d=-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),c=Math.min(c,B.y),h=Math.max(h,B.x),d=Math.max(d,B.y)}return{x:s,y:c,width:h-s,height:d-c}}f(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}};f(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 z=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=st();if(e&&!z.has(e)){this.key=e,z.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(){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)}};f(Q,"Shape");var I=Q;function X(i){return typeof i.resizable=="object"&&!!i.resizable.aspectRatio}f(X,"isLockAspectRatioResize");import{animationFrames as wt,filter as P,Subject as gt,takeUntil as j}from"rxjs";var Z=class Z extends I{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,c=e.y-this.target.y,h=this.getAnchorLocalPoint(e.width,e.height),d={x:s+h.x,y:c+h.y},o=U(d,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:c,rotation:h,strokeWidth:d}=this;e.save(),h&&(e.translate(n,s),e.rotate(Y(h))),e.beginPath(),e.arc(h?0:n,h?0:s,this.getRadius(),0,Math.PI*2,!1),e.fill(),c&&(e.lineWidth=(d!=null?d: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 c;let e=this.innerBox(),n=(c=this.strokeWidth)!=null?c: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}};f(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:c,canvasEvents:h,clientEvents:d}=t;this.context=e.getContext("2d"),this.shadowContext=n.getContext("2d",{willReadFrequently:!0}),this.draggableContext=s.getContext("2d"),this.draftContext=c.getContext("2d");for(let u of this.anchors)u.stage=this;this.resize();let o,a,r,B,D=!1;h.pipe(P(u=>u.type==="pointerdown"),j(this.destroyed$)).subscribe(u=>{if(u.button!==void 0&&u.button!==0)return;D=!0,o=u.offsetX,a=u.offsetY;let $=A(u.offsetX),N=A(u.offsetY),O=this.getShadowColor($,N),S=z.get(O);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()}),d.pipe(P(u=>u.type==="pointermove"),P(()=>D),j(this.destroyed$)).subscribe(u=>{let $=u.offsetX,N=u.offsetY,O=($-o)/this.scale,S=(N-a)/this.scale;if(this.activatedShape instanceof v){this.snapLines=null;let C=this.activatedShape,l=C.target,{rotation:W}=l,H=ct(O,S,W),p=H.x,R=H.y,k=0,w=0,g=r.width,x=r.height;switch(C.direction){case 7:k=p,w=R,x=r.height-R,g=r.width-p;break;case 0:w=R,x=r.height-R,X(l)&&(g=x*l.resizable.aspectRatio);break;case 4:w=R,x=r.height-R,g=r.width+p;break;case 1:g=r.width+p,X(l)&&(x=g/l.resizable.aspectRatio);break;case 5:g=r.width+p,x=r.height+R;break;case 2:x=r.height+R,X(l)&&(g=x*l.resizable.aspectRatio);break;case 6:x=r.height+R,k=p,g=r.width-p;break;case 3:k=p,g=r.width-p,X(l)&&(x=g/l.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)),g=Math.max(M,g),x=Math.max(b,x),k=Math.round(k),w=Math.round(w),g=Math.round(g),x=Math.round(x);let m=dt(k,w,W);l.x=Math.round(B.x+m.x),l.y=Math.round(B.y+m.y),l.setWidth?l.setWidth(Math.max(1,g)):l.width=Math.max(1,g),l.setHeight?l.setHeight(Math.max(1,x)):l.height=Math.max(1,x)}else if(this.activatedShape){let C=Math.round(B.x+O),l=Math.round(B.y+S),W=1/0,H=1/0;this.activatedShape.x=C,this.activatedShape.y=l;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,g=0,x,M;for(let b of R)switch(b.placement){case 0:{let m=p.y+g,F=p.y+p.height/2+g,G=p.y+p.height+g,q=[b.position-m,b.position-F,b.position-G];for(let E of q){let L=Math.abs(E);L<=k&&L<H&&(g=E,H=L,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 E of q){let L=Math.abs(E);L<=k&&L<W&&(w=E,W=L,x=b.position)}break}}C=Math.round(C+w),l=Math.round(l+g),this.snapLines=x!==void 0||M!==void 0?{x,y:M}:null,this.activatedShape.x=C,this.activatedShape.y=l}}),d.pipe(P(u=>u.type==="pointerup"||u.type==="pointercancel"),j(this.destroyed$)).subscribe(()=>{D=!1,this.snapLines=null,this.render()}),wt().pipe(P(()=>D&&!!this.activatedShape),j(this.destroyed$)).subscribe(()=>{this.drawDraggable()})}resize(t){Object.assign(this.config,t);let{sceneCanvas:e,shadowCanvas:n,draggableCanvas:s,draftCanvas:c,width:h,height:d,dpr:o}=this.config;for(let a of[e,n,s,c]){let r=a.getContext("2d");a.style.width=V(h),a.style.height=V(d),a.width=A(h),a.height=A(d),r.scale(o,o)}}zoom(t){let{sceneCanvas:e,shadowCanvas:n,draggableCanvas:s,draftCanvas:c,dpr:h,width:d,height:o}=this.config;for(let a of[e,n,s,c]){let r=a.getContext("2d");a.style.width=V(d*t),a.style.height=V(o*t),a.width=A(d*t),a.height=A(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){this.shapes.splice(this.shapes.indexOf(n),1);for(let s of this.shapes)s.index>n.index&&s.index--;e===n&&this.setActivatedShape(null),this.emit({type:"remove",target:n})}for(let n=0;n<this.shapes.length;n++){let s=this.shapes[n];s.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:c}=t,h=t.boundingBox();if(e.save(),c&&(e.translate(n,s),e.rotate(Y(c))),e.fillStyle=T.White,e.strokeStyle=T.Primary,e.lineWidth=2/this.scale,e.strokeRect(c?h.x-n:h.x,c?h.y-s:h.y,h.width,h.height),e.restore(),!t.resizable)return;let d=[];typeof t.resizable=="object"&&(t.resizable.aspectRatio?d=[0,1,2,3]:t.resizable.directions&&(d=t.resizable.directions));for(let a of this.anchors)d.length&&!d.includes(a.direction)||(e.fillStyle=(o=a.fill)!=null?o:T.Transparent,e.strokeStyle=a.stroke,a.rotation=c,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(at(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(P(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)})}};f(_,"Stage");var ft=_;export{T as Color,lt as Placement,y as ReferenceLine,mt as ResizeDirection,I as Shape,ft as Stage,Y as angleToRadian,X as isLockAspectRatioResize,dt as localDeltaToWorldRightAngle,St as radianToAngle,st as randomColor,at as rgbToHex,ht as rgba,J as rotateBoxRightAngle,U as rotatePointRightAngle,A as withDpr,rt as withHash,V as withPixel,ct as worldDeltaToLocalRightAngle};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sunspots/core",
3
- "version": "0.0.7",
3
+ "version": "0.1.0",
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": "7d737bf18a123e1084a0883b6a7d233182844a8c"
31
+ "gitHead": "1abe322b4ed0bfd15e92931d44a5c2350bdb986e"
32
32
  }