@hprint/plugins 0.0.9-alpha.1 → 0.0.10

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.
@@ -1,5 +1,5 @@
1
- import { fabric } from '@hprint/core';
2
- import type { IEditor, IPluginTempl } from '@hprint/core';
1
+ import { fabric } from '@hprint/core';
2
+ import type { IEditor, IPluginTempl } from '@hprint/core';
3
3
 
4
4
  type IPlugin = Pick<
5
5
  GroupAlignPlugin,
@@ -31,10 +31,22 @@ class GroupAlignPlugin implements IPluginTempl {
31
31
  'yequation',
32
32
  ];
33
33
  // public hotkeys: string[] = ['space'];
34
- constructor(
35
- public canvas: fabric.Canvas,
36
- public editor: IEditor
37
- ) { }
34
+ constructor(
35
+ public canvas: fabric.Canvas,
36
+ public editor: IEditor
37
+ ) { }
38
+
39
+ _refreshObjectCoordinates(objects: fabric.Object[]) {
40
+ objects.forEach((item) => {
41
+ item.setCoords();
42
+ (item as any).syncOriginSizeByUnit?.(['left', 'top']);
43
+ });
44
+ }
45
+
46
+ _notifySelectionModified(selection: fabric.ActiveSelection) {
47
+ this.canvas.fire('object:modified', { target: selection });
48
+ this.canvas.requestRenderAll();
49
+ }
38
50
 
39
51
  left() {
40
52
  const { canvas } = this;
@@ -51,15 +63,16 @@ class GroupAlignPlugin implements IPluginTempl {
51
63
  });
52
64
  item.setCoords();
53
65
  });
54
- const activeSelection = new fabric.ActiveSelection(selectObjects, {
66
+ this._refreshObjectCoordinates(selectObjects);
67
+ const activeSelection = new fabric.ActiveSelection(selectObjects, {
55
68
  canvas: canvas,
56
69
  });
57
70
  canvas.setActiveObject(activeSelection);
58
- canvas.requestRenderAll();
71
+ this._notifySelectionModified(activeSelection);
59
72
  }
60
73
  }
61
74
 
62
- right() {
75
+ right() {
63
76
  const { canvas } = this;
64
77
 
65
78
  const activeObject = canvas.getActiveObject();
@@ -77,15 +90,16 @@ class GroupAlignPlugin implements IPluginTempl {
77
90
  Number(item.left),
78
91
  });
79
92
  });
80
- const activeSelection = new fabric.ActiveSelection(selectObjects, {
93
+ this._refreshObjectCoordinates(selectObjects);
94
+ const activeSelection = new fabric.ActiveSelection(selectObjects, {
81
95
  canvas: canvas,
82
96
  });
83
97
  canvas.setActiveObject(activeSelection);
84
- canvas.requestRenderAll();
98
+ this._notifySelectionModified(activeSelection);
85
99
  }
86
100
  }
87
101
 
88
- xcenter() {
102
+ xcenter() {
89
103
  const { canvas } = this;
90
104
 
91
105
  const activeObject = canvas.getActiveObject();
@@ -103,15 +117,16 @@ class GroupAlignPlugin implements IPluginTempl {
103
117
  Number(item.left),
104
118
  });
105
119
  });
106
- const activeSelection = new fabric.ActiveSelection(selectObjects, {
120
+ this._refreshObjectCoordinates(selectObjects);
121
+ const activeSelection = new fabric.ActiveSelection(selectObjects, {
107
122
  canvas: canvas,
108
123
  });
109
124
  canvas.setActiveObject(activeSelection);
110
- canvas.requestRenderAll();
125
+ this._notifySelectionModified(activeSelection);
111
126
  }
112
127
  }
113
128
 
114
- ycenter() {
129
+ ycenter() {
115
130
  const { canvas } = this;
116
131
 
117
132
  const activeObject = canvas.getActiveObject();
@@ -129,15 +144,16 @@ class GroupAlignPlugin implements IPluginTempl {
129
144
  Number(item.top),
130
145
  });
131
146
  });
132
- const activeSelection = new fabric.ActiveSelection(selectObjects, {
147
+ this._refreshObjectCoordinates(selectObjects);
148
+ const activeSelection = new fabric.ActiveSelection(selectObjects, {
133
149
  canvas: canvas,
134
150
  });
135
151
  canvas.setActiveObject(activeSelection);
136
- canvas.requestRenderAll();
152
+ this._notifySelectionModified(activeSelection);
137
153
  }
138
154
  }
139
155
 
