@hprint/plugins 0.0.1-alpha.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.
Files changed (153) hide show
  1. package/dist/index.css +1 -0
  2. package/dist/index.js +478 -0
  3. package/dist/index.mjs +41731 -0
  4. package/dist/src/index.d.ts +8 -0
  5. package/dist/src/index.d.ts.map +1 -0
  6. package/dist/src/objects/Arrow.d.ts +2 -0
  7. package/dist/src/objects/Arrow.d.ts.map +1 -0
  8. package/dist/src/objects/ThinTailArrow.d.ts +2 -0
  9. package/dist/src/objects/ThinTailArrow.d.ts.map +1 -0
  10. package/dist/src/plugins/AddBaseTypePlugin.d.ts +26 -0
  11. package/dist/src/plugins/AddBaseTypePlugin.d.ts.map +1 -0
  12. package/dist/src/plugins/AlignGuidLinePlugin.d.ts +16 -0
  13. package/dist/src/plugins/AlignGuidLinePlugin.d.ts.map +1 -0
  14. package/dist/src/plugins/BarCodePlugin.d.ts +68 -0
  15. package/dist/src/plugins/BarCodePlugin.d.ts.map +1 -0
  16. package/dist/src/plugins/CenterAlignPlugin.d.ts +29 -0
  17. package/dist/src/plugins/CenterAlignPlugin.d.ts.map +1 -0
  18. package/dist/src/plugins/ControlsPlugin.d.ts +11 -0
  19. package/dist/src/plugins/ControlsPlugin.d.ts.map +1 -0
  20. package/dist/src/plugins/ControlsRotatePlugin.d.ts +11 -0
  21. package/dist/src/plugins/ControlsRotatePlugin.d.ts.map +1 -0
  22. package/dist/src/plugins/CopyPlugin.d.ts +30 -0
  23. package/dist/src/plugins/CopyPlugin.d.ts.map +1 -0
  24. package/dist/src/plugins/CreateElementPlugin.d.ts +121 -0
  25. package/dist/src/plugins/CreateElementPlugin.d.ts.map +1 -0
  26. package/dist/src/plugins/DeleteHotKeyPlugin.d.ts +25 -0
  27. package/dist/src/plugins/DeleteHotKeyPlugin.d.ts.map +1 -0
  28. package/dist/src/plugins/DrawLinePlugin.d.ts +26 -0
  29. package/dist/src/plugins/DrawLinePlugin.d.ts.map +1 -0
  30. package/dist/src/plugins/DrawPolygonPlugin.d.ts +41 -0
  31. package/dist/src/plugins/DrawPolygonPlugin.d.ts.map +1 -0
  32. package/dist/src/plugins/DringPlugin.d.ts +33 -0
  33. package/dist/src/plugins/DringPlugin.d.ts.map +1 -0
  34. package/dist/src/plugins/FlipPlugin.d.ts +26 -0
  35. package/dist/src/plugins/FlipPlugin.d.ts.map +1 -0
  36. package/dist/src/plugins/FontPlugin.d.ts +33 -0
  37. package/dist/src/plugins/FontPlugin.d.ts.map +1 -0
  38. package/dist/src/plugins/FreeDrawPlugin.d.ts +23 -0
  39. package/dist/src/plugins/FreeDrawPlugin.d.ts.map +1 -0
  40. package/dist/src/plugins/GroupAlignPlugin.d.ts +24 -0
  41. package/dist/src/plugins/GroupAlignPlugin.d.ts.map +1 -0
  42. package/dist/src/plugins/GroupPlugin.d.ts +24 -0
  43. package/dist/src/plugins/GroupPlugin.d.ts.map +1 -0
  44. package/dist/src/plugins/GroupTextEditorPlugin.d.ts +18 -0
  45. package/dist/src/plugins/GroupTextEditorPlugin.d.ts.map +1 -0
  46. package/dist/src/plugins/HistoryPlugin.d.ts +30 -0
  47. package/dist/src/plugins/HistoryPlugin.d.ts.map +1 -0
  48. package/dist/src/plugins/ImageStroke.d.ts +18 -0
  49. package/dist/src/plugins/ImageStroke.d.ts.map +1 -0
  50. package/dist/src/plugins/LayerPlugin.d.ts +31 -0
  51. package/dist/src/plugins/LayerPlugin.d.ts.map +1 -0
  52. package/dist/src/plugins/LockPlugin.d.ts +27 -0
  53. package/dist/src/plugins/LockPlugin.d.ts.map +1 -0
  54. package/dist/src/plugins/MaskPlugin.d.ts +38 -0
  55. package/dist/src/plugins/MaskPlugin.d.ts.map +1 -0
  56. package/dist/src/plugins/MaterialPlugin.d.ts +45 -0
  57. package/dist/src/plugins/MaterialPlugin.d.ts.map +1 -0
  58. package/dist/src/plugins/MiddleMousePlugin.d.ts +18 -0
  59. package/dist/src/plugins/MiddleMousePlugin.d.ts.map +1 -0
  60. package/dist/src/plugins/MoveHotKeyPlugin.d.ts +12 -0
  61. package/dist/src/plugins/MoveHotKeyPlugin.d.ts.map +1 -0
  62. package/dist/src/plugins/PathTextPlugin.d.ts +30 -0
  63. package/dist/src/plugins/PathTextPlugin.d.ts.map +1 -0
  64. package/dist/src/plugins/PolygonModifyPlugin.d.ts +28 -0
  65. package/dist/src/plugins/PolygonModifyPlugin.d.ts.map +1 -0
  66. package/dist/src/plugins/PrintPlugin.d.ts +39 -0
  67. package/dist/src/plugins/PrintPlugin.d.ts.map +1 -0
  68. package/dist/src/plugins/PsdPlugin.d.ts +17 -0
  69. package/dist/src/plugins/PsdPlugin.d.ts.map +1 -0
  70. package/dist/src/plugins/QrCodePlugin.d.ts +137 -0
  71. package/dist/src/plugins/QrCodePlugin.d.ts.map +1 -0
  72. package/dist/src/plugins/ResizePlugin.d.ts +44 -0
  73. package/dist/src/plugins/ResizePlugin.d.ts.map +1 -0
  74. package/dist/src/plugins/RulerPlugin.d.ts +24 -0
  75. package/dist/src/plugins/RulerPlugin.d.ts.map +1 -0
  76. package/dist/src/plugins/SimpleClipImagePlugin.d.ts +18 -0
  77. package/dist/src/plugins/SimpleClipImagePlugin.d.ts.map +1 -0
  78. package/dist/src/plugins/UnitPlugin.d.ts +84 -0
  79. package/dist/src/plugins/UnitPlugin.d.ts.map +1 -0
  80. package/dist/src/plugins/WaterMarkPlugin.d.ts +40 -0
  81. package/dist/src/plugins/WaterMarkPlugin.d.ts.map +1 -0
  82. package/dist/src/plugins/WorkspacePlugin.d.ts +57 -0
  83. package/dist/src/plugins/WorkspacePlugin.d.ts.map +1 -0
  84. package/dist/src/types/eventType.d.ts +11 -0
  85. package/dist/src/types/eventType.d.ts.map +1 -0
  86. package/dist/src/utils/psd.d.ts +3 -0
  87. package/dist/src/utils/psd.d.ts.map +1 -0
  88. package/dist/src/utils/ruler/guideline.d.ts +4 -0
  89. package/dist/src/utils/ruler/guideline.d.ts.map +1 -0
  90. package/dist/src/utils/ruler/index.d.ts +5 -0
  91. package/dist/src/utils/ruler/index.d.ts.map +1 -0
  92. package/dist/src/utils/ruler/ruler.d.ts +147 -0
  93. package/dist/src/utils/ruler/ruler.d.ts.map +1 -0
  94. package/dist/src/utils/ruler/utils.d.ts +50 -0
  95. package/dist/src/utils/ruler/utils.d.ts.map +1 -0
  96. package/dist/src/utils/units.d.ts +22 -0
  97. package/dist/src/utils/units.d.ts.map +1 -0
  98. package/package.json +51 -0
  99. package/src/assets/edgecontrol.svg +17 -0
  100. package/src/assets/lock.svg +7 -0
  101. package/src/assets/middlecontrol.svg +17 -0
  102. package/src/assets/middlecontrolhoz.svg +17 -0
  103. package/src/assets/rotateicon.svg +20 -0
  104. package/src/assets/style/resizePlugin.css +27 -0
  105. package/src/index.ts +121 -0
  106. package/src/objects/Arrow.js +47 -0
  107. package/src/objects/ThinTailArrow.js +50 -0
  108. package/src/plugins/AddBaseTypePlugin.ts +107 -0
  109. package/src/plugins/AlignGuidLinePlugin.ts +1141 -0
  110. package/src/plugins/BarCodePlugin.ts +860 -0
  111. package/src/plugins/CenterAlignPlugin.ts +133 -0
  112. package/src/plugins/ControlsPlugin.ts +251 -0
  113. package/src/plugins/ControlsRotatePlugin.ts +111 -0
  114. package/src/plugins/CopyPlugin.ts +255 -0
  115. package/src/plugins/CreateElementPlugin.ts +548 -0
  116. package/src/plugins/DeleteHotKeyPlugin.ts +57 -0
  117. package/src/plugins/DrawLinePlugin.ts +162 -0
  118. package/src/plugins/DrawPolygonPlugin.ts +205 -0
  119. package/src/plugins/DringPlugin.ts +125 -0
  120. package/src/plugins/FlipPlugin.ts +59 -0
  121. package/src/plugins/FontPlugin.ts +165 -0
  122. package/src/plugins/FreeDrawPlugin.ts +49 -0
  123. package/src/plugins/GroupAlignPlugin.ts +365 -0
  124. package/src/plugins/GroupPlugin.ts +82 -0
  125. package/src/plugins/GroupTextEditorPlugin.ts +198 -0
  126. package/src/plugins/HistoryPlugin.ts +181 -0
  127. package/src/plugins/ImageStroke.ts +121 -0
  128. package/src/plugins/LayerPlugin.ts +108 -0
  129. package/src/plugins/LockPlugin.ts +240 -0
  130. package/src/plugins/MaskPlugin.ts +155 -0
  131. package/src/plugins/MaterialPlugin.ts +224 -0
  132. package/src/plugins/MiddleMousePlugin.ts +45 -0
  133. package/src/plugins/MoveHotKeyPlugin.ts +46 -0
  134. package/src/plugins/PathTextPlugin.ts +89 -0
  135. package/src/plugins/PolygonModifyPlugin.ts +224 -0
  136. package/src/plugins/PrintPlugin.ts +81 -0
  137. package/src/plugins/PsdPlugin.ts +52 -0
  138. package/src/plugins/QrCodePlugin.ts +393 -0
  139. package/src/plugins/ResizePlugin.ts +274 -0
  140. package/src/plugins/RulerPlugin.ts +78 -0
  141. package/src/plugins/SimpleClipImagePlugin.ts +244 -0
  142. package/src/plugins/UnitPlugin.ts +327 -0
  143. package/src/plugins/WaterMarkPlugin.ts +257 -0
  144. package/src/plugins/WorkspacePlugin.ts +307 -0
  145. package/src/types/eventType.ts +11 -0
  146. package/src/utils/psd.js +432 -0
  147. package/src/utils/ruler/guideline.ts +145 -0
  148. package/src/utils/ruler/index.ts +91 -0
  149. package/src/utils/ruler/ruler.ts +924 -0
  150. package/src/utils/ruler/utils.ts +162 -0
  151. package/src/utils/units.ts +133 -0
  152. package/tsconfig.json +10 -0
  153. package/vite.config.ts +29 -0
