@pooder/kit 2.0.0 → 3.0.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/CHANGELOG.md +11 -0
- package/dist/index.d.mts +246 -134
- package/dist/index.d.ts +246 -134
- package/dist/index.js +2051 -1045
- package/dist/index.mjs +2042 -1050
- package/package.json +3 -2
- package/src/CanvasService.ts +65 -0
- package/src/background.ts +156 -109
- package/src/coordinate.ts +49 -0
- package/src/dieline.ts +536 -336
- package/src/film.ts +120 -89
- package/src/geometry.ts +251 -38
- package/src/hole.ts +422 -286
- package/src/image.ts +374 -174
- package/src/index.ts +1 -0
- package/src/mirror.ts +86 -49
- package/src/ruler.ts +188 -118
- package/src/tracer.ts +372 -0
- package/src/white-ink.ts +186 -142
package/src/image.ts
CHANGED
|
@@ -1,102 +1,211 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
CommandContribution,
|
|
3
|
+
ConfigurationContribution,
|
|
4
|
+
ConfigurationService,
|
|
5
|
+
ContributionPointIds,
|
|
5
6
|
Extension,
|
|
6
|
-
|
|
7
|
-
OptionSchema,
|
|
8
|
-
PooderLayer,
|
|
9
|
-
util,
|
|
10
|
-
Point,
|
|
7
|
+
ExtensionContext,
|
|
11
8
|
} from "@pooder/core";
|
|
9
|
+
import { FabricImage as Image, Point, util } from "fabric";
|
|
10
|
+
import CanvasService from "./CanvasService";
|
|
11
|
+
import { Coordinate } from "./coordinate";
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
opacity: number;
|
|
16
|
-
width?: number;
|
|
17
|
-
height?: number;
|
|
18
|
-
angle?: number;
|
|
19
|
-
left?: number;
|
|
20
|
-
top?: number;
|
|
21
|
-
}
|
|
22
|
-
export class ImageTool implements Extension<ImageToolOptions> {
|
|
23
|
-
name = "ImageTool";
|
|
24
|
-
private _loadingUrl: string | null = null;
|
|
25
|
-
options: ImageToolOptions = {
|
|
26
|
-
url: "",
|
|
27
|
-
opacity: 1,
|
|
28
|
-
};
|
|
13
|
+
export class ImageTool implements Extension {
|
|
14
|
+
id = "pooder.kit.image";
|
|
29
15
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
type: "string",
|
|
33
|
-
label: "Image URL",
|
|
34
|
-
},
|
|
35
|
-
opacity: {
|
|
36
|
-
type: "number",
|
|
37
|
-
min: 0,
|
|
38
|
-
max: 1,
|
|
39
|
-
step: 0.1,
|
|
40
|
-
label: "Opacity",
|
|
41
|
-
},
|
|
42
|
-
width: {
|
|
43
|
-
type: "number",
|
|
44
|
-
label: "Width",
|
|
45
|
-
min: 0,
|
|
46
|
-
max: 5000,
|
|
47
|
-
},
|
|
48
|
-
height: {
|
|
49
|
-
type: "number",
|
|
50
|
-
label: "Height",
|
|
51
|
-
min: 0,
|
|
52
|
-
max: 5000,
|
|
53
|
-
},
|
|
54
|
-
angle: {
|
|
55
|
-
type: "number",
|
|
56
|
-
label: "Rotation",
|
|
57
|
-
min: 0,
|
|
58
|
-
max: 360,
|
|
59
|
-
},
|
|
60
|
-
left: {
|
|
61
|
-
type: "number",
|
|
62
|
-
label: "Left",
|
|
63
|
-
min: 0,
|
|
64
|
-
max: 1000,
|
|
65
|
-
},
|
|
66
|
-
top: {
|
|
67
|
-
type: "number",
|
|
68
|
-
label: "Top",
|
|
69
|
-
min: 0,
|
|
70
|
-
max: 1000,
|
|
71
|
-
},
|
|
16
|
+
metadata = {
|
|
17
|
+
name: "ImageTool",
|
|
72
18
|
};
|
|
19
|
+
private _loadingUrl: string | null = null;
|
|
20
|
+
|
|
21
|
+
private url: string = "";
|
|
22
|
+
private opacity: number = 1;
|
|
23
|
+
private width?: number;
|
|
24
|
+
private height?: number;
|
|
25
|
+
private angle?: number;
|
|
26
|
+
private left?: number;
|
|
27
|
+
private top?: number;
|
|
28
|
+
|
|
29
|
+
private canvasService?: CanvasService;
|
|
30
|
+
private context?: ExtensionContext;
|
|
31
|
+
|
|
32
|
+
constructor(
|
|
33
|
+
options?: Partial<{
|
|
34
|
+
url: string;
|
|
35
|
+
opacity: number;
|
|
36
|
+
width: number;
|
|
37
|
+
height: number;
|
|
38
|
+
angle: number;
|
|
39
|
+
left: number;
|
|
40
|
+
top: number;
|
|
41
|
+
}>,
|
|
42
|
+
) {
|
|
43
|
+
if (options) {
|
|
44
|
+
Object.assign(this, options);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
activate(context: ExtensionContext) {
|
|
49
|
+
this.context = context;
|
|
50
|
+
this.canvasService = context.services.get<CanvasService>("CanvasService");
|
|
51
|
+
if (!this.canvasService) {
|
|
52
|
+
console.warn("CanvasService not found for ImageTool");
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
73
55
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
56
|
+
const configService = context.services.get<any>("ConfigurationService");
|
|
57
|
+
if (configService) {
|
|
58
|
+
// Load initial config
|
|
59
|
+
this.url = configService.get("image.url", this.url);
|
|
60
|
+
this.opacity = configService.get("image.opacity", this.opacity);
|
|
61
|
+
this.width = configService.get("image.width", this.width);
|
|
62
|
+
this.height = configService.get("image.height", this.height);
|
|
63
|
+
this.angle = configService.get("image.angle", this.angle);
|
|
64
|
+
this.left = configService.get("image.left", this.left);
|
|
65
|
+
this.top = configService.get("image.top", this.top);
|
|
66
|
+
|
|
67
|
+
// Listen for changes
|
|
68
|
+
configService.onAnyChange((e: { key: string; value: any }) => {
|
|
69
|
+
if (e.key.startsWith("image.")) {
|
|
70
|
+
const prop = e.key.split(".")[1];
|
|
71
|
+
console.log(
|
|
72
|
+
`[ImageTool] Config change detected: ${e.key} -> ${e.value}`,
|
|
73
|
+
);
|
|
74
|
+
if (prop && prop in this) {
|
|
75
|
+
(this as any)[prop] = e.value;
|
|
76
|
+
this.updateImage();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
this.ensureLayer();
|
|
83
|
+
this.updateImage();
|
|
77
84
|
}
|
|
78
85
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
+
deactivate(context: ExtensionContext) {
|
|
87
|
+
if (this.canvasService) {
|
|
88
|
+
const layer = this.canvasService.getLayer("user");
|
|
89
|
+
if (layer) {
|
|
90
|
+
const userImage = this.canvasService.getObject("user-image", "user");
|
|
91
|
+
if (userImage) {
|
|
92
|
+
layer.remove(userImage);
|
|
93
|
+
this.canvasService.requestRenderAll();
|
|
94
|
+
}
|
|
86
95
|
}
|
|
96
|
+
this.canvasService = undefined;
|
|
97
|
+
this.context = undefined;
|
|
87
98
|
}
|
|
88
99
|
}
|
|
89
100
|
|
|
90
|
-
|
|
91
|
-
|
|
101
|
+
contribute() {
|
|
102
|
+
return {
|
|
103
|
+
[ContributionPointIds.CONFIGURATIONS]: [
|
|
104
|
+
{
|
|
105
|
+
id: "image.url",
|
|
106
|
+
type: "string",
|
|
107
|
+
label: "Image URL",
|
|
108
|
+
default: this.url,
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
id: "image.opacity",
|
|
112
|
+
type: "number",
|
|
113
|
+
label: "Opacity",
|
|
114
|
+
min: 0,
|
|
115
|
+
max: 1,
|
|
116
|
+
step: 0.1,
|
|
117
|
+
default: this.opacity,
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
id: "image.width",
|
|
121
|
+
type: "number",
|
|
122
|
+
label: "Width",
|
|
123
|
+
min: 0,
|
|
124
|
+
max: 5000,
|
|
125
|
+
default: this.width,
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
id: "image.height",
|
|
129
|
+
type: "number",
|
|
130
|
+
label: "Height",
|
|
131
|
+
min: 0,
|
|
132
|
+
max: 5000,
|
|
133
|
+
default: this.height,
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
id: "image.angle",
|
|
137
|
+
type: "number",
|
|
138
|
+
label: "Rotation",
|
|
139
|
+
min: 0,
|
|
140
|
+
max: 360,
|
|
141
|
+
default: this.angle,
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
id: "image.left",
|
|
145
|
+
type: "number",
|
|
146
|
+
label: "Left (Normalized)",
|
|
147
|
+
min: 0,
|
|
148
|
+
max: 1,
|
|
149
|
+
default: this.left,
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
id: "image.top",
|
|
153
|
+
type: "number",
|
|
154
|
+
label: "Top (Normalized)",
|
|
155
|
+
min: 0,
|
|
156
|
+
max: 1,
|
|
157
|
+
default: this.top,
|
|
158
|
+
},
|
|
159
|
+
] as ConfigurationContribution[],
|
|
160
|
+
[ContributionPointIds.COMMANDS]: [
|
|
161
|
+
{
|
|
162
|
+
command: "setUserImage",
|
|
163
|
+
title: "Set User Image",
|
|
164
|
+
handler: (
|
|
165
|
+
url: string,
|
|
166
|
+
opacity: number,
|
|
167
|
+
width?: number,
|
|
168
|
+
height?: number,
|
|
169
|
+
angle?: number,
|
|
170
|
+
left?: number,
|
|
171
|
+
top?: number,
|
|
172
|
+
) => {
|
|
173
|
+
if (
|
|
174
|
+
this.url === url &&
|
|
175
|
+
this.opacity === opacity &&
|
|
176
|
+
this.width === width &&
|
|
177
|
+
this.height === height &&
|
|
178
|
+
this.angle === angle &&
|
|
179
|
+
this.left === left &&
|
|
180
|
+
this.top === top
|
|
181
|
+
)
|
|
182
|
+
return true;
|
|
183
|
+
|
|
184
|
+
this.url = url;
|
|
185
|
+
this.opacity = opacity;
|
|
186
|
+
this.width = width;
|
|
187
|
+
this.height = height;
|
|
188
|
+
this.angle = angle;
|
|
189
|
+
this.left = left;
|
|
190
|
+
this.top = top;
|
|
191
|
+
|
|
192
|
+
// Direct update
|
|
193
|
+
this.updateImage();
|
|
194
|
+
|
|
195
|
+
return true;
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
] as CommandContribution[],
|
|
199
|
+
};
|
|
92
200
|
}
|
|
93
201
|
|
|
94
|
-
private ensureLayer(
|
|
95
|
-
|
|
202
|
+
private ensureLayer() {
|
|
203
|
+
if (!this.canvasService) return;
|
|
204
|
+
let userLayer = this.canvasService.getLayer("user");
|
|
96
205
|
if (!userLayer) {
|
|
97
|
-
userLayer =
|
|
98
|
-
width:
|
|
99
|
-
height:
|
|
206
|
+
userLayer = this.canvasService.createLayer("user", {
|
|
207
|
+
width: this.canvasService.canvas.width,
|
|
208
|
+
height: this.canvasService.canvas.height,
|
|
100
209
|
left: 0,
|
|
101
210
|
top: 0,
|
|
102
211
|
originX: "left",
|
|
@@ -105,24 +214,40 @@ export class ImageTool implements Extension<ImageToolOptions> {
|
|
|
105
214
|
evented: true,
|
|
106
215
|
subTargetCheck: true,
|
|
107
216
|
interactive: true,
|
|
108
|
-
data: {
|
|
109
|
-
id: "user",
|
|
110
|
-
},
|
|
111
217
|
});
|
|
112
|
-
|
|
218
|
+
|
|
219
|
+
// Try to insert below dieline-overlay
|
|
220
|
+
const dielineLayer = this.canvasService.getLayer("dieline-overlay");
|
|
221
|
+
if (dielineLayer) {
|
|
222
|
+
const index = this.canvasService.canvas
|
|
223
|
+
.getObjects()
|
|
224
|
+
.indexOf(dielineLayer);
|
|
225
|
+
// If dieline is at 0, move user to 0 (dieline shifts to 1)
|
|
226
|
+
if (index >= 0) {
|
|
227
|
+
this.canvasService.canvas.moveObjectTo(userLayer, index);
|
|
228
|
+
}
|
|
229
|
+
} else {
|
|
230
|
+
// Ensure background is behind
|
|
231
|
+
const bgLayer = this.canvasService.getLayer("background");
|
|
232
|
+
if (bgLayer) {
|
|
233
|
+
this.canvasService.canvas.sendObjectToBack(bgLayer);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
this.canvasService.requestRenderAll();
|
|
113
237
|
}
|
|
114
238
|
}
|
|
115
239
|
|
|
116
|
-
private updateImage(
|
|
117
|
-
|
|
240
|
+
private updateImage() {
|
|
241
|
+
if (!this.canvasService) return;
|
|
242
|
+
let { url, opacity, width, height, angle, left, top } = this;
|
|
118
243
|
|
|
119
|
-
const layer =
|
|
244
|
+
const layer = this.canvasService.getLayer("user");
|
|
120
245
|
if (!layer) {
|
|
121
246
|
console.warn("[ImageTool] User layer not found");
|
|
122
247
|
return;
|
|
123
248
|
}
|
|
124
249
|
|
|
125
|
-
const userImage =
|
|
250
|
+
const userImage = this.canvasService.getObject("user-image", "user") as any;
|
|
126
251
|
|
|
127
252
|
if (this._loadingUrl === url) return;
|
|
128
253
|
|
|
@@ -130,24 +255,37 @@ export class ImageTool implements Extension<ImageToolOptions> {
|
|
|
130
255
|
const currentSrc = userImage.getSrc?.() || userImage._element?.src;
|
|
131
256
|
|
|
132
257
|
if (currentSrc !== url) {
|
|
133
|
-
this.loadImage(
|
|
258
|
+
this.loadImage(layer);
|
|
134
259
|
} else {
|
|
135
260
|
const updates: any = {};
|
|
136
|
-
const
|
|
137
|
-
const
|
|
261
|
+
const canvasW = this.canvasService.canvas.width || 800;
|
|
262
|
+
const canvasH = this.canvasService.canvas.height || 600;
|
|
263
|
+
const centerX = canvasW / 2;
|
|
264
|
+
const centerY = canvasH / 2;
|
|
138
265
|
|
|
139
266
|
if (userImage.opacity !== opacity) updates.opacity = opacity;
|
|
140
267
|
if (angle !== undefined && userImage.angle !== angle)
|
|
141
268
|
updates.angle = angle;
|
|
142
269
|
|
|
270
|
+
if (userImage.originX !== "center") {
|
|
271
|
+
userImage.set({
|
|
272
|
+
originX: "center",
|
|
273
|
+
originY: "center",
|
|
274
|
+
left: userImage.left + (userImage.width * userImage.scaleX) / 2,
|
|
275
|
+
top: userImage.top + (userImage.height * userImage.scaleY) / 2,
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
|
|
143
279
|
if (left !== undefined) {
|
|
144
|
-
const
|
|
280
|
+
const globalLeft = Coordinate.toAbsolute(left, canvasW);
|
|
281
|
+
const localLeft = globalLeft - centerX;
|
|
145
282
|
if (Math.abs(userImage.left - localLeft) > 1)
|
|
146
283
|
updates.left = localLeft;
|
|
147
284
|
}
|
|
148
285
|
|
|
149
286
|
if (top !== undefined) {
|
|
150
|
-
const
|
|
287
|
+
const globalTop = Coordinate.toAbsolute(top, canvasH);
|
|
288
|
+
const localTop = globalTop - centerY;
|
|
151
289
|
if (Math.abs(userImage.top - localTop) > 1) updates.top = localTop;
|
|
152
290
|
}
|
|
153
291
|
|
|
@@ -158,31 +296,80 @@ export class ImageTool implements Extension<ImageToolOptions> {
|
|
|
158
296
|
|
|
159
297
|
if (Object.keys(updates).length > 0) {
|
|
160
298
|
userImage.set(updates);
|
|
161
|
-
|
|
299
|
+
layer.dirty = true;
|
|
300
|
+
this.canvasService.requestRenderAll();
|
|
162
301
|
}
|
|
163
302
|
}
|
|
164
303
|
} else {
|
|
165
|
-
this.loadImage(
|
|
304
|
+
this.loadImage(layer);
|
|
166
305
|
}
|
|
167
306
|
}
|
|
168
307
|
|
|
169
|
-
private loadImage(
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
) {
|
|
174
|
-
const { url } = opts;
|
|
308
|
+
private loadImage(layer: any) {
|
|
309
|
+
if (!this.canvasService) return;
|
|
310
|
+
const { url } = this;
|
|
311
|
+
if (!url) return; // Don't load if empty
|
|
175
312
|
this._loadingUrl = url;
|
|
176
313
|
|
|
177
|
-
Image.fromURL(url)
|
|
314
|
+
Image.fromURL(url, { crossOrigin: "anonymous" })
|
|
178
315
|
.then((image) => {
|
|
179
316
|
if (this._loadingUrl !== url) return;
|
|
180
317
|
this._loadingUrl = null;
|
|
181
318
|
|
|
182
|
-
|
|
183
|
-
|
|
319
|
+
let { opacity, width, height, angle, left, top } = this;
|
|
320
|
+
|
|
321
|
+
// Auto-scale and center if not set
|
|
322
|
+
if (this.context) {
|
|
323
|
+
const configService = this.context.services.get<ConfigurationService>(
|
|
324
|
+
"ConfigurationService",
|
|
325
|
+
)!;
|
|
326
|
+
const dielineWidth = configService.get("dieline.width");
|
|
327
|
+
const dielineHeight = configService.get("dieline.height");
|
|
328
|
+
|
|
329
|
+
console.log(
|
|
330
|
+
"[ImageTool] Dieline config debug:",
|
|
331
|
+
{
|
|
332
|
+
widthVal: dielineWidth,
|
|
333
|
+
heightVal: dielineHeight,
|
|
334
|
+
// Debug: dump all keys to see what is available
|
|
335
|
+
allKeys: Array.from(
|
|
336
|
+
(configService as any).configValues?.keys() || [],
|
|
337
|
+
),
|
|
338
|
+
},
|
|
339
|
+
configService,
|
|
340
|
+
);
|
|
341
|
+
|
|
342
|
+
if (width === undefined && height === undefined) {
|
|
343
|
+
// Scale to fit dieline
|
|
344
|
+
const scale = Math.min(
|
|
345
|
+
dielineWidth / (image.width || 1),
|
|
346
|
+
dielineHeight / (image.height || 1),
|
|
347
|
+
);
|
|
348
|
+
width = (image.width || 1) * scale;
|
|
349
|
+
height = (image.height || 1) * scale;
|
|
350
|
+
this.width = width;
|
|
351
|
+
this.height = height;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (left === undefined && top === undefined) {
|
|
355
|
+
// Default to Dieline Position if available, otherwise Center (0.5)
|
|
356
|
+
const dielinePos = configService?.get("dieline.position");
|
|
357
|
+
if (dielinePos) {
|
|
358
|
+
this.left = dielinePos.x;
|
|
359
|
+
this.top = dielinePos.y;
|
|
360
|
+
} else {
|
|
361
|
+
this.left = 0.5;
|
|
362
|
+
this.top = 0.5;
|
|
363
|
+
}
|
|
364
|
+
left = this.left;
|
|
365
|
+
top = this.top;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
184
368
|
|
|
185
|
-
const existingImage =
|
|
369
|
+
const existingImage = this.canvasService!.getObject(
|
|
370
|
+
"user-image",
|
|
371
|
+
"user",
|
|
372
|
+
) as any;
|
|
186
373
|
|
|
187
374
|
if (existingImage) {
|
|
188
375
|
const defaultLeft = existingImage.left;
|
|
@@ -191,9 +378,51 @@ export class ImageTool implements Extension<ImageToolOptions> {
|
|
|
191
378
|
const defaultScaleX = existingImage.scaleX;
|
|
192
379
|
const defaultScaleY = existingImage.scaleY;
|
|
193
380
|
|
|
381
|
+
const canvasW = this.canvasService?.canvas.width || 800;
|
|
382
|
+
const canvasH = this.canvasService?.canvas.height || 600;
|
|
383
|
+
const centerX = canvasW / 2;
|
|
384
|
+
const centerY = canvasH / 2;
|
|
385
|
+
|
|
386
|
+
let targetLeft = left !== undefined ? left : defaultLeft;
|
|
387
|
+
let targetTop = top !== undefined ? top : defaultTop;
|
|
388
|
+
|
|
389
|
+
// Log for debugging
|
|
390
|
+
const configService = this.context?.services.get<any>(
|
|
391
|
+
"ConfigurationService",
|
|
392
|
+
);
|
|
393
|
+
console.log("[ImageTool] Loading EXISTING image...", {
|
|
394
|
+
canvasW,
|
|
395
|
+
canvasH,
|
|
396
|
+
centerX,
|
|
397
|
+
centerY,
|
|
398
|
+
incomingLeft: left,
|
|
399
|
+
incomingTop: top,
|
|
400
|
+
dielinePos: configService?.get("dieline.position"),
|
|
401
|
+
existingImage: !!existingImage,
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
if (left !== undefined) {
|
|
405
|
+
const globalLeft = Coordinate.toAbsolute(left, canvasW);
|
|
406
|
+
targetLeft = globalLeft; // Layer is absolute, do not subtract center
|
|
407
|
+
console.log("[ImageTool] Calculated targetLeft", {
|
|
408
|
+
globalLeft,
|
|
409
|
+
targetLeft,
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
if (top !== undefined) {
|
|
413
|
+
const globalTop = Coordinate.toAbsolute(top, canvasH);
|
|
414
|
+
targetTop = globalTop; // Layer is absolute, do not subtract center
|
|
415
|
+
console.log("[ImageTool] Calculated targetTop", {
|
|
416
|
+
globalTop,
|
|
417
|
+
targetTop,
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
|
|
194
421
|
image.set({
|
|
195
|
-
|
|
196
|
-
|
|
422
|
+
originX: "center", // Use center origin for easier positioning
|
|
423
|
+
originY: "center",
|
|
424
|
+
left: targetLeft,
|
|
425
|
+
top: targetTop,
|
|
197
426
|
angle: angle !== undefined ? angle : defaultAngle,
|
|
198
427
|
scaleX:
|
|
199
428
|
width !== undefined && image.width
|
|
@@ -207,14 +436,34 @@ export class ImageTool implements Extension<ImageToolOptions> {
|
|
|
207
436
|
|
|
208
437
|
layer.remove(existingImage);
|
|
209
438
|
} else {
|
|
439
|
+
// New image
|
|
440
|
+
image.set({
|
|
441
|
+
originX: "center",
|
|
442
|
+
originY: "center",
|
|
443
|
+
});
|
|
444
|
+
|
|
210
445
|
if (width !== undefined && image.width)
|
|
211
446
|
image.scaleX = width / image.width;
|
|
212
447
|
if (height !== undefined && image.height)
|
|
213
448
|
image.scaleY = height / image.height;
|
|
214
449
|
if (angle !== undefined) image.angle = angle;
|
|
215
450
|
|
|
216
|
-
|
|
217
|
-
|
|
451
|
+
const canvasW = this.canvasService?.canvas.width || 800;
|
|
452
|
+
const canvasH = this.canvasService?.canvas.height || 600;
|
|
453
|
+
const centerX = canvasW / 2;
|
|
454
|
+
const centerY = canvasH / 2;
|
|
455
|
+
|
|
456
|
+
if (left !== undefined) {
|
|
457
|
+
image.left = Coordinate.toAbsolute(left, canvasW); // Layer is absolute
|
|
458
|
+
} else {
|
|
459
|
+
image.left = centerX; // Default to center of canvas
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
if (top !== undefined) {
|
|
463
|
+
image.top = Coordinate.toAbsolute(top, canvasH); // Layer is absolute
|
|
464
|
+
} else {
|
|
465
|
+
image.top = centerY; // Default to center of canvas
|
|
466
|
+
}
|
|
218
467
|
}
|
|
219
468
|
|
|
220
469
|
image.set({
|
|
@@ -226,79 +475,30 @@ export class ImageTool implements Extension<ImageToolOptions> {
|
|
|
226
475
|
layer.add(image);
|
|
227
476
|
|
|
228
477
|
// Bind events to keep options in sync
|
|
229
|
-
image.on("modified", (e) => {
|
|
478
|
+
image.on("modified", (e: any) => {
|
|
230
479
|
const matrix = image.calcTransformMatrix();
|
|
231
480
|
const globalPoint = util.transformPoint(new Point(0, 0), matrix);
|
|
481
|
+
const canvasW = this.canvasService?.canvas.width || 800;
|
|
482
|
+
const canvasH = this.canvasService?.canvas.height || 600;
|
|
232
483
|
|
|
233
|
-
this.
|
|
234
|
-
this.
|
|
235
|
-
this.
|
|
484
|
+
this.left = Coordinate.toNormalized(globalPoint.x, canvasW);
|
|
485
|
+
this.top = Coordinate.toNormalized(globalPoint.y, canvasH);
|
|
486
|
+
this.angle = e.target.angle;
|
|
236
487
|
|
|
237
|
-
if (image.width)
|
|
238
|
-
|
|
239
|
-
if (image.height)
|
|
240
|
-
this.options.height = e.target.height * e.target.scaleY;
|
|
488
|
+
if (image.width) this.width = e.target.width * e.target.scaleX;
|
|
489
|
+
if (image.height) this.height = e.target.height * e.target.scaleY;
|
|
241
490
|
|
|
242
|
-
|
|
491
|
+
if (this.context) {
|
|
492
|
+
this.context.eventBus.emit("update");
|
|
493
|
+
}
|
|
243
494
|
});
|
|
244
495
|
|
|
245
|
-
|
|
496
|
+
layer.dirty = true;
|
|
497
|
+
this.canvasService!.requestRenderAll();
|
|
246
498
|
})
|
|
247
499
|
.catch((err) => {
|
|
248
500
|
if (this._loadingUrl === url) this._loadingUrl = null;
|
|
249
501
|
console.error("Failed to load image", url, err);
|
|
250
502
|
});
|
|
251
503
|
}
|
|
252
|
-
|
|
253
|
-
commands: Record<string, Command> = {
|
|
254
|
-
setUserImage: {
|
|
255
|
-
execute: (
|
|
256
|
-
editor: Editor,
|
|
257
|
-
url: string,
|
|
258
|
-
opacity: number,
|
|
259
|
-
width?: number,
|
|
260
|
-
height?: number,
|
|
261
|
-
angle?: number,
|
|
262
|
-
left?: number,
|
|
263
|
-
top?: number,
|
|
264
|
-
) => {
|
|
265
|
-
if (
|
|
266
|
-
this.options.url === url &&
|
|
267
|
-
this.options.opacity === opacity &&
|
|
268
|
-
this.options.width === width &&
|
|
269
|
-
this.options.height === height &&
|
|
270
|
-
this.options.angle === angle &&
|
|
271
|
-
this.options.left === left &&
|
|
272
|
-
this.options.top === top
|
|
273
|
-
)
|
|
274
|
-
return true;
|
|
275
|
-
|
|
276
|
-
this.options = { url, opacity, width, height, angle, left, top };
|
|
277
|
-
|
|
278
|
-
// Direct update
|
|
279
|
-
this.updateImage(editor, this.options);
|
|
280
|
-
|
|
281
|
-
return true;
|
|
282
|
-
},
|
|
283
|
-
schema: {
|
|
284
|
-
url: {
|
|
285
|
-
type: "string",
|
|
286
|
-
label: "Image URL",
|
|
287
|
-
required: true,
|
|
288
|
-
},
|
|
289
|
-
opacity: {
|
|
290
|
-
type: "number",
|
|
291
|
-
label: "Opacity",
|
|
292
|
-
min: 0,
|
|
293
|
-
max: 1,
|
|
294
|
-
required: true,
|
|
295
|
-
},
|
|
296
|
-
width: { type: "number", label: "Width" },
|
|
297
|
-
height: { type: "number", label: "Height" },
|
|
298
|
-
angle: { type: "number", label: "Angle" },
|
|
299
|
-
left: { type: "number", label: "Left" },
|
|
300
|
-
top: { type: "number", label: "Top" },
|
|
301
|
-
},
|
|
302
|
-
},
|
|
303
|
-
};
|
|
304
504
|
}
|
package/src/index.ts
CHANGED