140
- top() {
156
+ top() {
141
157
  const { canvas } = this;
142
158
 
143
159
  const activeObject = canvas.getActiveObject();
@@ -151,15 +167,16 @@ class GroupAlignPlugin implements IPluginTempl {
151
167
  top: top - bounding.top + Number(item.top),
152
168
  });
153
169
  });
154
- const activeSelection = new fabric.ActiveSelection(selectObjects, {
170
+ this._refreshObjectCoordinates(selectObjects);
171
+ const activeSelection = new fabric.ActiveSelection(selectObjects, {
155
172
  canvas: canvas,
156
173
  });
157
174
  canvas.setActiveObject(activeSelection);
158
- canvas.requestRenderAll();
175
+ this._notifySelectionModified(activeSelection);
159
176
  }
160
177
  }
161
178
 
162
- bottom() {
179
+ bottom() {
163
180
  const { canvas } = this;
164
181
 
165
182
  const activeObject = canvas.getActiveObject();
@@ -177,11 +194,12 @@ class GroupAlignPlugin implements IPluginTempl {
177
194
  Number(item.top),
178
195
  });
179
196
  });
180
- const activeSelection = new fabric.ActiveSelection(selectObjects, {
197
+ this._refreshObjectCoordinates(selectObjects);
198
+ const activeSelection = new fabric.ActiveSelection(selectObjects, {
181
199
  canvas: canvas,
182
200
  });
183
201
  canvas.setActiveObject(activeSelection);
184
- canvas.requestRenderAll();
202
+ this._notifySelectionModified(activeSelection);
185
203
  }
186
204
  }
187
205
 