@@ -0,0 +1,78 @@
1
+ import { fabric } from '@hprint/core';
2
+ import type { IEditor, IPluginTempl } from '@hprint/core';
3
+
4
+ type IPlugin = Pick<
5
+ RulerPlugin,
6
+ 'hideGuideline' | 'showGuideline' | 'rulerEnable' | 'rulerDisable'
7
+ >;
8
+
9
+ declare module '@hprint/core' {
10
+ // eslint-disable-next-line @typescript-eslint/no-empty-interface
11
+ interface IEditor extends IPlugin {}
12
+ }
13
+
14
+ import initRuler from '../utils/ruler';
15
+
16
+ class RulerPlugin implements IPluginTempl {
17
+ static pluginName = 'RulerPlugin';
18
+ // static events = ['sizeChange'];
19
+ static apis = [
20
+ 'hideGuideline',
21
+ 'showGuideline',
22
+ 'rulerEnable',
23
+ 'rulerDisable',
24
+ ];
25
+ ruler: any;
26
+ constructor(
27
+ public canvas: fabric.Canvas,
28
+ public editor: IEditor
29
+ ) {
30
+ this.init();
31
+ }
32
+
33
+ hookSaveBefore() {
34
+ return new Promise((resolve) => {
35
+ this.hideGuideline();
36
+ resolve(true);
37
+ });
38
+ }
39
+
40
+ hookSaveAfter() {
41
+ return new Promise((resolve) => {
42
+ this.showGuideline();
43
+ resolve(true);
44
+ });
45
+ }
46
+
47
+ init() {
48
+ this.ruler = initRuler(this.canvas, this.editor, {
49
+ unit: (this.editor.getUnit?.() as 'px' | 'mm') || 'px',
50
+ });
51
+ // 监听全局单位变化,更新标尺展示
52
+ this.editor.on?.('unitChange', (unit: 'px' | 'mm') => {
53
+ this.ruler?.setUnit?.(unit);
54
+ });
55
+ }
56
+
57
+ hideGuideline() {
58
+ this.ruler.hideGuideline();
59
+ }
60
+
61
+ showGuideline() {
62
+ this.ruler.showGuideline();
63
+ }
64
+
65
+ rulerEnable() {
66
+ this.ruler.enable();
67
+ }
68
+
69
+ rulerDisable() {
70
+ this.ruler.disable();
71
+ }
72
+
73
+ destroy() {
74
+ console.log('pluginDestroy');
75
+ }
76
+ }
77
+
78
+ export default RulerPlugin;
@@ -0,0 +1,244 @@
1
+ import { fabric } from '@hprint/core';
2
+ import { mathHelper } from '@hprint/shared';
3
+ import { get, set } from 'lodash-es';
4
+ import type { IEditor, IPluginTempl } from '@hprint/core';
5
+
6
+ type IPlugin = Pick<SimpleClipImagePlugin, 'addClipPathToImage' | 'removeClip'>;
7
+
8
+ declare module '@hprint/core' {
9
+ // eslint-disable-next-line @typescript-eslint/no-empty-interface
10
+ interface IEditor extends IPlugin {}
11
+ }
12
+
13
+ const getBounds = (activeObject: fabric.Object) => {
14
+ const { left = 0, top = 0 } = activeObject;
15
+ return {
16
+ width: activeObject.getScaledWidth(),
17
+ height: activeObject.getScaledHeight(),
18
+ left,
19
+ top,
20
+ };
21
+ };
22
+ const bindInfo = (shell: fabric.Object, activeObject: fabric.Object) => {
23
+ bindFlagToObject(shell);
24
+ bindFlagToObject(shell, 'targetId', get(activeObject, 'id'));
25
+ bindFlagToObject(shell, 'targetType', get(activeObject, 'type'));
26
+ };
27
+ const bindFlagToObject = (
28
+ activeObject: fabric.Object,
29
+ key = 'clip',
30
+ value: any = true
31
+ ) => {
32
+ set(activeObject, key, value);
33
+ };
34
+ const createRectClip = (activeObject: fabric.Object, inverted: boolean) => {
35
+ const {
36
+ width = 0,
37
+ height = 0,
38
+ left = 0,
39
+ top = 0,
40
+ } = getBounds(activeObject);
41
+ const clipW = Math.round(width / 2);
42
+ const clipH = Math.round(height / 2);
43
+ const shell = new fabric.Rect({
44
+ width: clipW,
45
+ height: clipH,
46
+ fill: 'rgba(0,0,0,0)',
47
+ originX: 'center',
48
+ originY: 'center',
49
+ left: left + width / 2,
50
+ top: top + height / 2,
51
+ });
52
+ bindInfo(shell, activeObject);
53
+ const clipPath = new fabric.Rect({
54
+ absolutePositioned: true,
55
+ width: shell.width,
56
+ height: shell.height,
57
+ originX: 'center',
58
+ originY: 'center',
59
+ left: shell.left,
60
+ top: shell.top,
61
+ inverted: inverted,
62
+ });
63
+ return { clipPath, shell };
64
+ };
65
+ const createCircleClip = (activeObject: fabric.Object, inverted: boolean) => {
66
+ const point = activeObject.getCenterPoint();
67
+ const { width } = getBounds(activeObject);
68
+ const shell = new fabric.Ellipse({
69
+ fill: 'rgba(0,0,0,0)',
70
+ originX: 'center',
71
+ originY: 'center',
72
+ left: point.x,
73
+ top: point.y,
74
+ rx: width / 4,
75
+ ry: width / 4,
76
+ });
77
+ bindInfo(shell, activeObject);
78
+ const clipPath = new fabric.Ellipse({
79
+ absolutePositioned: true,
80
+ originX: 'center',
81
+ originY: 'center',
82
+ left: shell.left,
83
+ top: shell.top,
84
+ inverted: inverted,
85
+ rx: shell.rx,
86
+ ry: shell.ry,
87
+ });
88
+ return { shell, clipPath };
89
+ };
90
+ const createTriClip = (activeObject: fabric.Object, inverted: boolean) => {
91
+ const point = activeObject.getCenterPoint();
92
+ const { width = 0, height = 0 } = getBounds(activeObject);
93
+ const clipW = Math.round(width / 2);
94
+ const clipH = Math.round(height / 2);
95
+ const shell = new fabric.Triangle({
96
+ fill: 'rgba(0,0,0,0)',
97
+ originX: 'center',
98
+ originY: 'center',
99
+ left: point.x,
100
+ top: point.y,
101
+ width: clipW,
102
+ height: clipH,
103
+ });
104
+ bindInfo(shell, activeObject);
105
+ const clipPath = new fabric.Triangle({
106
+ absolutePositioned: true,
107
+ originX: 'center',
108
+ originY: 'center',
109
+ left: shell.left,
110
+ top: shell.top,
111
+ width: shell.width,
112
+ height: shell.height,
113
+ inverted: inverted,
114
+ });
115
+ return { shell, clipPath };
116
+ };
117
+ const createPolygonClip = (activeObject: fabric.Object, inverted: boolean) => {
118
+ const point = activeObject.getCenterPoint();
119
+ const points = mathHelper.getPolygonVertices(5, 200);
120
+ const shell = new fabric.Polygon(points, {
121
+ fill: 'rgba(0,0,0,0)',
122
+ originY: 'center',
123
+ originX: 'center',
124
+ left: point.x,
125
+ top: point.y,
126
+ });
127
+ bindInfo(shell, activeObject);
128
+ const clipPath = new fabric.Polygon([...points], {
129
+ absolutePositioned: true,
130
+ originX: 'center',
131
+ originY: 'center',
132
+ left: shell.left,
133
+ top: shell.top,
134
+ inverted: inverted,
135
+ });
136
+ return { shell, clipPath };
137
+ };
138
+ export default class SimpleClipImagePlugin implements IPluginTempl {
139
+ static pluginName = 'SimpleClipImagePlugin';
140
+ // static events = ['sizeChange'];
141
+ static apis = ['addClipPathToImage', 'removeClip'];
142
+ constructor(
143
+ public canvas: fabric.Canvas,
144
+ public editor: IEditor
145
+ ) {}
146
+ addClipPathToImage(value: string) {
147
+ const activeObject = this.canvas.getActiveObjects()[0];
148
+ if (activeObject && activeObject.type === 'image') {
149
+ let clip: { shell: fabric.Object; clipPath: fabric.Object } | null =
150
+ null;
151
+ const [name, inverted] = value.split('-');
152
+ const isInverted = !!inverted;
153
+ switch (name) {
154
+ case 'polygon':
155
+ clip = createPolygonClip(activeObject, isInverted);
156
+ break;
157
+ case 'rect':
158
+ clip = createRectClip(activeObject, isInverted);
159
+ break;
160
+ case 'circle':
161
+ clip = createCircleClip(activeObject, isInverted);
162
+ break;
163
+ case 'triangle':
164
+ clip = createTriClip(activeObject, isInverted);
165
+ break;
166
+ }
167
+ if (clip == null) return;
168
+ const { shell, clipPath } = clip;
169
+ shell.on('moving', () => {
170
+ clipPath.setPositionByOrigin(
171
+ shell.getCenterPoint(),
172
+ 'center',
173
+ 'center'
174
+ );
175
+ activeObject.set('dirty', true);
176
+ });
177
+ shell.on('rotating', () => {
178
+ clipPath.set({ angle: shell.angle });
179
+ activeObject.set('dirty', true);
180
+ });
181
+ shell.on('scaling', () => {
182
+ clipPath.set({ scaleX: shell.scaleX, scaleY: shell.scaleY });
183
+ clipPath.setPositionByOrigin(
184
+ shell.getCenterPoint(),
185
+ 'center',
186
+ 'center'
187
+ );
188
+ activeObject.set('dirty', true);
189
+ });
190
+ shell.on('deselected', () => {
191
+ if (
192
+ clipPath instanceof fabric.Ellipse &&
193
+ shell instanceof fabric.Ellipse
194
+ ) {
195
+ clipPath.set({ rx: shell.getRx(), ry: shell.getRy() });
196
+ this.correctPosition(activeObject, shell, clipPath);
197
+ } else if (shell instanceof fabric.Polygon) {
198
+ this.correctPosition(activeObject, shell, clipPath);
199
+ const { scaleX: cSx = 1, scaleY: cSy = 1 } = clipPath;
200
+ const { scaleX: sSx = 1, scaleY: sSy = 1 } = shell;
201
+ clipPath.set('scaleX', cSx * sSx);
202
+ clipPath.set('scaleY', cSy * sSy);
203
+ } else {
204
+ this.correctPosition(activeObject, shell, clipPath);
205
+ clipPath.set('width', shell.getScaledWidth());
206
+ clipPath.set('height', shell.getScaledHeight());
207
+ }
208
+ activeObject.set('dirty', true);
209
+ this.canvas.remove(shell);
210
+ this.canvas.requestRenderAll();
211
+ });
212
+ activeObject.set({ clipPath: clipPath });
213
+ this.canvas.add(shell);
214
+ this.canvas.setActiveObject(shell);
215
+ }
216
+ }
217
+ correctPosition(
218
+ activeObject: fabric.Object,
219
+ shell: fabric.Object,
220
+ clipPath: fabric.Object
221
+ ) {
222
+ const position = activeObject.toLocalPoint(
223
+ shell.getCenterPoint(),
224
+ 'center',
225
+ 'center'
226
+ );
227
+ const { scaleX = 1, scaleY = 1 } = activeObject;
228
+ clipPath.set({
229
+ absolutePositioned: false,
230
+ left: position.x / scaleX,
231
+ top: position.y / scaleY,
232
+ scaleX: 1 / scaleX,
233
+ scaleY: 1 / scaleY,
234
+ });
235
+ }
236
+ removeClip() {
237
+ const activeObject = this.canvas.getActiveObjects()[0];
238
+ if (activeObject && activeObject.type === 'image') {
239
+ activeObject.set({ clipPath: undefined });
240
+ activeObject.set('dirty', true);
241
+ this.canvas.requestRenderAll();
242
+ }
243
+ }
244
+ }
@@ -0,0 +1,327 @@
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';
5
+ import { throttle } from 'lodash-es';
6
+
7
+ type IPlugin = Pick<
8
+ UnitPlugin,
9
+ | 'getUnit'
10
+ | 'setUnit'
11
+ | 'getSizeByUnit'
12
+ | 'setSizeByUnit'
13
+ | 'getOriginSize'
14
+ | 'syncOriginSizeByUnit'
15
+ | 'applyObjectPx'
16
+ | 'applyObjectMm'
17
+ | 'applyObjectInch'
18
+ | 'applyObjectByUnit'
19
+ >;
20
+
21
+ type TUnit = 'px' | 'mm' | 'inch';
22
+
23
+ declare module '@hprint/core' {
24
+ // eslint-disable-next-line @typescript-eslint/no-empty-interface
25
+ interface IEditor extends IPlugin { }
26
+ }
27
+
28
+ class UnitPlugin implements IPluginTempl {
29
+ static pluginName = 'UnitPlugin';
30
+ // static events = ['sizeChange'];
31
+ static apis = [
32
+ 'getUnit',
33
+ 'setUnit',
34
+ 'getSizeByUnit',
35
+ 'setSizeByUnit',
36
+ 'getOriginSize',
37
+ 'getPrecision',
38
+ 'syncOriginSizeByUnit',
39
+ 'applyObjectPx',
40
+ 'applyObjectMm',
41
+ 'applyObjectInch',
42
+ 'applyObjectByUnit',
43
+ ];
44
+ unit: TUnit = 'px';
45
+ precision?: number;
46
+ _originSize: Record<string, { width: number, height: number }> = { mm: { width: 0, height: 0 } };
47
+ constructor(
48
+ public canvas: fabric.Canvas,
49
+ public editor: IEditor,
50
+ options?: {
51
+ unit?: TUnit;
52
+ precision?: number;
53
+ }
54
+ ) {
55
+ this.init(options);
56
+ this._bindEvents();
57
+ }
58
+
59
+ hookSaveBefore() {
60
+ return new Promise((resolve) => {
61
+ resolve(true);
62
+ });
63
+ }
64
+
65
+ hookSaveAfter() {
66
+ return new Promise((resolve) => {
67
+ resolve(true);
68
+ });
69
+ }
70
+
71
+ init(options?: { unit?: TUnit; precision?: number }) {
72
+ if (options?.unit) this.unit = options?.unit;
73
+ if (options?.precision) this.precision = options?.precision;
74
+ }
75
+
76
+ setUnit(unit: TUnit) {
77
+ if (this.unit === unit) return;
78
+ this.unit = unit;
79
+ this.editor.emit('unitChange', unit);
80
+ }
81
+
82
+ getUnit() {
83
+ return this.unit;
84
+ }
85
+
86
+ getPrecision() {
87
+ return this.precision;
88
+ }
89
+
90
+ getSizeByUnit(pxValue: number, unit?: TUnit, dpi?: number) {
91
+ const targetUnit = unit ?? this.editor.getUnit();
92
+ if (targetUnit === 'px') return pxValue;
93
+ if (targetUnit === 'mm') return LengthConvert.pxToMm(pxValue, dpi, { direct: true });
94
+ if (targetUnit === 'inch') {
95
+ const mm = LengthConvert.pxToMm(pxValue, dpi, { direct: true });
96
+ return mm / LengthConvert.CONSTANTS.INCH_TO_MM;
97
+ }
98
+ }
99
+
100
+ _toPrecisionValue(value: number) {
101
+ if (this.precision === undefined) return value;
102
+ const factor = Math.pow(10, this.precision);
103
+ return Math.round(value * factor) / factor;
104
+ }
105
+
106
+ _formatOriginUnitValues<T extends { [k: string]: number | undefined }>(vals: T): T {
107
+ const result: Record<string, number | undefined> = {};
108
+ for (const key in vals) {
109
+ const v = vals[key];
110
+ result[key] = v === undefined ? undefined : this._toPrecisionValue(v);
111
+ }
112
+ return result as T;
113
+ }
114
+
115
+ applyObjectPx(
116
+ obj: fabric.Object,
117
+ opts: {
118
+ left?: number;
119
+ top?: number;
120
+ width?: number;
121
+ height?: number;
122
+ strokeWidth?: number;
123
+ fontSize?: number;
124
+ }
125
+ ) {
126
+ const { left, top, width, height, strokeWidth, fontSize } = opts;
127
+ // fontSize必须在width之前,否则可能宽度设置不正确
128
+ if (fontSize !== undefined) obj.set('fontSize', fontSize);
129
+ if (strokeWidth !== undefined) obj.set('strokeWidth', strokeWidth);
130
+ if (width !== undefined) obj.set('width', width);
131
+ if (height !== undefined) obj.set('height', height);
132
+ if (left !== undefined) obj.set('left', left);
133
+ if (top !== undefined) obj.set('top', top);
134
+ }
135
+
136
+ applyObjectMm(
137
+ obj: fabric.Object,
138
+ mm: {
139
+ left?: number;
140
+ top?: number;
141
+ width?: number;
142
+ height?: number;
143
+ strokeWidth?: number;
144
+ fontSize?: number;
145
+ },
146
+ dpi?: number
147
+ ) {
148
+ const toPx = (v: number | undefined) =>
149
+ v === undefined ? undefined : LengthConvert.mmToPx(v, dpi, { direct: true });
150
+ this.applyObjectPx(obj, {
151
+ left: toPx(mm.left),
152
+ top: toPx(mm.top),
153
+ width: toPx(mm.width),
154
+ height: toPx(mm.height),
155
+ strokeWidth: toPx(mm.strokeWidth),
156
+ fontSize: toPx(mm.fontSize),
157
+ });
158
+ const formatted = this._formatOriginUnitValues(mm);
159
+ (obj as any)._originSize = { ...(obj as any)._originSize, mm: { ...formatted } };
160
+ }
161
+
162
+ applyObjectInch(
163
+ obj: fabric.Object,
164
+ inch: {
165
+ left?: number;
166
+ top?: number;
167
+ width?: number;
168
+ height?: number;
169
+ strokeWidth?: number;
170
+ fontSize?: number;
171
+ },
172
+ dpi?: number
173
+ ) {
174
+ const toMm = (v: number | undefined) =>
175
+ v === undefined ? undefined : v * LengthConvert.CONSTANTS.INCH_TO_MM;
176
+ this.applyObjectMm(
177
+ obj,
178
+ {
179
+ left: toMm(inch.left),
180
+ top: toMm(inch.top),
181
+ width: toMm(inch.width),
182
+ height: toMm(inch.height),
183
+ strokeWidth: toMm(inch.strokeWidth),
184
+ fontSize: toMm(inch.fontSize),
185
+ },
186
+ dpi
187
+ );
188
+ const formatted = this._formatOriginUnitValues(inch);
189
+ (obj as any)._originSize = { ...(obj as any)._originSize, inch: { ...formatted } };
190
+ }
191
+
192
+ applyObjectByUnit(
193
+ obj: fabric.Object,
194
+ opts: {
195
+ left?: number;
196
+ top?: number;
197
+ width?: number;
198
+ height?: number;
199
+ strokeWidth?: number;
200
+ fontSize?: number;
201
+ },
202
+ dpi?: number
203
+ ) {
204
+ const unit = this.getUnit();
205
+ if (unit === 'mm') return this.applyObjectMm(obj, opts, dpi);
206
+ if (unit === 'inch') return this.applyObjectInch(obj, opts, dpi);
207
+ return this.applyObjectPx(obj, opts);
208
+ }
209
+
210
+ // mm
211
+ setSizeMm(widthMm: number, heightMm: number, dpi?: number) {
212
+ const width = LengthConvert.mmToPx(widthMm, dpi, { direct: true });
213
+ const height = LengthConvert.mmToPx(heightMm, dpi, { direct: true });
214
+ this.editor.setSize(width, height);
215
+ this._syncOriginSize(widthMm, heightMm);
216
+ }
217
+
218
+ setSizeByUnit(width: number, height: number, options: { dpi: number, slient?: boolean }) {
219
+ const unit = (this.editor as any).getUnit?.() || 'px';
220
+ if (unit === 'mm') {
221
+ return this.setSizeMm(width, height, options.dpi);
222
+ }
223
+ if (unit === 'inch') {
224
+ this._syncOriginSize(width, height);
225
+ const wmm = width * LengthConvert.CONSTANTS.INCH_TO_MM;
226
+ const hmm = height * LengthConvert.CONSTANTS.INCH_TO_MM;
227
+ return this.setSizeMm(wmm, hmm, options.dpi);
228
+ }
229
+ this.editor.setSize(width, height, { slient: options.slient });
230
+ }
231
+
232
+ getOriginSize(dpi?: number) {
233
+ const unit = this.getUnit();
234
+ const origin = (this.canvas as any)._originSize || {};
235
+ const originMm: { width?: number; height?: number } = origin.mm || {};
236
+
237
+ if (unit === 'px') {
238
+ return {
239
+ width: this.canvas.getWidth(),
240
+ height: this.canvas.getHeight(),
241
+ };
242
+ }
243
+
244
+ const ensureMmWidth = originMm.width !== undefined
245
+ ? originMm.width
246
+ : LengthConvert.pxToMm(this.canvas.getWidth(), dpi, { direct: true });
247
+ const ensureMmHeight = originMm.height !== undefined
248
+ ? originMm.height
249
+ : LengthConvert.pxToMm(this.canvas.getHeight(), dpi, { direct: true });
250
+
251
+ if (unit === 'mm') {
252
+ return this._originSize.mm ?? {
253
+ width: ensureMmWidth,
254
+ height: ensureMmHeight,
255
+ };
256
+ }
257
+
258
+ // inch
259
+ return this._originSize.inch ?? {
260
+ width: ensureMmWidth / LengthConvert.CONSTANTS.INCH_TO_MM,
261
+ height: ensureMmHeight / LengthConvert.CONSTANTS.INCH_TO_MM,
262
+ };
263
+ }
264
+
265
+ _syncOriginSize(width?: number, height?: number) {
266
+ const unit = this.getUnit();
267
+ if (unit === 'px') return;
268
+ this._originSize[unit] = this._originSize[unit] || { width: 0, height: 0 };
269
+ if (width !== undefined) this._originSize[unit].width = this._toPrecisionValue(width);
270
+ if (height !== undefined) this._originSize[unit].height = this._toPrecisionValue(height);
271
+ }
272
+
273
+ syncOriginSizeByUnit(width?: number, height?: number) {
274
+ const unit = this.getUnit();
275
+ if (unit === 'mm') {
276
+ if (width !== undefined) {
277
+ const v = LengthConvert.pxToMm(width, undefined, { direct: true });
278
+ this._originSize[unit].width = this._toPrecisionValue(v);
279
+ }
280
+ if (height !== undefined) {
281
+ const v = LengthConvert.pxToMm(height, undefined, { direct: true });
282
+ this._originSize[unit].height = this._toPrecisionValue(v);
283
+ }
284
+ }
285
+ }
286
+
287
+ _bindEvents() {
288
+ const throttledSync = throttle((obj: fabric.Object) => {
289
+ syncMmFromObject(obj, undefined, this.precision);
290
+ }, 30);
291
+
292
+ this.canvas.on('object:modified', (e: any) => {
293
+ const target = e.target as fabric.Object | undefined;
294
+ if (target) syncMmFromObject(target, undefined, this.precision);
295
+ });
296
+
297
+ this.canvas.on('object:moving', (e: any) => {
298
+ const target = e.target as fabric.Object | undefined;
299
+ if (target) throttledSync(target);
300
+ });
301
+
302
+ this.canvas.on('object:scaling', (e: any) => {
303
+ const target = e.target as fabric.Object | undefined;
304
+ if (target) throttledSync(target);
305
+ });
306
+
307
+ this.canvas.on('object:rotating', (e: any) => {
308
+ const target = e.target as fabric.Object | undefined;
309
+ if (target) throttledSync(target);
310
+ });
311
+
312
+ this.editor.on?.('sizeChange', (event: { width: number, height: number }) => {
313
+ const unit = this.getUnit();
314
+ if (unit === 'px') return;
315
+ if (unit === 'mm') {
316
+ this._syncOriginSize(event.width, event.height);
317
+ return
318
+ }
319
+ });
320
+ }
321
+
322
+ destroy() {
323
+ console.log('pluginDestroy');
324
+ }
325
+ }
326
+
327
+ export default UnitPlugin;