@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,198 @@
1
+ import { fabric } from '@hprint/core';
2
+ import { utils } from '@hprint/shared';
3
+ import { v4 as uuid } from 'uuid';
4
+ import { pick } from 'lodash-es';
5
+ import type { IEditor, IPluginTempl } from '@hprint/core';
6
+
7
+ class GroupTextEditorPlugin implements IPluginTempl {
8
+ static pluginName = 'GroupTextEditorPlugin';
9
+ isDown = false;
10
+ constructor(
11
+ public canvas: fabric.Canvas,
12
+ public editor: IEditor
13
+ ) {
14
+ this._init();
15
+ }
16
+
17
+ // 组内文本输入
18
+ _init() {
19
+ this.canvas.on('mouse:down', (opt) => {
20
+ this.isDown = true;
21
+ // 重置选中controls
22
+ if (
23
+ opt.target &&
24
+ !opt.target.lockMovementX &&
25
+ !opt.target.lockMovementY &&
26
+ !opt.target.lockRotation &&
27
+ !opt.target.lockScalingX &&
28
+ !opt.target.lockScalingY
29
+ ) {
30
+ opt.target.hasControls = true;
31
+ }
32
+ });
33
+
34
+ this.canvas.on('mouse:up', () => {
35
+ this.isDown = false;
36
+ });
37
+
38
+ this.canvas.on('mouse:dblclick', (opt) => {
39
+ if (utils.isGroup(opt.target)) {
40
+ const selectedObject = this._getGroupObj(opt) as fabric.IText;
41
+ if (!selectedObject) return;
42
+ selectedObject.selectable = true;
43
+ // 由于组内的元素,双击以后会导致controls偏移,因此隐藏他
44
+ if (selectedObject.hasControls) {
45
+ selectedObject.hasControls = false;
46
+ }
47
+ if (this.isText(selectedObject)) {
48
+ this._bedingTextEditingEvent(selectedObject, opt.target);
49
+ return;
50
+ }
51
+ this.canvas.setActiveObject(selectedObject);
52
+ this.canvas.renderAll();
53
+ }
54
+ });
55
+ }
56
+
57
+ // 获取点击区域内的组内文字元素
58
+ _getGroupTextObj(opt: fabric.IEvent<MouseEvent>) {
59
+ const pointer = this.canvas.getPointer(opt.e, true);
60
+ if (!utils.isGroup(opt.target)) return false;
61
+ const clickObj = this.canvas._searchPossibleTargets(
62
+ opt.target._objects,
63
+ pointer
64
+ );
65
+ if (clickObj && this.isText(clickObj)) {
66
+ return clickObj;
67
+ }
68
+ return false;
69
+ }
70
+
71
+ _getGroupObj(opt: fabric.IEvent<MouseEvent>) {
72
+ const pointer = this.canvas.getPointer(opt.e, true);
73
+ if (!utils.isGroup(opt.target)) return false;
74
+ const clickObj = this.canvas._searchPossibleTargets(
75
+ opt.target._objects,
76
+ pointer
77
+ );
78
+ return clickObj;
79
+ }
80
+
81
+ // 通过组合重新组装来编辑文字,可能会耗性能。
82
+ _bedingTextEditingEvent(textObject: fabric.IText, groupObj: fabric.Group) {
83
+ const textObjectJSON = textObject.toObject();
84
+
85
+ const groupMatrix: number[] = groupObj.calcTransformMatrix();
86
+
87
+ const a: number = groupMatrix[0];
88
+ const b: number = groupMatrix[1];
89
+ const c: number = groupMatrix[2];
90
+ const d: number = groupMatrix[3];
91
+ const e: number = groupMatrix[4];
92
+ const f: number = groupMatrix[5];
93
+
94
+ const newX = a * (textObject.left ?? 0) + c * (textObject.top ?? 0) + e;
95
+ const newY = b * (textObject.left ?? 0) + d * (textObject.top ?? 0) + f;
96
+
97
+ const tempText = new (textObject.constructor as typeof fabric.IText)(
98
+ textObject.text ?? '',
99
+ {
100
+ ...textObjectJSON,
101
+ scaleX: textObjectJSON.scaleX * a,
102
+ scaleY: textObjectJSON.scaleY * a,
103
+ textAlign: textObject.textAlign,
104
+ left: newX,
105
+ top: newY,
106
+ styles: textObject.styles,
107
+ groupCopyed: textObject.group,
108
+ }
109
+ );
110
+ tempText.id = uuid();
111
+ textObject.visible = false;
112
+ groupObj.addWithUpdate();
113
+ tempText.visible = true;
114
+ tempText.selectable = true;
115
+ tempText.hasControls = false;
116
+ tempText.editable = true;
117
+ this.canvas.add(tempText);
118
+ this.canvas.setActiveObject(tempText);
119
+ tempText.enterEditing();
120
+ tempText.selectAll();
121
+
122
+ tempText.on('editing:exited', () => {
123
+ const attrs = tempText.toObject();
124
+
125
+ // 进入编辑模式时触发
126
+ textObject.set({
127
+ ...pick(attrs, [
128
+ 'fill',
129
+ 'fontSize',
130
+ 'fontStyle',
131
+ 'fontFamily',
132
+ 'lineHeight',
133
+ 'backgroundColor',
134
+ ]),
135
+ text: tempText.text,
136
+ visible: true,
137
+ });
138
+ groupObj.addWithUpdate();
139
+ tempText.visible = false;
140
+ this.canvas.remove(tempText);
141
+ this.canvas.setActiveObject(groupObj);
142
+ });
143
+ }
144
+
145
+ // 绑定编辑取消事件
146
+ _bedingEditingEvent(
147
+ textObject: fabric.IText,
148
+ opt: fabric.IEvent<MouseEvent>
149
+ ) {
150
+ if (!opt.target) return;
151
+ const left = opt.target.left;
152
+ const top = opt.target.top;
153
+ const ids = this._unGroup() || [];
154
+
155
+ const resetGroup = () => {
156
+ const groupArr = this.canvas
157
+ .getObjects()
158
+ .filter((item) => item.id && ids.includes(item.id));
159
+ // 删除元素
160
+ groupArr.forEach((item) => this.canvas.remove(item));
161
+
162
+ // 生成新组
163
+ const group = new fabric.Group([...groupArr]);
164
+ group.set('left', left);
165
+ group.set('top', top);
166
+ group.set('id', uuid());
167
+ textObject.off('editing:exited', resetGroup);
168
+ this.canvas.add(group);
169
+ this.canvas.discardActiveObject().renderAll();
170
+ };
171
+ // 绑定取消事件
172
+ textObject.on('editing:exited', resetGroup);
173
+ }
174
+
175
+ // 拆分组合并返回ID
176
+ _unGroup() {
177
+ const ids: string[] = [];
178
+ const activeObj = this.canvas.getActiveObject() as fabric.Group;
179
+ if (!activeObj) return;
180
+ activeObj.getObjects().forEach((item) => {
181
+ const id = uuid();
182
+ ids.push(id);
183
+ item.set('id', id);
184
+ });
185
+ activeObj.toActiveSelection();
186
+ return ids;
187
+ }
188
+
189
+ isText(obj: fabric.Object) {
190
+ return obj.type && ['i-text', 'text', 'textbox'].includes(obj.type);
191
+ }
192
+
193
+ destroy() {
194
+ console.log('pluginDestroy');
195
+ }
196
+ }
197
+
198
+ export default GroupTextEditorPlugin;
@@ -0,0 +1,181 @@
1
+ import Editor, { fabric } from '@hprint/core';
2
+ import type { IPluginTempl } from '@hprint/core';
3
+
4
+ type IEditor = Editor;
5
+ type callback = () => void;
6
+
7
+ class HistoryPlugin implements IPluginTempl {
8
+ static pluginName = 'HistoryPlugin';
9
+ static apis = [
10
+ 'undo',
11
+ 'redo',
12
+ 'historyUpdate',
13
+ 'clearAndSaveState',
14
+ 'saveState',
15
+ ];
16
+ static events = [];
17
+
18
+ // 历史记录相关属性
19
+ private stack: string[] = [];
20
+ private currentIndex = 0;
21
+ private maxLength = 100;
22
+ private isProcessing = false;
23
+ private isLoading = false;
24
+
25
+ hotkeys: string[] = ['ctrl+z', 'ctrl+shift+z', '⌘+z', '⌘+shift+z'];
26
+
27
+ constructor(
28
+ public canvas: fabric.Canvas,
29
+ public editor: IEditor
30
+ ) {
31
+ this._init();
32
+ }
33
+
34
+ private _init() {
35
+ // 监听对象变更事件
36
+ const events = {
37
+ 'object:removed': () => this.saveState(),
38
+ 'object:modified': () => this.saveState(),
39
+ 'object:skewing': () => this.saveState(),
40
+ };
41
+
42
+ // 绑定事件
43
+ Object.entries(events).forEach(([event, handler]) => {
44
+ this.canvas.on(event, handler);
45
+ });
46
+
47
+ // 初始化状态
48
+ this.saveState();
49
+
50
+ // 更新历史记录状态
51
+ this.canvas.on('history:append', () => {
52
+ this.historyUpdate();
53
+ });
54
+
55
+ // 页面离开提示
56
+ // window.addEventListener('beforeunload', (e) => {
57
+ // const { undoCount } = this.getState();
58
+ // if (undoCount > 0) {
59
+ // (e || window.event).returnValue = '确认离开';
60
+ // }
61
+ // });
62
+ }
63
+
64
+ // 获取当前状态
65
+ private getCurrentState() {
66
+ return this.editor.getJson();
67
+ }
68
+
69
+ // 保存状态
70
+ private saveState() {
71
+ if (this.isProcessing) return;
72
+
73
+ // 清除当前索引后的记录
74
+ this.stack.splice(this.currentIndex);
75
+ this.stack.push(this.getCurrentState());
76
+
77
+ // 维护最大长度
78
+ if (this.stack.length > this.maxLength) {
79
+ this.stack.shift();
80
+ } else {
81
+ this.currentIndex++;
82
+ }
83
+ this.historyUpdate();
84
+ }
85
+
86
+ // 加载状态
87
+ private _loadState(state: string, eventName: string, callback?: callback) {
88
+ this.isLoading = true;
89
+ this.isProcessing = true;
90
+
91
+ // 处理 workspace 的特殊情况
92
+ const parsedState = JSON.parse(state);
93
+ const workspace = parsedState.objects?.find(
94
+ (item: any) => item.id === 'workspace'
95
+ );
96
+ if (workspace) {
97
+ workspace.evented = false;
98
+ }
99
+
100
+ this.canvas.loadFromJSON(state, () => {
101
+ this.canvas.renderAll();
102
+ this.canvas.fire(eventName);
103
+ this.isProcessing = false;
104
+ this.isLoading = false;
105
+ callback?.();
106
+ });
107
+ }
108
+
109
+ // 获取历史记录状态
110
+ private getState() {
111
+ return {
112
+ undoCount: this.currentIndex - 1,
113
+ redoCount: this.stack.length - this.currentIndex,
114
+ };
115
+ }
116
+
117
+ // 清空历史记录
118
+ private clear() {
119
+ this.stack = [];
120
+ this.currentIndex = 0;
121
+ this.saveState();
122
+ }
123
+
124
+ // 公开方法
125
+ historyUpdate() {
126
+ const { undoCount, redoCount } = this.getState();
127
+ this.editor.emit('historyUpdate', undoCount, redoCount);
128
+ }
129
+
130
+ hookImportAfter() {
131
+ this.clear();
132
+ this.historyUpdate();
133
+ return Promise.resolve();
134
+ }
135
+
136
+ undo() {
137
+ if (this.isLoading || this.currentIndex <= 1) return;
138
+
139
+ this.currentIndex--;
140
+ const state = this.stack[this.currentIndex - 1];
141
+ if (state) {
142
+ this._loadState(JSON.stringify(state), 'history:undo');
143
+ this.historyUpdate();
144
+ }
145
+ }
146
+
147
+ redo() {
148
+ if (this.isLoading || this.currentIndex >= this.stack.length) return;
149
+
150
+ const state = this.stack[this.currentIndex];
151
+ if (state) {
152
+ this._loadState(JSON.stringify(state), 'history:redo');
153
+ this.currentIndex++;
154
+ this.historyUpdate();
155
+ }
156
+ }
157
+
158
+ hotkeyEvent(eventName: string, e: KeyboardEvent) {
159
+ if (e.type === 'keydown') {
160
+ switch (eventName) {
161
+ case 'ctrl+z':
162
+ case '⌘+z':
163
+ this.undo();
164
+ break;
165
+ case 'ctrl+shift+z':
166
+ case '⌘+shift+z':
167
+ this.redo();
168
+ break;
169
+ }
170
+ }
171
+ }
172
+
173
+ clearAndSaveState() {
174
+ const currentState = this.getCurrentState();
175
+ this.stack = [currentState]; // 只保留当前状态作为第一条记录
176
+ this.currentIndex = 1;
177
+ this.historyUpdate();
178
+ }
179
+ }
180
+
181
+ export default HistoryPlugin;
@@ -0,0 +1,121 @@
1
+ import { fabric } from '@hprint/core';
2
+ import type { IEditor, IPluginTempl } from '@hprint/core';
3
+
4
+ type IPlugin = Pick<ImageStrokePlugin, 'imageStrokeDraw'>;
5
+
6
+ declare module '@hprint/core' {
7
+ // eslint-disable-next-line @typescript-eslint/no-empty-interface
8
+ interface IEditor extends IPlugin {}
9
+ }
10
+
11
+ /*type IStrokeOps = {
12
+ enabled: boolean;
13
+ width: number;
14
+ color: string;
15
+ type: 'destination-out' | 'source-over' | 'source-in';
16
+ };*/
17
+ interface IExtendImage {
18
+ [x: string]: any;
19
+ originWidth?: number;
20
+ originHeight?: number;
21
+ originSrc?: string;
22
+ }
23
+ class ImageStrokePlugin implements IPluginTempl {
24
+ static pluginName = 'ImageStroke';
25
+ static apis = ['imageStrokeDraw'];
26
+ // public options: Required<IStrokeOps>;
27
+ constructor(
28
+ public canvas: fabric.Canvas,
29
+ public editor: IEditor
30
+ ) {
31
+ // this.options = Object.assign(
32
+ // {
33
+ // enabled: false,
34
+ // width: 5,
35
+ // color: '#000',
36
+ // type: 'source-over',
37
+ // },
38
+ // _options
39
+ // );
40
+ }
41
+
42
+ private addImage(src: string): Promise<HTMLImageElement | undefined> {
43
+ return new Promise((resolve, reject) => {
44
+ const img = new Image();
45
+ img.crossOrigin = 'anonymous';
46
+ img.onload = () => resolve(img);
47
+ img.onerror = () => reject();
48
+ img.src = src;
49
+ });
50
+ }
51
+
52
+ // imageStrokeEnable() {
53
+ // this.options.enabled = true;
54
+ // }
55
+
56
+ // imageStrokeDisable() {
57
+ // this.options.enabled = false;
58
+ // }
59
+
60
+ // imageStrokeSet(key: 'enabled' | 'width' | 'color' | 'type', val: any) {
61
+ // this.options[key] = val;
62
+ // }
63
+
64
+ async imageStrokeDraw(
65
+ stroke: string,
66
+ strokeWidth: number,
67
+ type = 'source-over'
68
+ ) {
69
+ const activeObject = this.canvas.getActiveObject() as
70
+ | (fabric.Image & IExtendImage)
71
+ | undefined;
72
+ if (!activeObject) return;
73
+ const w = activeObject.originWidth || 0,
74
+ h = activeObject.originHeight || 0,
75
+ src = activeObject?.originSrc || activeObject.getSrc();
76
+ let canvas: HTMLCanvasElement | null = document.createElement('canvas');
77
+ const ctx = canvas!.getContext('2d');
78
+ if (!ctx) return;
79
+ // 描边等于0 说明关闭了开关或者不需要描边 直接从原图绘制
80
+ if (strokeWidth === 0) {
81
+ activeObject.setSrc(src, () => {
82
+ activeObject.canvas?.renderAll();
83
+ });
84
+ return;
85
+ }
86
+ ctx.save();
87
+ ctx.clearRect(0, 0, canvas!.width, canvas!.height);
88
+ ctx.restore();
89
+ canvas!.width = w + strokeWidth * 2;
90
+ canvas!.height = h + strokeWidth * 2;
91
+ const dArr = [-1, -1, 0, -1, 1, -1, -1, 0, 1, 0, -1, 1, 0, 1, 1, 1];
92
+ const img = await this.addImage(src);
93
+ if (!img) return;
94
+ for (let i = 0; i < dArr.length; i += 2) {
95
+ ctx.drawImage(
96
+ img,
97
+ strokeWidth + dArr[i] * strokeWidth,
98
+ strokeWidth + dArr[i + 1] * strokeWidth,
99
+ w,
100
+ h
101
+ );
102
+ }
103
+ ctx.globalCompositeOperation = 'source-in';
104
+ ctx.fillStyle = stroke;
105
+ ctx.fillRect(0, 0, w + strokeWidth * 2, h + strokeWidth * 2);
106
+ ctx.globalCompositeOperation = type as any;
107
+ ctx.drawImage(img, strokeWidth, strokeWidth, w, h);
108
+ const res = canvas?.toDataURL();
109
+ canvas = null;
110
+ if (!res) return;
111
+ activeObject.setSrc(res, () => {
112
+ activeObject.canvas?.renderAll();
113
+ });
114
+ }
115
+
116
+ destroy() {
117
+ // this.editor.off('sizeChange', this.drawWaterMark);
118
+ }
119
+ }
120
+
121
+ export default ImageStrokePlugin;
@@ -0,0 +1,108 @@
1
+ import { fabric } from '@hprint/core';
2
+ import type { IEditor, IPluginTempl } from '@hprint/core';
3
+
4
+ type IPlugin = Pick<LayerPlugin, 'up' | 'down' | 'toFront' | 'toBack'>;
5
+
6
+ declare module '@hprint/core' {
7
+ // eslint-disable-next-line @typescript-eslint/no-empty-interface
8
+ interface IEditor extends IPlugin {}
9
+ }
10
+
11
+ class LayerPlugin implements IPluginTempl {
12
+ static pluginName = 'LayerPlugin';
13
+ static apis = ['up', 'down', 'toFront', 'toBack'];
14
+ constructor(
15
+ public canvas: fabric.Canvas,
16
+ public editor: IEditor
17
+ ) {}
18
+
19
+ _getWorkspace() {
20
+ return this.canvas.getObjects().find((item) => item.id === 'workspace');
21
+ }
22
+
23
+ _workspaceSendToBack() {
24
+ const workspace = this._getWorkspace();
25
+ workspace && workspace.sendToBack();
26
+ }
27
+
28
+ up() {
29
+ const actives = this.canvas.getActiveObjects();
30
+ if (actives && actives.length === 1) {
31
+ const activeObject = this.canvas.getActiveObjects()[0];
32
+ activeObject && activeObject.bringForward();
33
+ this.canvas.renderAll();
34
+ this._workspaceSendToBack();
35
+ }
36
+ }
37
+
38
+ down() {
39
+ const actives = this.canvas.getActiveObjects();
40
+ if (actives && actives.length === 1) {
41
+ const activeObject = this.canvas.getActiveObjects()[0];
42
+ activeObject && activeObject.sendBackwards();
43
+ this.canvas.renderAll();
44
+ this._workspaceSendToBack();
45
+ }
46
+ }
47
+
48
+ toFront() {
49
+ const actives = this.canvas.getActiveObjects();
50
+ if (actives && actives.length === 1) {
51
+ const activeObject = this.canvas.getActiveObjects()[0];
52
+ activeObject && activeObject.bringToFront();
53
+ this.canvas.renderAll();
54
+ this._workspaceSendToBack();
55
+ }
56
+ }
57
+
58
+ toBack() {
59
+ const actives = this.canvas.getActiveObjects();
60
+ if (actives && actives.length === 1) {
61
+ const activeObject = this.canvas.getActiveObjects()[0];
62
+ activeObject && activeObject.sendToBack();
63
+ this.canvas.renderAll();
64
+ this._workspaceSendToBack();
65
+ }
66
+ }
67
+
68
+ contextMenu() {
69
+ const activeObject = this.canvas.getActiveObject();
70
+ if (activeObject) {
71
+ return [
72
+ {
73
+ text: '图层管理',
74
+ hotkey: '❯',
75
+ subitems: [
76
+ {
77
+ text: '上一个',
78
+ hotkey: '',
79
+ onclick: () => this.up(),
80
+ },
81
+ {
82
+ text: '下一个',
83
+ hotkey: '',
84
+ onclick: () => this.down(),
85
+ },
86
+ {
87
+ text: '置顶',
88
+ hotkey: '',
89
+ onclick: () => this.toFront(),
90
+ },
91
+ {
92
+ text: '置底',
93
+ hotkey: '',
94
+ onclick: () => this.toBack(),
95
+ },
96
+ ],
97
+ },
98
+ ];
99
+ // return [{ text: '复制', hotkey: 'Ctrl+V', disabled: false, onclick: () => this.clone() }];
100
+ }
101
+ }
102
+
103
+ destroy() {
104
+ console.log('pluginDestroy');
105
+ }
106
+ }
107
+
108
+ export default LayerPlugin;