@@ -256,21 +274,23 @@ class GroupAlignPlugin implements IPluginTempl {
256
274
 
257
275
  const objecs = canvas.getActiveObjects();
258
276
  canvas.discardActiveObject();
259
- objecs.forEach((item: fabric.Object) => {
260
- let x = Infinity;
277
+ objecs.forEach((item: fabric.Object) => {
278
+ let x = Infinity;
261
279
  for (const key in item.aCoords) {
262
280
  if (item.aCoords[key].x < x) {
263
281
  x = item.aCoords[key].x;
264
282
  }
265
283
  }
266
- item.set('left', 2 * item.left - x);
267
- });
268
-
269
- const sel = new fabric.ActiveSelection(objecs, {
284
+ item.set('left', 2 * item.left - x);
285
+ });
286
+
287
+ this._refreshObjectCoordinates(objecs);
288
+
289
+ const sel = new fabric.ActiveSelection(objecs, {
270
290
  canvas: canvas,
271
291
  });
272
292
  canvas.setActiveObject(sel);
273
- canvas.requestRenderAll();
293
+ this._notifySelectionModified(sel);
274
294
  }
275
295
 
276
296
  yequation() {
@@ -340,21 +360,23 @@ class GroupAlignPlugin implements IPluginTempl {
340
360
 
341
361
  const objecs = canvas.getActiveObjects();
342
362
  canvas.discardActiveObject();
343
- objecs.forEach((item) => {
344
- let y = Infinity;
363
+ objecs.forEach((item) => {
364
+ let y = Infinity;
345
365
  for (const key in item.aCoords) {
346
366
  if (item.aCoords[key].y < y) {
347
367
  y = item.aCoords[key].y;
348
368
  }
349
369
  }
350
- item.set('top', 2 * item.top - y);
351
- });
352
-
353
- const sel = new fabric.ActiveSelection(objecs, {
370
+ item.set('top', 2 * item.top - y);
371
+ });
372
+
373
+ this._refreshObjectCoordinates(objecs);
374
+
375
+ const sel = new fabric.ActiveSelection(objecs, {
354
376
  canvas: canvas,
355
377
  });
356
378
  canvas.setActiveObject(sel);
357
- canvas.requestRenderAll();
379
+ this._notifySelectionModified(sel);
358
380
  }
359
381
 
360
382
  destroy() {
@@ -1,7 +1,6 @@
1
- import type { IEditor, IPluginTempl } from '@hprint/core';
2
- import { LengthConvert } from '@hprint/shared';
3
- import { syncMmFromObject } from '../utils/units';
4
- import { fabric } from '@hprint/core';
1
+ import type { IEditor, IPluginTempl } from '@hprint/core';
2
+ import { LengthConvert } from '@hprint/shared';
3
+ import { fabric } from '@hprint/core';
5
4
  import { throttle } from 'lodash-es';
6
5
 
7
6
  type IPlugin = Pick<
@@ -305,14 +304,17 @@ class UnitPlugin implements IPluginTempl {
305
304
  }
306
305
  }
307
306
 
308
- _bindEvents() {
309
- const throttledSync = throttle((obj: fabric.Object) => {
310
- syncMmFromObject(obj, undefined, this.precision);
311
- }, 30);
312
-
313
- this.canvas.on('object:modified', (e: any) => {
314
- const target = e.target as fabric.Object | undefined;
315
- if (target) syncMmFromObject(target, undefined, this.precision);
307
+ _bindEvents() {
308
+ const throttledSync = throttle((obj: fabric.Object) => {
309
+ // 多选坐标只需在 object:modified 时同步,避免拖动过程中反复创建计算对象。
310
+ if (obj.type !== 'activeSelection') {
311
+ (obj as any).syncOriginSizeByUnit?.();
312
+ }
313
+ }, 30);
314
+
315
+ this.canvas.on('object:modified', (e: any) => {
316
+ const target = e.target as fabric.Object | undefined;
317
+ if (target) this._syncTargetOriginSize(target);
316
318
  });
317
319
 
318
320
  this.canvas.on('object:moving', (e: any) => {
@@ -330,15 +332,46 @@ class UnitPlugin implements IPluginTempl {
330
332
  if (target) throttledSync(target);
331
333
  });
332
334
 
333
- this.editor.on?.('sizeChange', (event: { width: number, height: number }) => {
335
+ this.editor.on?.('sizeChange', (event: { width: number, height: number }) => {
334
336
  const unit = this.getUnit();
335
337
  if (unit === 'px') return;
336
338
  if (unit === 'mm') {
337
339
  this._syncOriginSize(event.width, event.height);
338
340
  return
339
341
  }
340
- });
341
- }
342
+ });
343
+ }
344
+
345
+ _syncTargetOriginSize(target: fabric.Object) {
346
+ if (target.type !== 'activeSelection') {
347
+ (target as any).syncOriginSizeByUnit?.();
348
+ return;
349
+ }
350
+
351
+ // 将 ActiveSelection 的平移和旋转真正应用到每个子元素。
352
+ // 之后重新创建选区,避免子元素继续保留相对于旧选区的坐标。
353
+ const objects = (target as fabric.ActiveSelection).getObjects();
354
+ const absoluteAngles = new Map<fabric.Object, number>();
355
+ objects.forEach((item: fabric.Object) => {
356
+ // item.angle 在 ActiveSelection 内是相对角度。必须先通过完整变换矩阵
357
+ // 取得画布上的绝对角度,再销毁选区并写回子元素。
358
+ const { angle } = fabric.util.qrDecompose(item.calcTransformMatrix());
359
+ absoluteAngles.set(item, ((angle % 360) + 360) % 360);
360
+ });
361
+ this.canvas.discardActiveObject();
362
+ objects.forEach((item: fabric.Object) => {
363
+ item.set('angle', absoluteAngles.get(item) ?? 0);
364
+ item.setCoords();
365
+ // 仅同步位置;旋转角度已由 ActiveSelection 变换写回 item.angle。
366
+ (item as any).syncOriginSizeByUnit?.(['left', 'top']);
367
+ });
368
+
369
+ const normalizedSelection = new fabric.ActiveSelection(objects, {
370
+ canvas: this.canvas,
371
+ });
372
+ this.canvas.setActiveObject(normalizedSelection);
373
+ this.canvas.requestRenderAll();
374
+ }
342
375
 
343
376
  destroy() {
344
377
  console.log('pluginDestroy');
@@ -49,7 +49,7 @@ export function applyMmToObject(
49
49
  };
50
50
  }
51
51
 
52
- export function syncMmFromObject(obj: fabric.Object, dpi?: number, precision?: number) {
52
+ export function syncMmFromObject(obj: fabric.Object, dpi?: number, precision?: number) {
53
53
  const isImage = obj.type === 'image';
54
54
  const toMm = (v: number | undefined) =>
55
55
  v === undefined ? undefined : applyPrecision(LengthConvert.pxToMm(v, dpi), precision);
@@ -76,10 +76,10 @@ export function syncMmFromObject(obj: fabric.Object, dpi?: number, precision?: n
76
76
  strokeWidth: toMm(strokeWidth),
77
77
  fontSize: toMm(fontSize),
78
78
  },
79
- };
80
- }
81
-
82
- export type UnitType = 'px' | 'mm' | 'inch';
79
+ };
80
+ }
81
+
82
+ export type UnitType = 'px' | 'mm' | 'inch';
83
83
 
84
84
  export function getUnit(editor: IEditor): UnitType {
85
85
  return (editor as any).getUnit?.() ?? 'px';