@hprint/plugins 0.0.2 → 0.0.3

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,1152 +1,1152 @@
1
- import { fabric, IEditor, IPluginTempl } from '@hprint/core';
2
-
3
- declare interface VerticalLine {
4
- x: number;
5
- y1: number;
6
- y2: number;
7
- }
8
-
9
- declare interface HorizontalLine {
10
- x1: number;
11
- x2: number;
12
- y: number;
13
- }
14
-
15
- class AlignGuidLinePlugin implements IPluginTempl {
16
- static pluginName = 'AlignGuidLinePlugin';
17
- static apis = ['changeSwitch'];
18
- defautOption = {
19
- color: 'rgba(255,95,95,1)',
20
- width: 1,
21
- };
22
- dragMode = false;
23
- switch!: boolean;
24
- constructor(
25
- public canvas: fabric.Canvas,
26
- public editor: IEditor,
27
- options?: {
28
- switch?: boolean;
29
- }
30
- ) {
31
- this.dragMode = false;
32
- this.init();
33
- this.switch = options?.switch ?? true;
34
- }
35
- init() {
36
- const { canvas } = this;
37
- const ctx = canvas.getSelectionContext();
38
- const aligningLineOffset = 5;
39
- const aligningLineMargin = 4;
40
- const This = this;
41
- let viewportTransform: number[] | undefined;
42
- let zoom = 1;
43
- let activeWidth = 0;
44
- let activeHeight = 0;
45
- let activeLeft = 0;
46
- let activeTop = 0;
47
-
48
- function drawVerticalLine(coords: VerticalLine) {
49
- drawLine(
50
- coords.x + 0.5,
51
- coords.y1 > coords.y2 ? coords.y2 : coords.y1,
52
- coords.x + 0.5,
53
- coords.y2 > coords.y1 ? coords.y2 : coords.y1
54
- );
55
- }
56
-
57
- function drawHorizontalLine(coords: HorizontalLine) {
58
- drawLine(
59
- coords.x1 > coords.x2 ? coords.x2 : coords.x1,
60
- coords.y + 0.5,
61
- coords.x2 > coords.x1 ? coords.x2 : coords.x1,
62
- coords.y + 0.5
63
- );
64
- }
65
-
66
- function drawLine(x1: number, y1: number, x2: number, y2: number) {
67
- if (viewportTransform == null) return;
68
-
69
- ctx.moveTo(
70
- x1 * zoom + viewportTransform[4],
71
- y1 * zoom + viewportTransform[5]
72
- );
73
- ctx.lineTo(
74
- x2 * zoom + viewportTransform[4],
75
- y2 * zoom + viewportTransform[5]
76
- );
77
- }
78
-
79
- function isInRange(value1: number, value2: number) {
80
- value1 = Math.round(value1);
81
- value2 = Math.round(value2);
82
- for (
83
- let i = value1 - aligningLineMargin,
84
- len = value1 + aligningLineMargin;
85
- i <= len;
86
- i++
87
- ) {
88
- if (i === value2) {
89
- return true;
90
- }
91
- }
92
- return false;
93
- }
94
-
95
- let verticalLines: VerticalLine[] = [];
96
- let horizontalLines: HorizontalLine[] = [];
97
-
98
- canvas.on('mouse:down', (e) => {
99
- viewportTransform = canvas.viewportTransform;
100
- zoom = canvas.getZoom();
101
- try {
102
- if (e) {
103
- activeLeft = e.target.left;
104
- activeTop = e.target.top;
105
- activeWidth = e.target.getScaledWidth();
106
- activeHeight = e.target.getScaledHeight();
107
- }
108
- } catch (e) { }
109
- });
110
-
111
- canvas.on('object:moving', (e) => {
112
- if (!this.switch) return;
113
- if (viewportTransform === undefined || e.target === undefined)
114
- return;
115
-
116
- const activeObject = e.target;
117
- const canvasObjects = canvas.getObjects();
118
- const activeObjectCenter = activeObject.getCenterPoint();
119
- const activeObjectLeft = activeObjectCenter.x;
120
- const activeObjectTop = activeObjectCenter.y;
121
- const activeObjectBoundingRect = activeObject.getBoundingRect();
122
- const activeObjectHeight = activeObject.getScaledHeight();
123
- const activeObjectWidth = activeObject.getScaledWidth();
124
- let horizontalInTheRange = false;
125
- let verticalInTheRange = false;
126
- const transform = canvas._currentTransform;
127
-
128
- //到达位置
129
- let reachLeft = false; //左
130
- let reachTop = false; //上
131
-
132
- //距离
133
- let _elReachLeft = 0; //左距离
134
- let _elReachTop = 0; //上距离
135
-
136
- activeObject.set('hasControls', false);
137
-
138
- if (!transform) return;
139
-
140
- for (let i = canvasObjects.length; i--;) {
141
- // eslint-disable-next-line no-continue
142
- if (canvasObjects[i] === activeObject) continue;
143
-
144
- // 排除辅助线
145
- if (
146
- activeObject instanceof fabric.GuideLine &&
147
- canvasObjects[i] instanceof fabric.GuideLine
148
- ) {
149
- continue;
150
- }
151
-
152
- const objectCenter = canvasObjects[i].getCenterPoint();
153
- const objectLeft = objectCenter.x;
154
- const objectTop = objectCenter.y;
155
- const objectBoundingRect = canvasObjects[i].getBoundingRect();
156
- const objectHeight =
157
- objectBoundingRect.height / viewportTransform[3];
158
- const objectWidth =
159
- objectBoundingRect.width / viewportTransform[0];
160
-
161
- // snap by the horizontal center line
162
- //水平中心线
163
- if (isInRange(objectLeft, activeObjectLeft)) {
164
- verticalInTheRange = true;
165
- verticalLines = [];
166
- verticalLines.push({
167
- x: objectLeft,
168
- y1:
169
- objectTop < activeObjectTop
170
- ? objectTop -
171
- objectHeight / 2 -
172
- aligningLineOffset
173
- : objectTop +
174
- objectHeight / 2 +
175
- aligningLineOffset,
176
- y2:
177
- activeObjectTop > objectTop
178
- ? activeObjectTop +
179
- activeObjectHeight / 2 +
180
- aligningLineOffset
181
- : activeObjectTop -
182
- activeObjectHeight / 2 -
183
- aligningLineOffset,
184
- });
185
- activeObject.setPositionByOrigin(
186
- new fabric.Point(objectLeft, activeObjectTop),
187
- 'center',
188
- 'center'
189
- );
190
- }
191
-
192
- // snap by the vertical center line
193
- //垂直中心线
194
- if (isInRange(objectTop, activeObjectTop)) {
195
- horizontalInTheRange = true;
196
- horizontalLines = [];
197
- horizontalLines.push({
198
- y: objectTop,
199
- x1:
200
- objectLeft < activeObjectLeft
201
- ? objectLeft -
202
- objectWidth / 2 -
203
- aligningLineOffset
204
- : objectLeft +
205
- objectWidth / 2 +
206
- aligningLineOffset,
207
- x2:
208
- activeObjectLeft > objectLeft
209
- ? activeObjectLeft +
210
- activeObjectWidth / 2 +
211
- aligningLineOffset
212
- : activeObjectLeft -
213
- activeObjectWidth / 2 -
214
- aligningLineOffset,
215
- });
216
- activeObject.setPositionByOrigin(
217
- new fabric.Point(activeObjectLeft, objectTop),
218
- 'center',
219
- 'center'
220
- );
221
- }
222
- // snap by the left edge
223
- //左边线
224
- if (
225
- isInRange(
226
- objectLeft - objectWidth / 2,
227
- activeObjectLeft - activeObjectWidth / 2
228
- )
229
- ) {
230
- verticalInTheRange = true;
231
- verticalLines = [];
232
- reachLeft = true;
233
- verticalLines.push({
234
- x: objectLeft - objectWidth / 2,
235
- y1:
236
- objectTop < activeObjectTop
237
- ? objectTop -
238
- objectHeight / 2 -
239
- aligningLineOffset
240
- : objectTop +
241
- objectHeight / 2 +
242
- aligningLineOffset,
243
- y2:
244
- activeObjectTop > objectTop
245
- ? activeObjectTop +
246
- activeObjectHeight / 2 +
247
- aligningLineOffset
248
- : activeObjectTop -
249
- activeObjectHeight / 2 -
250
- aligningLineOffset,
251
- });
252
- _elReachLeft =
253
- objectLeft - objectWidth / 2 + activeObjectWidth / 2;
254
- let x =
255
- objectLeft - objectWidth / 2 + activeObjectWidth / 2;
256
- let y = reachTop ? _elReachTop : activeObjectTop;
257
-
258
- activeObject.setPositionByOrigin(
259
- new fabric.Point(x, y),
260
- 'center',
261
- 'center'
262
- );
263
- }
264
-
265
- // snap by the right edge
266
- //右边线
267
- if (
268
- isInRange(
269
- objectLeft + objectWidth / 2,
270
- activeObjectLeft + activeObjectWidth / 2
271
- )
272
- ) {
273
- reachLeft = true;
274
- verticalInTheRange = true;
275
- verticalLines = [];
276
- verticalLines.push({
277
- x: objectLeft + objectWidth / 2,
278
- y1:
279
- objectTop < activeObjectTop
280
- ? objectTop -
281
- objectHeight / 2 -
282
- aligningLineOffset
283
- : objectTop +
284
- objectHeight / 2 +
285
- aligningLineOffset,
286
- y2:
287
- activeObjectTop > objectTop
288
- ? activeObjectTop +
289
- activeObjectHeight / 2 +
290
- aligningLineOffset
291
- : activeObjectTop -
292
- activeObjectHeight / 2 -
293
- aligningLineOffset,
294
- });
295
- _elReachLeft =
296
- objectLeft + objectWidth / 2 - activeObjectWidth / 2;
297
- let x =
298
- objectLeft + objectWidth / 2 - activeObjectWidth / 2;
299
- let y = reachTop ? _elReachTop : activeObjectTop;
300
- activeObject.setPositionByOrigin(
301
- new fabric.Point(x, y),
302
- 'center',
303
- 'center'
304
- );
305
- }
306
-
307
- // snap by the top edge
308
- //上边线
309
- if (
310
- isInRange(
311
- objectTop - objectHeight / 2,
312
- activeObjectTop - activeObjectHeight / 2
313
- )
314
- ) {
315
- reachTop = true;
316
- horizontalInTheRange = true;
317
- horizontalLines = [];
318
- horizontalLines.push({
319
- y: objectTop - objectHeight / 2,
320
- x1:
321
- objectLeft < activeObjectLeft
322
- ? objectLeft -
323
- objectWidth / 2 -
324
- aligningLineOffset
325
- : objectLeft +
326
- objectWidth / 2 +
327
- aligningLineOffset,
328
- x2:
329
- activeObjectLeft > objectLeft
330
- ? activeObjectLeft +
331
- activeObjectWidth / 2 +
332
- aligningLineOffset
333
- : activeObjectLeft -
334
- activeObjectWidth / 2 -
335
- aligningLineOffset,
336
- });
337
- _elReachTop =
338
- objectTop - objectHeight / 2 + activeObjectHeight / 2;
339
- let x = reachLeft ? _elReachLeft : activeObjectLeft;
340
- let y =
341
- objectTop - objectHeight / 2 + activeObjectHeight / 2;
342
- activeObject.setPositionByOrigin(
343
- new fabric.Point(x, y),
344
- 'center',
345
- 'center'
346
- );
347
- }
348
-
349
- // snap by the bottom edge
350
- //底边线
351
- if (
352
- isInRange(
353
- objectTop + objectHeight / 2,
354
- activeObjectTop + activeObjectHeight / 2
355
- )
356
- ) {
357
- reachTop = true;
358
-
359
- horizontalInTheRange = true;
360
- horizontalLines = [];
361
- horizontalLines.push({
362
- y: objectTop + objectHeight / 2,
363
- x1:
364
- objectLeft < activeObjectLeft
365
- ? objectLeft -
366
- objectWidth / 2 -
367
- aligningLineOffset
368
- : objectLeft +
369
- objectWidth / 2 +
370
- aligningLineOffset,
371
- x2:
372
- activeObjectLeft > objectLeft
373
- ? activeObjectLeft +
374
- activeObjectWidth / 2 +
375
- aligningLineOffset
376
- : activeObjectLeft -
377
- activeObjectWidth / 2 -
378
- aligningLineOffset,
379
- });
380
- _elReachTop =
381
- objectTop + objectHeight / 2 - activeObjectHeight / 2;
382
- let x = reachLeft ? _elReachLeft : activeObjectLeft;
383
- let y =
384
- objectTop + objectHeight / 2 - activeObjectHeight / 2;
385
- activeObject.setPositionByOrigin(
386
- new fabric.Point(x, y),
387
- 'center',
388
- 'center'
389
- );
390
- }
391
-
392
- //左边线和右边线
393
- if (
394
- isInRange(
395
- objectLeft - objectWidth / 2,
396
- activeObjectLeft + activeObjectWidth / 2
397
- )
398
- ) {
399
- reachLeft = true;
400
- verticalInTheRange = true;
401
- verticalInTheRange = [];
402
- verticalLines.push({
403
- x: objectLeft - objectWidth / 2,
404
- y1:
405
- objectTop < activeObjectTop
406
- ? objectTop -
407
- objectHeight / 2 -
408
- aligningLineOffset
409
- : objectTop +
410
- objectHeight / 2 +
411
- aligningLineOffset,
412
- y2:
413
- activeObjectTop > objectTop
414
- ? activeObjectTop +
415
- activeObjectHeight / 2 +
416
- aligningLineOffset
417
- : activeObjectTop -
418
- activeObjectHeight / 2 -
419
- aligningLineOffset,
420
- });
421
-
422
- _elReachLeft =
423
- objectLeft - objectWidth / 2 - activeObjectWidth / 2;
424
- let x =
425
- objectLeft - objectWidth / 2 - activeObjectWidth / 2;
426
- let y = activeObjectTop;
427
-
428
- activeObject.setPositionByOrigin(
429
- new fabric.Point(x, y),
430
- 'center',
431
- 'center'
432
- );
433
- }
434
- //右边线和左边线
435
- if (
436
- isInRange(
437
- objectLeft + objectWidth / 2,
438
- activeObjectLeft - activeObjectWidth / 2
439
- )
440
- ) {
441
- reachLeft = true;
442
- verticalInTheRange = true;
443
- verticalInTheRange = [];
444
- verticalLines.push({
445
- x: objectLeft + objectWidth / 2,
446
- y1:
447
- objectTop < activeObjectTop
448
- ? objectTop -
449
- objectHeight / 2 -
450
- aligningLineOffset
451
- : objectTop +
452
- objectHeight / 2 +
453
- aligningLineOffset,
454
- y2:
455
- activeObjectTop > objectTop
456
- ? activeObjectTop +
457
- activeObjectHeight / 2 +
458
- aligningLineOffset
459
- : activeObjectTop -
460
- activeObjectHeight / 2 -
461
- aligningLineOffset,
462
- });
463
- _elReachLeft =
464
- objectLeft + objectWidth / 2 + activeObjectWidth / 2;
465
-
466
- let x =
467
- objectLeft + objectWidth / 2 + activeObjectWidth / 2;
468
- let y = activeObjectTop;
469
-
470
- activeObject.setPositionByOrigin(
471
- new fabric.Point(x, y),
472
- 'center',
473
- 'center'
474
- );
475
- }
476
- //上边线和下边线
477
- if (
478
- isInRange(
479
- objectTop - objectHeight / 2,
480
- activeObjectTop + activeObjectHeight / 2
481
- )
482
- ) {
483
- reachTop = true;
484
- horizontalInTheRange = true;
485
- horizontalLines = [];
486
- horizontalLines.push({
487
- y: objectTop - objectHeight / 2,
488
- x1:
489
- objectLeft < activeObjectLeft
490
- ? objectLeft -
491
- objectWidth / 2 -
492
- aligningLineOffset
493
- : objectLeft +
494
- objectWidth / 2 +
495
- aligningLineOffset,
496
- x2:
497
- activeObjectLeft > objectLeft
498
- ? activeObjectLeft +
499
- activeObjectWidth / 2 +
500
- aligningLineOffset
501
- : activeObjectLeft -
502
- activeObjectWidth / 2 -
503
- aligningLineOffset,
504
- });
505
- _elReachTop =
506
- objectTop - objectHeight / 2 - activeObjectHeight / 2;
507
-
508
- let x = reachLeft ? _elReachLeft : activeObjectLeft;
509
- let y =
510
- objectTop - objectHeight / 2 - activeObjectHeight / 2;
511
- activeObject.setPositionByOrigin(
512
- new fabric.Point(x, y),
513
- 'center',
514
- 'center'
515
- );
516
- }
517
- //下边线和上变线
518
- if (
519
- isInRange(
520
- objectTop + objectHeight / 2,
521
- activeObjectTop - activeObjectHeight / 2
522
- )
523
- ) {
524
- reachTop = true;
525
- horizontalInTheRange = true;
526
- horizontalLines = [];
527
- horizontalLines.push({
528
- y: objectTop + objectHeight / 2,
529
- x1:
530
- objectLeft < activeObjectLeft
531
- ? objectLeft -
532
- objectWidth / 2 -
533
- aligningLineOffset
534
- : objectLeft +
535
- objectWidth / 2 +
536
- aligningLineOffset,
537
- x2:
538
- activeObjectLeft > objectLeft
539
- ? activeObjectLeft +
540
- activeObjectWidth / 2 +
541
- aligningLineOffset
542
- : activeObjectLeft -
543
- activeObjectWidth / 2 -
544
- aligningLineOffset,
545
- });
546
- _elReachTop =
547
- objectTop + objectHeight / 2 + activeObjectHeight / 2;
548
- let x = reachLeft ? _elReachLeft : activeObjectLeft;
549
- let y =
550
- objectTop + objectHeight / 2 + activeObjectHeight / 2;
551
- activeObject.setPositionByOrigin(
552
- new fabric.Point(x, y),
553
- 'center',
554
- 'center'
555
- );
556
- }
557
- }
558
-
559
- if (!horizontalInTheRange) {
560
- horizontalLines.length = 0;
561
- }
562
-
563
- if (!verticalInTheRange) {
564
- verticalLines.length = 0;
565
- }
566
- });
567
-
568
- canvas.on('before:render', () => {
569
- // fix 保存图片时报错
570
- if (canvas.contextTop === null) {
571
- return;
572
- }
573
- try {
574
- canvas.clearContext(canvas.contextTop);
575
- } catch (error) {
576
- console.log(error);
577
- }
578
- });
579
-
580
- canvas.on('object:scaling', (e) => {
581
- if (viewportTransform === undefined || e.target === undefined)
582
- return;
583
-
584
- const activeObject = e.target;
585
- const canvasObjects = canvas.getObjects();
586
- const activeObjectCenter = activeObject.getCenterPoint();
587
- const activeObjectLeft = activeObjectCenter.x;
588
- const activeObjectTop = activeObjectCenter.y;
589
- const activeObjectBoundingRect = activeObject.getBoundingRect();
590
- const activeObjectHeight = activeObject.getScaledHeight();
591
- const activeObjectWidth = activeObject.getScaledWidth();
592
- let horizontalInTheRange = false;
593
- let verticalInTheRange = false;
594
- const transform = canvas._currentTransform;
595
-
596
- activeObject.set('hasControls', false);
597
- if (!transform) return;
598
-
599
- for (let i = canvasObjects.length; i--;) {
600
- // eslint-disable-next-line no-continue
601
- if (canvasObjects[i] === activeObject) continue;
602
-
603
- // 排除辅助线
604
- if (
605
- activeObject instanceof fabric.GuideLine &&
606
- canvasObjects[i] instanceof fabric.GuideLine
607
- ) {
608
- continue;
609
- }
610
-
611
- const objectCenter = canvasObjects[i].getCenterPoint();
612
- const objectLeft = objectCenter.x;
613
- const objectTop = objectCenter.y;
614
- const objectBoundingRect = canvasObjects[i].getBoundingRect();
615
- const objectHeight =
616
- objectBoundingRect.height / viewportTransform[3];
617
- const objectWidth =
618
- objectBoundingRect.width / viewportTransform[0];
619
-
620
- // snap by the horizontal center line
621
- //水平中心线
622
- if (isInRange(objectLeft, activeObjectLeft)) {
623
- verticalInTheRange = true;
624
- verticalLines = [];
625
- verticalLines.push({
626
- x: objectLeft,
627
- y1:
628
- objectTop < activeObjectTop
629
- ? objectTop -
630
- objectHeight / 2 -
631
- aligningLineOffset
632
- : objectTop +
633
- objectHeight / 2 +
634
- aligningLineOffset,
635
- y2:
636
- activeObjectTop > objectTop
637
- ? activeObjectTop +
638
- activeObjectHeight / 2 +
639
- aligningLineOffset
640
- : activeObjectTop -
641
- activeObjectHeight / 2 -
642
- aligningLineOffset,
643
- });
644
- }
645
-
646
- // snap by the left edge
647
- //左边线
648
- if (
649
- isInRange(
650
- objectLeft - objectWidth / 2,
651
- activeObjectLeft - activeObjectWidth / 2
652
- )
653
- ) {
654
- verticalInTheRange = true;
655
- verticalLines = [];
656
- verticalLines.push({
657
- x: objectLeft - objectWidth / 2,
658
- y1:
659
- objectTop < activeObjectTop
660
- ? objectTop -
661
- objectHeight / 2 -
662
- aligningLineOffset
663
- : objectTop +
664
- objectHeight / 2 +
665
- aligningLineOffset,
666
- y2:
667
- activeObjectTop > objectTop
668
- ? activeObjectTop +
669
- activeObjectHeight / 2 +
670
- aligningLineOffset
671
- : activeObjectTop -
672
- activeObjectHeight / 2 -
673
- aligningLineOffset,
674
- });
675
-
676
- let leftRight = new Map([
677
- ['bl', 1],
678
- ['ml', 1],
679
- ['tl', 1],
680
- ]);
681
- if (leftRight.get(e.transform.corner)) {
682
- activeObject.setPositionByOrigin(
683
- new fabric.Point(
684
- objectLeft -
685
- objectWidth / 2 +
686
- activeObjectWidth / 2,
687
- activeObjectTop
688
- ),
689
- 'center',
690
- 'center'
691
- );
692
-
693
- activeObject.set(
694
- 'scaleX',
695
- ((activeLeft -
696
- (objectLeft - objectWidth / 2) +
697
- activeWidth) *
698
- activeObject.scaleX) /
699
- activeObject.getScaledWidth()
700
- );
701
- break;
702
- }
703
- }
704
-
705
- // snap by the right edge
706
- //右边线
707
- if (
708
- isInRange(
709
- objectLeft + objectWidth / 2,
710
- activeObjectLeft + activeObjectWidth / 2
711
- )
712
- ) {
713
- verticalInTheRange = true;
714
- verticalLines = [];
715
- verticalLines.push({
716
- x: objectLeft + objectWidth / 2,
717
- y1:
718
- objectTop < activeObjectTop
719
- ? objectTop -
720
- objectHeight / 2 -
721
- aligningLineOffset
722
- : objectTop +
723
- objectHeight / 2 +
724
- aligningLineOffset,
725
- y2:
726
- activeObjectTop > objectTop
727
- ? activeObjectTop +
728
- activeObjectHeight / 2 +
729
- aligningLineOffset
730
- : activeObjectTop -
731
- activeObjectHeight / 2 -
732
- aligningLineOffset,
733
- });
734
-
735
- let Right = new Map([
736
- ['mr', 1],
737
- ['tr', 1],
738
- ['br', 1],
739
- ]);
740
-
741
- if (Right.get(e.transform.corner)) {
742
- activeObject.set(
743
- 'scaleX',
744
- ((objectLeft +
745
- objectWidth / 2 -
746
- (activeLeft + activeWidth) +
747
- activeWidth) *
748
- activeObject.scaleX) /
749
- activeObject.getScaledWidth()
750
- );
751
- break;
752
- }
753
- }
754
-
755
- // snap by the vertical center line
756
- //垂直中心线
757
- if (isInRange(objectTop, activeObjectTop)) {
758
- horizontalInTheRange = true;
759
- horizontalLines = [];
760
- horizontalLines.push({
761
- y: objectTop,
762
- x1:
763
- objectLeft < activeObjectLeft
764
- ? objectLeft -
765
- objectWidth / 2 -
766
- aligningLineOffset
767
- : objectLeft +
768
- objectWidth / 2 +
769
- aligningLineOffset,
770
- x2:
771
- activeObjectLeft > objectLeft
772
- ? activeObjectLeft +
773
- activeObjectWidth / 2 +
774
- aligningLineOffset
775
- : activeObjectLeft -
776
- activeObjectWidth / 2 -
777
- aligningLineOffset,
778
- });
779
- }
780
-
781
- // snap by the top edge
782
- if (
783
- isInRange(
784
- objectTop - objectHeight / 2,
785
- activeObjectTop - activeObjectHeight / 2
786
- )
787
- ) {
788
- horizontalInTheRange = true;
789
- horizontalLines = [];
790
- horizontalLines.push({
791
- y: objectTop - objectHeight / 2,
792
- x1:
793
- objectLeft < activeObjectLeft
794
- ? objectLeft -
795
- objectWidth / 2 -
796
- aligningLineOffset
797
- : objectLeft +
798
- objectWidth / 2 +
799
- aligningLineOffset,
800
- x2:
801
- activeObjectLeft > objectLeft
802
- ? activeObjectLeft +
803
- activeObjectWidth / 2 +
804
- aligningLineOffset
805
- : activeObjectLeft -
806
- activeObjectWidth / 2 -
807
- aligningLineOffset,
808
- });
809
-
810
- let bottomRight = new Map([
811
- ['tr', 1],
812
- ['tl', 1],
813
- ['mt', 1],
814
- ]);
815
-
816
- if (bottomRight.get(e.transform.corner)) {
817
- activeObject.setPositionByOrigin(
818
- new fabric.Point(
819
- activeObjectLeft,
820
- objectTop -
821
- objectHeight / 2 +
822
- activeObjectHeight / 2
823
- ),
824
- 'center',
825
- 'center'
826
- );
827
-
828
- activeObject.set(
829
- 'scaleY',
830
- ((activeTop +
831
- activeHeight -
832
- (objectTop - objectHeight / 2)) *
833
- activeObject.scaleY) /
834
- activeObject.getScaledHeight()
835
- );
836
- break;
837
- }
838
- }
839
-
840
- // snap by the bottom edge
841
- if (
842
- isInRange(
843
- objectTop + objectHeight / 2,
844
- activeObjectTop + activeObjectHeight / 2
845
- )
846
- ) {
847
- horizontalInTheRange = true;
848
- horizontalLines = [];
849
- horizontalLines.push({
850
- y: objectTop + objectHeight / 2,
851
- x1:
852
- objectLeft < activeObjectLeft
853
- ? objectLeft -
854
- objectWidth / 2 -
855
- aligningLineOffset
856
- : objectLeft +
857
- objectWidth / 2 +
858
- aligningLineOffset,
859
- x2:
860
- activeObjectLeft > objectLeft
861
- ? activeObjectLeft +
862
- activeObjectWidth / 2 +
863
- aligningLineOffset
864
- : activeObjectLeft -
865
- activeObjectWidth / 2 -
866
- aligningLineOffset,
867
- });
868
-
869
- let bottom = new Map([
870
- ['mb', 1],
871
- ['bl', 1],
872
- ['br', 1],
873
- ]);
874
- if (bottom.get(e.transform.corner)) {
875
- activeObject.set(
876
- 'scaleY',
877
- ((objectTop +
878
- objectHeight / 2 -
879
- (activeTop + activeHeight) +
880
- activeHeight) *
881
- activeObject.scaleY) /
882
- activeObject.getScaledHeight()
883
- );
884
- break;
885
- }
886
- }
887
-
888
- //左边线和右边线
889
- if (
890
- isInRange(
891
- objectLeft - objectWidth / 2,
892
- activeObjectLeft + activeObjectWidth / 2
893
- )
894
- ) {
895
- verticalInTheRange = true;
896
- verticalLines = [];
897
- verticalLines.push({
898
- x: objectLeft - objectWidth / 2,
899
- y1:
900
- objectTop < activeObjectTop
901
- ? objectTop -
902
- objectHeight / 2 -
903
- aligningLineOffset
904
- : objectTop +
905
- objectHeight / 2 +
906
- aligningLineOffset,
907
- y2:
908
- activeObjectTop > objectTop
909
- ? activeObjectTop +
910
- activeObjectHeight / 2 +
911
- aligningLineOffset
912
- : activeObjectTop -
913
- activeObjectHeight / 2 -
914
- aligningLineOffset,
915
- });
916
-
917
- let right = new Map([
918
- ['mr', 1],
919
- ['tr', 1],
920
- ['br', 1],
921
- ]);
922
- if (right.get(e.transform.corner)) {
923
- activeObject.set(
924
- 'scaleX',
925
- ((objectLeft -
926
- objectWidth / 2 -
927
- activeObject.left) *
928
- activeObject.scaleX) /
929
- activeObject.getScaledWidth()
930
- );
931
- break;
932
- }
933
- }
934
- //右边线和左边线
935
- if (
936
- isInRange(
937
- objectLeft + objectWidth / 2,
938
- activeObjectLeft - activeObjectWidth / 2
939
- )
940
- ) {
941
- verticalInTheRange = true;
942
- verticalLines = [];
943
- verticalLines.push({
944
- x: objectLeft + objectWidth / 2,
945
- y1:
946
- objectTop < activeObjectTop
947
- ? objectTop -
948
- objectHeight / 2 -
949
- aligningLineOffset
950
- : objectTop +
951
- objectHeight / 2 +
952
- aligningLineOffset,
953
- y2:
954
- activeObjectTop > objectTop
955
- ? activeObjectTop +
956
- activeObjectHeight / 2 +
957
- aligningLineOffset
958
- : activeObjectTop -
959
- activeObjectHeight / 2 -
960
- aligningLineOffset,
961
- });
962
-
963
- let leftRight = new Map([
964
- ['bl', 1],
965
- ['ml', 1],
966
- ['tl', 1],
967
- ]);
968
- if (leftRight.get(e.transform.corner)) {
969
- activeObject.setPositionByOrigin(
970
- new fabric.Point(
971
- objectLeft +
972
- objectWidth / 2 +
973
- activeObjectWidth / 2,
974
- activeObjectTop
975
- ),
976
- 'center',
977
- 'center'
978
- );
979
-
980
- activeObject.set(
981
- 'scaleX',
982
- ((activeLeft +
983
- activeWidth -
984
- (objectLeft + objectWidth / 2)) *
985
- activeObject.scaleX) /
986
- activeObject.getScaledWidth()
987
- );
988
- break;
989
- }
990
- }
991
- //上边线和下边线
992
- if (
993
- isInRange(
994
- objectTop - objectHeight / 2,
995
- activeObjectTop + activeObjectHeight / 2
996
- )
997
- ) {
998
- horizontalInTheRange = true;
999
- horizontalLines = [];
1000
- horizontalLines.push({
1001
- y: objectTop - objectHeight / 2,
1002
- x1:
1003
- objectLeft < activeObjectLeft
1004
- ? objectLeft -
1005
- objectWidth / 2 -
1006
- aligningLineOffset
1007
- : objectLeft +
1008
- objectWidth / 2 +
1009
- aligningLineOffset,
1010
- x2:
1011
- activeObjectLeft > objectLeft
1012
- ? activeObjectLeft +
1013
- activeObjectWidth / 2 +
1014
- aligningLineOffset
1015
- : activeObjectLeft -
1016
- activeObjectWidth / 2 -
1017
- aligningLineOffset,
1018
- });
1019
-
1020
- let bottom = new Map([
1021
- ['mb', 1],
1022
- ['bl', 1],
1023
- ['br', 1],
1024
- ]);
1025
- if (bottom.get(e.transform.corner)) {
1026
- activeObject.set(
1027
- 'scaleY',
1028
- ((objectTop - objectHeight / 2 - activeObject.top) *
1029
- activeObject.scaleY) /
1030
- activeObject.getScaledHeight()
1031
- );
1032
- break;
1033
- }
1034
- }
1035
- //下边线和上变线
1036
- if (
1037
- isInRange(
1038
- objectTop + objectHeight / 2,
1039
- activeObjectTop - activeObjectHeight / 2
1040
- )
1041
- ) {
1042
- horizontalInTheRange = true;
1043
- horizontalLines = [];
1044
- horizontalLines.push({
1045
- y: objectTop + objectHeight / 2,
1046
- x1:
1047
- objectLeft < activeObjectLeft
1048
- ? objectLeft -
1049
- objectWidth / 2 -
1050
- aligningLineOffset
1051
- : objectLeft +
1052
- objectWidth / 2 +
1053
- aligningLineOffset,
1054
- x2:
1055
- activeObjectLeft > objectLeft
1056
- ? activeObjectLeft +
1057
- activeObjectWidth / 2 +
1058
- aligningLineOffset
1059
- : activeObjectLeft -
1060
- activeObjectWidth / 2 -
1061
- aligningLineOffset,
1062
- });
1063
-
1064
- let bottomRight = new Map([
1065
- ['tr', 1],
1066
- ['tl', 1],
1067
- ['mt', 1],
1068
- ]);
1069
-
1070
- if (bottomRight.get(e.transform.corner)) {
1071
- activeObject.setPositionByOrigin(
1072
- new fabric.Point(
1073
- activeObjectLeft,
1074
- objectTop +
1075
- objectHeight / 2 +
1076
- activeObjectHeight / 2
1077
- ),
1078
- 'center',
1079
- 'center'
1080
- );
1081
-
1082
- activeObject.set(
1083
- 'scaleY',
1084
- ((activeTop +
1085
- activeHeight -
1086
- (objectTop + objectHeight / 2)) *
1087
- activeObject.scaleY) /
1088
- activeObject.getScaledHeight()
1089
- );
1090
-
1091
- break;
1092
- }
1093
- }
1094
- }
1095
-
1096
- if (!horizontalInTheRange) {
1097
- horizontalLines.length = 0;
1098
- }
1099
-
1100
- if (!verticalInTheRange) {
1101
- verticalLines.length = 0;
1102
- }
1103
- });
1104
- canvas.on('after:render', () => {
1105
- ctx.save();
1106
- ctx.beginPath();
1107
- ctx.lineWidth = This.defautOption.width;
1108
- ctx.strokeStyle = This.defautOption.color;
1109
-
1110
- for (let i = verticalLines.length; i--;) {
1111
- if (verticalLines[i]) {
1112
- drawVerticalLine(verticalLines[i]);
1113
- }
1114
- }
1115
- for (let j = horizontalLines.length; j--;) {
1116
- if (horizontalLines[j]) {
1117
- drawHorizontalLine(horizontalLines[j]);
1118
- }
1119
- }
1120
- ctx.stroke();
1121
- ctx.restore();
1122
-
1123
- // noinspection NestedAssignmentJS
1124
- verticalLines.length = 0;
1125
- horizontalLines.length = 0;
1126
- });
1127
-
1128
- canvas.on('mouse:up', (e) => {
1129
- const activeObject = e.target;
1130
- if (
1131
- activeObject &&
1132
- activeObject.selectable &&
1133
- !activeObject.lockRotation
1134
- ) {
1135
- activeObject.set('hasControls', true);
1136
- }
1137
- verticalLines.length = 0;
1138
- horizontalLines.length = 0;
1139
- canvas.renderAll();
1140
- });
1141
- }
1142
-
1143
- changeSwitch(switchStatus: boolean) {
1144
- this.switch = switchStatus ?? !this.switch;
1145
- }
1146
-
1147
- destroy() {
1148
- console.log('pluginDestroy');
1149
- }
1150
- }
1151
-
1152
- export default AlignGuidLinePlugin;
1
+ import { fabric, IEditor, IPluginTempl } from '@hprint/core';
2
+
3
+ declare interface VerticalLine {
4
+ x: number;
5
+ y1: number;
6
+ y2: number;
7
+ }
8
+
9
+ declare interface HorizontalLine {
10
+ x1: number;
11
+ x2: number;
12
+ y: number;
13
+ }
14
+
15
+ class AlignGuidLinePlugin implements IPluginTempl {
16
+ static pluginName = 'AlignGuidLinePlugin';
17
+ static apis = ['changeSwitch'];
18
+ defautOption = {
19
+ color: 'rgba(255,95,95,1)',
20
+ width: 1,
21
+ };
22
+ dragMode = false;
23
+ switch!: boolean;
24
+ constructor(
25
+ public canvas: fabric.Canvas,
26
+ public editor: IEditor,
27
+ options?: {
28
+ switch?: boolean;
29
+ }
30
+ ) {
31
+ this.dragMode = false;
32
+ this.init();
33
+ this.switch = options?.switch ?? true;
34
+ }
35
+ init() {
36
+ const { canvas } = this;
37
+ const ctx = canvas.getSelectionContext();
38
+ const aligningLineOffset = 5;
39
+ const aligningLineMargin = 4;
40
+ const This = this;
41
+ let viewportTransform: number[] | undefined;
42
+ let zoom = 1;
43
+ let activeWidth = 0;
44
+ let activeHeight = 0;
45
+ let activeLeft = 0;
46
+ let activeTop = 0;
47
+
48
+ function drawVerticalLine(coords: VerticalLine) {
49
+ drawLine(
50
+ coords.x + 0.5,
51
+ coords.y1 > coords.y2 ? coords.y2 : coords.y1,
52
+ coords.x + 0.5,
53
+ coords.y2 > coords.y1 ? coords.y2 : coords.y1
54
+ );
55
+ }
56
+
57
+ function drawHorizontalLine(coords: HorizontalLine) {
58
+ drawLine(
59
+ coords.x1 > coords.x2 ? coords.x2 : coords.x1,
60
+ coords.y + 0.5,
61
+ coords.x2 > coords.x1 ? coords.x2 : coords.x1,
62
+ coords.y + 0.5
63
+ );
64
+ }
65
+
66
+ function drawLine(x1: number, y1: number, x2: number, y2: number) {
67
+ if (viewportTransform == null) return;
68
+
69
+ ctx.moveTo(
70
+ x1 * zoom + viewportTransform[4],
71
+ y1 * zoom + viewportTransform[5]
72
+ );
73
+ ctx.lineTo(
74
+ x2 * zoom + viewportTransform[4],
75
+ y2 * zoom + viewportTransform[5]
76
+ );
77
+ }
78
+
79
+ function isInRange(value1: number, value2: number) {
80
+ value1 = Math.round(value1);
81
+ value2 = Math.round(value2);
82
+ for (
83
+ let i = value1 - aligningLineMargin,
84
+ len = value1 + aligningLineMargin;
85
+ i <= len;
86
+ i++
87
+ ) {
88
+ if (i === value2) {
89
+ return true;
90
+ }
91
+ }
92
+ return false;
93
+ }
94
+
95
+ let verticalLines: VerticalLine[] = [];
96
+ let horizontalLines: HorizontalLine[] = [];
97
+
98
+ canvas.on('mouse:down', (e) => {
99
+ viewportTransform = canvas.viewportTransform;
100
+ zoom = canvas.getZoom();
101
+ try {
102
+ if (e) {
103
+ activeLeft = e.target.left;
104
+ activeTop = e.target.top;
105
+ activeWidth = e.target.getScaledWidth();
106
+ activeHeight = e.target.getScaledHeight();
107
+ }
108
+ } catch (e) { }
109
+ });
110
+
111
+ canvas.on('object:moving', (e) => {
112
+ if (!this.switch) return;
113
+ if (viewportTransform === undefined || e.target === undefined)
114
+ return;
115
+
116
+ const activeObject = e.target;
117
+ const canvasObjects = canvas.getObjects();
118
+ const activeObjectCenter = activeObject.getCenterPoint();
119
+ const activeObjectLeft = activeObjectCenter.x;
120
+ const activeObjectTop = activeObjectCenter.y;
121
+ const activeObjectBoundingRect = activeObject.getBoundingRect();
122
+ const activeObjectHeight = activeObject.getScaledHeight();
123
+ const activeObjectWidth = activeObject.getScaledWidth();
124
+ let horizontalInTheRange = false;
125
+ let verticalInTheRange = false;
126
+ const transform = canvas._currentTransform;
127
+
128
+ //到达位置
129
+ let reachLeft = false; //左
130
+ let reachTop = false; //上
131
+
132
+ //距离
133
+ let _elReachLeft = 0; //左距离
134
+ let _elReachTop = 0; //上距离
135
+
136
+ activeObject.set('hasControls', false);
137
+
138
+ if (!transform) return;
139
+
140
+ for (let i = canvasObjects.length; i--;) {
141
+ // eslint-disable-next-line no-continue
142
+ if (canvasObjects[i] === activeObject) continue;
143
+
144
+ // 排除辅助线
145
+ if (
146
+ activeObject instanceof fabric.GuideLine &&
147
+ canvasObjects[i] instanceof fabric.GuideLine
148
+ ) {
149
+ continue;
150
+ }
151
+
152
+ const objectCenter = canvasObjects[i].getCenterPoint();
153
+ const objectLeft = objectCenter.x;
154
+ const objectTop = objectCenter.y;
155
+ const objectBoundingRect = canvasObjects[i].getBoundingRect();
156
+ const objectHeight =
157
+ objectBoundingRect.height / viewportTransform[3];
158
+ const objectWidth =
159
+ objectBoundingRect.width / viewportTransform[0];
160
+
161
+ // snap by the horizontal center line
162
+ //水平中心线
163
+ if (isInRange(objectLeft, activeObjectLeft)) {
164
+ verticalInTheRange = true;
165
+ verticalLines = [];
166
+ verticalLines.push({
167
+ x: objectLeft,
168
+ y1:
169
+ objectTop < activeObjectTop
170
+ ? objectTop -
171
+ objectHeight / 2 -
172
+ aligningLineOffset
173
+ : objectTop +
174
+ objectHeight / 2 +
175
+ aligningLineOffset,
176
+ y2:
177
+ activeObjectTop > objectTop
178
+ ? activeObjectTop +
179
+ activeObjectHeight / 2 +
180
+ aligningLineOffset
181
+ : activeObjectTop -
182
+ activeObjectHeight / 2 -
183
+ aligningLineOffset,
184
+ });
185
+ activeObject.setPositionByOrigin(
186
+ new fabric.Point(objectLeft, activeObjectTop),
187
+ 'center',
188
+ 'center'
189
+ );
190
+ }
191
+
192
+ // snap by the vertical center line
193
+ //垂直中心线
194
+ if (isInRange(objectTop, activeObjectTop)) {
195
+ horizontalInTheRange = true;
196
+ horizontalLines = [];
197
+ horizontalLines.push({
198
+ y: objectTop,
199
+ x1:
200
+ objectLeft < activeObjectLeft
201
+ ? objectLeft -
202
+ objectWidth / 2 -
203
+ aligningLineOffset
204
+ : objectLeft +
205
+ objectWidth / 2 +
206
+ aligningLineOffset,
207
+ x2:
208
+ activeObjectLeft > objectLeft
209
+ ? activeObjectLeft +
210
+ activeObjectWidth / 2 +
211
+ aligningLineOffset
212
+ : activeObjectLeft -
213
+ activeObjectWidth / 2 -
214
+ aligningLineOffset,
215
+ });
216
+ activeObject.setPositionByOrigin(
217
+ new fabric.Point(activeObjectLeft, objectTop),
218
+ 'center',
219
+ 'center'
220
+ );
221
+ }
222
+ // snap by the left edge
223
+ //左边线
224
+ if (
225
+ isInRange(
226
+ objectLeft - objectWidth / 2,
227
+ activeObjectLeft - activeObjectWidth / 2
228
+ )
229
+ ) {
230
+ verticalInTheRange = true;
231
+ verticalLines = [];
232
+ reachLeft = true;
233
+ verticalLines.push({
234
+ x: objectLeft - objectWidth / 2,
235
+ y1:
236
+ objectTop < activeObjectTop
237
+ ? objectTop -
238
+ objectHeight / 2 -
239
+ aligningLineOffset
240
+ : objectTop +
241
+ objectHeight / 2 +
242
+ aligningLineOffset,
243
+ y2:
244
+ activeObjectTop > objectTop
245
+ ? activeObjectTop +
246
+ activeObjectHeight / 2 +
247
+ aligningLineOffset
248
+ : activeObjectTop -
249
+ activeObjectHeight / 2 -
250
+ aligningLineOffset,
251
+ });
252
+ _elReachLeft =
253
+ objectLeft - objectWidth / 2 + activeObjectWidth / 2;
254
+ let x =
255
+ objectLeft - objectWidth / 2 + activeObjectWidth / 2;
256
+ let y = reachTop ? _elReachTop : activeObjectTop;
257
+
258
+ activeObject.setPositionByOrigin(
259
+ new fabric.Point(x, y),
260
+ 'center',
261
+ 'center'
262
+ );
263
+ }
264
+
265
+ // snap by the right edge
266
+ //右边线
267
+ if (
268
+ isInRange(
269
+ objectLeft + objectWidth / 2,
270
+ activeObjectLeft + activeObjectWidth / 2
271
+ )
272
+ ) {
273
+ reachLeft = true;
274
+ verticalInTheRange = true;
275
+ verticalLines = [];
276
+ verticalLines.push({
277
+ x: objectLeft + objectWidth / 2,
278
+ y1:
279
+ objectTop < activeObjectTop
280
+ ? objectTop -
281
+ objectHeight / 2 -
282
+ aligningLineOffset
283
+ : objectTop +
284
+ objectHeight / 2 +
285
+ aligningLineOffset,
286
+ y2:
287
+ activeObjectTop > objectTop
288
+ ? activeObjectTop +
289
+ activeObjectHeight / 2 +
290
+ aligningLineOffset
291
+ : activeObjectTop -
292
+ activeObjectHeight / 2 -
293
+ aligningLineOffset,
294
+ });
295
+ _elReachLeft =
296
+ objectLeft + objectWidth / 2 - activeObjectWidth / 2;
297
+ let x =
298
+ objectLeft + objectWidth / 2 - activeObjectWidth / 2;
299
+ let y = reachTop ? _elReachTop : activeObjectTop;
300
+ activeObject.setPositionByOrigin(
301
+ new fabric.Point(x, y),
302
+ 'center',
303
+ 'center'
304
+ );
305
+ }
306
+
307
+ // snap by the top edge
308
+ //上边线
309
+ if (
310
+ isInRange(
311
+ objectTop - objectHeight / 2,
312
+ activeObjectTop - activeObjectHeight / 2
313
+ )
314
+ ) {
315
+ reachTop = true;
316
+ horizontalInTheRange = true;
317
+ horizontalLines = [];
318
+ horizontalLines.push({
319
+ y: objectTop - objectHeight / 2,
320
+ x1:
321
+ objectLeft < activeObjectLeft
322
+ ? objectLeft -
323
+ objectWidth / 2 -
324
+ aligningLineOffset
325
+ : objectLeft +
326
+ objectWidth / 2 +
327
+ aligningLineOffset,
328
+ x2:
329
+ activeObjectLeft > objectLeft
330
+ ? activeObjectLeft +
331
+ activeObjectWidth / 2 +
332
+ aligningLineOffset
333
+ : activeObjectLeft -
334
+ activeObjectWidth / 2 -
335
+ aligningLineOffset,
336
+ });
337
+ _elReachTop =
338
+ objectTop - objectHeight / 2 + activeObjectHeight / 2;
339
+ let x = reachLeft ? _elReachLeft : activeObjectLeft;
340
+ let y =
341
+ objectTop - objectHeight / 2 + activeObjectHeight / 2;
342
+ activeObject.setPositionByOrigin(
343
+ new fabric.Point(x, y),
344
+ 'center',
345
+ 'center'
346
+ );
347
+ }
348
+
349
+ // snap by the bottom edge
350
+ //底边线
351
+ if (
352
+ isInRange(
353
+ objectTop + objectHeight / 2,
354
+ activeObjectTop + activeObjectHeight / 2
355
+ )
356
+ ) {
357
+ reachTop = true;
358
+
359
+ horizontalInTheRange = true;
360
+ horizontalLines = [];
361
+ horizontalLines.push({
362
+ y: objectTop + objectHeight / 2,
363
+ x1:
364
+ objectLeft < activeObjectLeft
365
+ ? objectLeft -
366
+ objectWidth / 2 -
367
+ aligningLineOffset
368
+ : objectLeft +
369
+ objectWidth / 2 +
370
+ aligningLineOffset,
371
+ x2:
372
+ activeObjectLeft > objectLeft
373
+ ? activeObjectLeft +
374
+ activeObjectWidth / 2 +
375
+ aligningLineOffset
376
+ : activeObjectLeft -
377
+ activeObjectWidth / 2 -
378
+ aligningLineOffset,
379
+ });
380
+ _elReachTop =
381
+ objectTop + objectHeight / 2 - activeObjectHeight / 2;
382
+ let x = reachLeft ? _elReachLeft : activeObjectLeft;
383
+ let y =
384
+ objectTop + objectHeight / 2 - activeObjectHeight / 2;
385
+ activeObject.setPositionByOrigin(
386
+ new fabric.Point(x, y),
387
+ 'center',
388
+ 'center'
389
+ );
390
+ }
391
+
392
+ //左边线和右边线
393
+ if (
394
+ isInRange(
395
+ objectLeft - objectWidth / 2,
396
+ activeObjectLeft + activeObjectWidth / 2
397
+ )
398
+ ) {
399
+ reachLeft = true;
400
+ verticalInTheRange = true;
401
+ verticalInTheRange = [];
402
+ verticalLines.push({
403
+ x: objectLeft - objectWidth / 2,
404
+ y1:
405
+ objectTop < activeObjectTop
406
+ ? objectTop -
407
+ objectHeight / 2 -
408
+ aligningLineOffset
409
+ : objectTop +
410
+ objectHeight / 2 +
411
+ aligningLineOffset,
412
+ y2:
413
+ activeObjectTop > objectTop
414
+ ? activeObjectTop +
415
+ activeObjectHeight / 2 +
416
+ aligningLineOffset
417
+ : activeObjectTop -
418
+ activeObjectHeight / 2 -
419
+ aligningLineOffset,
420
+ });
421
+
422
+ _elReachLeft =
423
+ objectLeft - objectWidth / 2 - activeObjectWidth / 2;
424
+ let x =
425
+ objectLeft - objectWidth / 2 - activeObjectWidth / 2;
426
+ let y = activeObjectTop;
427
+
428
+ activeObject.setPositionByOrigin(
429
+ new fabric.Point(x, y),
430
+ 'center',
431
+ 'center'
432
+ );
433
+ }
434
+ //右边线和左边线
435
+ if (
436
+ isInRange(
437
+ objectLeft + objectWidth / 2,
438
+ activeObjectLeft - activeObjectWidth / 2
439
+ )
440
+ ) {
441
+ reachLeft = true;
442
+ verticalInTheRange = true;
443
+ verticalInTheRange = [];
444
+ verticalLines.push({
445
+ x: objectLeft + objectWidth / 2,
446
+ y1:
447
+ objectTop < activeObjectTop
448
+ ? objectTop -
449
+ objectHeight / 2 -
450
+ aligningLineOffset
451
+ : objectTop +
452
+ objectHeight / 2 +
453
+ aligningLineOffset,
454
+ y2:
455
+ activeObjectTop > objectTop
456
+ ? activeObjectTop +
457
+ activeObjectHeight / 2 +
458
+ aligningLineOffset
459
+ : activeObjectTop -
460
+ activeObjectHeight / 2 -
461
+ aligningLineOffset,
462
+ });
463
+ _elReachLeft =
464
+ objectLeft + objectWidth / 2 + activeObjectWidth / 2;
465
+
466
+ let x =
467
+ objectLeft + objectWidth / 2 + activeObjectWidth / 2;
468
+ let y = activeObjectTop;
469
+
470
+ activeObject.setPositionByOrigin(
471
+ new fabric.Point(x, y),
472
+ 'center',
473
+ 'center'
474
+ );
475
+ }
476
+ //上边线和下边线
477
+ if (
478
+ isInRange(
479
+ objectTop - objectHeight / 2,
480
+ activeObjectTop + activeObjectHeight / 2
481
+ )
482
+ ) {
483
+ reachTop = true;
484
+ horizontalInTheRange = true;
485
+ horizontalLines = [];
486
+ horizontalLines.push({
487
+ y: objectTop - objectHeight / 2,
488
+ x1:
489
+ objectLeft < activeObjectLeft
490
+ ? objectLeft -
491
+ objectWidth / 2 -
492
+ aligningLineOffset
493
+ : objectLeft +
494
+ objectWidth / 2 +
495
+ aligningLineOffset,
496
+ x2:
497
+ activeObjectLeft > objectLeft
498
+ ? activeObjectLeft +
499
+ activeObjectWidth / 2 +
500
+ aligningLineOffset
501
+ : activeObjectLeft -
502
+ activeObjectWidth / 2 -
503
+ aligningLineOffset,
504
+ });
505
+ _elReachTop =
506
+ objectTop - objectHeight / 2 - activeObjectHeight / 2;
507
+
508
+ let x = reachLeft ? _elReachLeft : activeObjectLeft;
509
+ let y =
510
+ objectTop - objectHeight / 2 - activeObjectHeight / 2;
511
+ activeObject.setPositionByOrigin(
512
+ new fabric.Point(x, y),
513
+ 'center',
514
+ 'center'
515
+ );
516
+ }
517
+ //下边线和上变线
518
+ if (
519
+ isInRange(
520
+ objectTop + objectHeight / 2,
521
+ activeObjectTop - activeObjectHeight / 2
522
+ )
523
+ ) {
524
+ reachTop = true;
525
+ horizontalInTheRange = true;
526
+ horizontalLines = [];
527
+ horizontalLines.push({
528
+ y: objectTop + objectHeight / 2,
529
+ x1:
530
+ objectLeft < activeObjectLeft
531
+ ? objectLeft -
532
+ objectWidth / 2 -
533
+ aligningLineOffset
534
+ : objectLeft +
535
+ objectWidth / 2 +
536
+ aligningLineOffset,
537
+ x2:
538
+ activeObjectLeft > objectLeft
539
+ ? activeObjectLeft +
540
+ activeObjectWidth / 2 +
541
+ aligningLineOffset
542
+ : activeObjectLeft -
543
+ activeObjectWidth / 2 -
544
+ aligningLineOffset,
545
+ });
546
+ _elReachTop =
547
+ objectTop + objectHeight / 2 + activeObjectHeight / 2;
548
+ let x = reachLeft ? _elReachLeft : activeObjectLeft;
549
+ let y =
550
+ objectTop + objectHeight / 2 + activeObjectHeight / 2;
551
+ activeObject.setPositionByOrigin(
552
+ new fabric.Point(x, y),
553
+ 'center',
554
+ 'center'
555
+ );
556
+ }
557
+ }
558
+
559
+ if (!horizontalInTheRange) {
560
+ horizontalLines.length = 0;
561
+ }
562
+
563
+ if (!verticalInTheRange) {
564
+ verticalLines.length = 0;
565
+ }
566
+ });
567
+
568
+ canvas.on('before:render', () => {
569
+ // fix 保存图片时报错
570
+ if (canvas.contextTop === null) {
571
+ return;
572
+ }
573
+ try {
574
+ canvas.clearContext(canvas.contextTop);
575
+ } catch (error) {
576
+ console.log(error);
577
+ }
578
+ });
579
+
580
+ canvas.on('object:scaling', (e) => {
581
+ if (viewportTransform === undefined || e.target === undefined)
582
+ return;
583
+
584
+ const activeObject = e.target;
585
+ const canvasObjects = canvas.getObjects();
586
+ const activeObjectCenter = activeObject.getCenterPoint();
587
+ const activeObjectLeft = activeObjectCenter.x;
588
+ const activeObjectTop = activeObjectCenter.y;
589
+ const activeObjectBoundingRect = activeObject.getBoundingRect();
590
+ const activeObjectHeight = activeObject.getScaledHeight();
591
+ const activeObjectWidth = activeObject.getScaledWidth();
592
+ let horizontalInTheRange = false;
593
+ let verticalInTheRange = false;
594
+ const transform = canvas._currentTransform;
595
+
596
+ activeObject.set('hasControls', false);
597
+ if (!transform) return;
598
+
599
+ for (let i = canvasObjects.length; i--;) {
600
+ // eslint-disable-next-line no-continue
601
+ if (canvasObjects[i] === activeObject) continue;
602
+
603
+ // 排除辅助线
604
+ if (
605
+ activeObject instanceof fabric.GuideLine &&
606
+ canvasObjects[i] instanceof fabric.GuideLine
607
+ ) {
608
+ continue;
609
+ }
610
+
611
+ const objectCenter = canvasObjects[i].getCenterPoint();
612
+ const objectLeft = objectCenter.x;
613
+ const objectTop = objectCenter.y;
614
+ const objectBoundingRect = canvasObjects[i].getBoundingRect();
615
+ const objectHeight =
616
+ objectBoundingRect.height / viewportTransform[3];
617
+ const objectWidth =
618
+ objectBoundingRect.width / viewportTransform[0];
619
+
620
+ // snap by the horizontal center line
621
+ //水平中心线
622
+ if (isInRange(objectLeft, activeObjectLeft)) {
623
+ verticalInTheRange = true;
624
+ verticalLines = [];
625
+ verticalLines.push({
626
+ x: objectLeft,
627
+ y1:
628
+ objectTop < activeObjectTop
629
+ ? objectTop -
630
+ objectHeight / 2 -
631
+ aligningLineOffset
632
+ : objectTop +
633
+ objectHeight / 2 +
634
+ aligningLineOffset,
635
+ y2:
636
+ activeObjectTop > objectTop
637
+ ? activeObjectTop +
638
+ activeObjectHeight / 2 +
639
+ aligningLineOffset
640
+ : activeObjectTop -
641
+ activeObjectHeight / 2 -
642
+ aligningLineOffset,
643
+ });
644
+ }
645
+
646
+ // snap by the left edge
647
+ //左边线
648
+ if (
649
+ isInRange(
650
+ objectLeft - objectWidth / 2,
651
+ activeObjectLeft - activeObjectWidth / 2
652
+ )
653
+ ) {
654
+ verticalInTheRange = true;
655
+ verticalLines = [];
656
+ verticalLines.push({
657
+ x: objectLeft - objectWidth / 2,
658
+ y1:
659
+ objectTop < activeObjectTop
660
+ ? objectTop -
661
+ objectHeight / 2 -
662
+ aligningLineOffset
663
+ : objectTop +
664
+ objectHeight / 2 +
665
+ aligningLineOffset,
666
+ y2:
667
+ activeObjectTop > objectTop
668
+ ? activeObjectTop +
669
+ activeObjectHeight / 2 +
670
+ aligningLineOffset
671
+ : activeObjectTop -
672
+ activeObjectHeight / 2 -
673
+ aligningLineOffset,
674
+ });
675
+
676
+ let leftRight = new Map([
677
+ ['bl', 1],
678
+ ['ml', 1],
679
+ ['tl', 1],
680
+ ]);
681
+ if (leftRight.get(e.transform.corner)) {
682
+ activeObject.setPositionByOrigin(
683
+ new fabric.Point(
684
+ objectLeft -
685
+ objectWidth / 2 +
686
+ activeObjectWidth / 2,
687
+ activeObjectTop
688
+ ),
689
+ 'center',
690
+ 'center'
691
+ );
692
+
693
+ activeObject.set(
694
+ 'scaleX',
695
+ ((activeLeft -
696
+ (objectLeft - objectWidth / 2) +
697
+ activeWidth) *
698
+ activeObject.scaleX) /
699
+ activeObject.getScaledWidth()
700
+ );
701
+ break;
702
+ }
703
+ }
704
+
705
+ // snap by the right edge
706
+ //右边线
707
+ if (
708
+ isInRange(
709
+ objectLeft + objectWidth / 2,
710
+ activeObjectLeft + activeObjectWidth / 2
711
+ )
712
+ ) {
713
+ verticalInTheRange = true;
714
+ verticalLines = [];
715
+ verticalLines.push({
716
+ x: objectLeft + objectWidth / 2,
717
+ y1:
718
+ objectTop < activeObjectTop
719
+ ? objectTop -
720
+ objectHeight / 2 -
721
+ aligningLineOffset
722
+ : objectTop +
723
+ objectHeight / 2 +
724
+ aligningLineOffset,
725
+ y2:
726
+ activeObjectTop > objectTop
727
+ ? activeObjectTop +
728
+ activeObjectHeight / 2 +
729
+ aligningLineOffset
730
+ : activeObjectTop -
731
+ activeObjectHeight / 2 -
732
+ aligningLineOffset,
733
+ });
734
+
735
+ let Right = new Map([
736
+ ['mr', 1],
737
+ ['tr', 1],
738
+ ['br', 1],
739
+ ]);
740
+
741
+ if (Right.get(e.transform.corner)) {
742
+ activeObject.set(
743
+ 'scaleX',
744
+ ((objectLeft +
745
+ objectWidth / 2 -
746
+ (activeLeft + activeWidth) +
747
+ activeWidth) *
748
+ activeObject.scaleX) /
749
+ activeObject.getScaledWidth()
750
+ );
751
+ break;
752
+ }
753
+ }
754
+
755
+ // snap by the vertical center line
756
+ //垂直中心线
757
+ if (isInRange(objectTop, activeObjectTop)) {
758
+ horizontalInTheRange = true;
759
+ horizontalLines = [];
760
+ horizontalLines.push({
761
+ y: objectTop,
762
+ x1:
763
+ objectLeft < activeObjectLeft
764
+ ? objectLeft -
765
+ objectWidth / 2 -
766
+ aligningLineOffset
767
+ : objectLeft +
768
+ objectWidth / 2 +
769
+ aligningLineOffset,
770
+ x2:
771
+ activeObjectLeft > objectLeft
772
+ ? activeObjectLeft +
773
+ activeObjectWidth / 2 +
774
+ aligningLineOffset
775
+ : activeObjectLeft -
776
+ activeObjectWidth / 2 -
777
+ aligningLineOffset,
778
+ });
779
+ }
780
+
781
+ // snap by the top edge
782
+ if (
783
+ isInRange(
784
+ objectTop - objectHeight / 2,
785
+ activeObjectTop - activeObjectHeight / 2
786
+ )
787
+ ) {
788
+ horizontalInTheRange = true;
789
+ horizontalLines = [];
790
+ horizontalLines.push({
791
+ y: objectTop - objectHeight / 2,
792
+ x1:
793
+ objectLeft < activeObjectLeft
794
+ ? objectLeft -
795
+ objectWidth / 2 -
796
+ aligningLineOffset
797
+ : objectLeft +
798
+ objectWidth / 2 +
799
+ aligningLineOffset,
800
+ x2:
801
+ activeObjectLeft > objectLeft
802
+ ? activeObjectLeft +
803
+ activeObjectWidth / 2 +
804
+ aligningLineOffset
805
+ : activeObjectLeft -
806
+ activeObjectWidth / 2 -
807
+ aligningLineOffset,
808
+ });
809
+
810
+ let bottomRight = new Map([
811
+ ['tr', 1],
812
+ ['tl', 1],
813
+ ['mt', 1],
814
+ ]);
815
+
816
+ if (bottomRight.get(e.transform.corner)) {
817
+ activeObject.setPositionByOrigin(
818
+ new fabric.Point(
819
+ activeObjectLeft,
820
+ objectTop -
821
+ objectHeight / 2 +
822
+ activeObjectHeight / 2
823
+ ),
824
+ 'center',
825
+ 'center'
826
+ );
827
+
828
+ activeObject.set(
829
+ 'scaleY',
830
+ ((activeTop +
831
+ activeHeight -
832
+ (objectTop - objectHeight / 2)) *
833
+ activeObject.scaleY) /
834
+ activeObject.getScaledHeight()
835
+ );
836
+ break;
837
+ }
838
+ }
839
+
840
+ // snap by the bottom edge
841
+ if (
842
+ isInRange(
843
+ objectTop + objectHeight / 2,
844
+ activeObjectTop + activeObjectHeight / 2
845
+ )
846
+ ) {
847
+ horizontalInTheRange = true;
848
+ horizontalLines = [];
849
+ horizontalLines.push({
850
+ y: objectTop + objectHeight / 2,
851
+ x1:
852
+ objectLeft < activeObjectLeft
853
+ ? objectLeft -
854
+ objectWidth / 2 -
855
+ aligningLineOffset
856
+ : objectLeft +
857
+ objectWidth / 2 +
858
+ aligningLineOffset,
859
+ x2:
860
+ activeObjectLeft > objectLeft
861
+ ? activeObjectLeft +
862
+ activeObjectWidth / 2 +
863
+ aligningLineOffset
864
+ : activeObjectLeft -
865
+ activeObjectWidth / 2 -
866
+ aligningLineOffset,
867
+ });
868
+
869
+ let bottom = new Map([
870
+ ['mb', 1],
871
+ ['bl', 1],
872
+ ['br', 1],
873
+ ]);
874
+ if (bottom.get(e.transform.corner)) {
875
+ activeObject.set(
876
+ 'scaleY',
877
+ ((objectTop +
878
+ objectHeight / 2 -
879
+ (activeTop + activeHeight) +
880
+ activeHeight) *
881
+ activeObject.scaleY) /
882
+ activeObject.getScaledHeight()
883
+ );
884
+ break;
885
+ }
886
+ }
887
+
888
+ //左边线和右边线
889
+ if (
890
+ isInRange(
891
+ objectLeft - objectWidth / 2,
892
+ activeObjectLeft + activeObjectWidth / 2
893
+ )
894
+ ) {
895
+ verticalInTheRange = true;
896
+ verticalLines = [];
897
+ verticalLines.push({
898
+ x: objectLeft - objectWidth / 2,
899
+ y1:
900
+ objectTop < activeObjectTop
901
+ ? objectTop -
902
+ objectHeight / 2 -
903
+ aligningLineOffset
904
+ : objectTop +
905
+ objectHeight / 2 +
906
+ aligningLineOffset,
907
+ y2:
908
+ activeObjectTop > objectTop
909
+ ? activeObjectTop +
910
+ activeObjectHeight / 2 +
911
+ aligningLineOffset
912
+ : activeObjectTop -
913
+ activeObjectHeight / 2 -
914
+ aligningLineOffset,
915
+ });
916
+
917
+ let right = new Map([
918
+ ['mr', 1],
919
+ ['tr', 1],
920
+ ['br', 1],
921
+ ]);
922
+ if (right.get(e.transform.corner)) {
923
+ activeObject.set(
924
+ 'scaleX',
925
+ ((objectLeft -
926
+ objectWidth / 2 -
927
+ activeObject.left) *
928
+ activeObject.scaleX) /
929
+ activeObject.getScaledWidth()
930
+ );
931
+ break;
932
+ }
933
+ }
934
+ //右边线和左边线
935
+ if (
936
+ isInRange(
937
+ objectLeft + objectWidth / 2,
938
+ activeObjectLeft - activeObjectWidth / 2
939
+ )
940
+ ) {
941
+ verticalInTheRange = true;
942
+ verticalLines = [];
943
+ verticalLines.push({
944
+ x: objectLeft + objectWidth / 2,
945
+ y1:
946
+ objectTop < activeObjectTop
947
+ ? objectTop -
948
+ objectHeight / 2 -
949
+ aligningLineOffset
950
+ : objectTop +
951
+ objectHeight / 2 +
952
+ aligningLineOffset,
953
+ y2:
954
+ activeObjectTop > objectTop
955
+ ? activeObjectTop +
956
+ activeObjectHeight / 2 +
957
+ aligningLineOffset
958
+ : activeObjectTop -
959
+ activeObjectHeight / 2 -
960
+ aligningLineOffset,
961
+ });
962
+
963
+ let leftRight = new Map([
964
+ ['bl', 1],
965
+ ['ml', 1],
966
+ ['tl', 1],
967
+ ]);
968
+ if (leftRight.get(e.transform.corner)) {
969
+ activeObject.setPositionByOrigin(
970
+ new fabric.Point(
971
+ objectLeft +
972
+ objectWidth / 2 +
973
+ activeObjectWidth / 2,
974
+ activeObjectTop
975
+ ),
976
+ 'center',
977
+ 'center'
978
+ );
979
+
980
+ activeObject.set(
981
+ 'scaleX',
982
+ ((activeLeft +
983
+ activeWidth -
984
+ (objectLeft + objectWidth / 2)) *
985
+ activeObject.scaleX) /
986
+ activeObject.getScaledWidth()
987
+ );
988
+ break;
989
+ }
990
+ }
991
+ //上边线和下边线
992
+ if (
993
+ isInRange(
994
+ objectTop - objectHeight / 2,
995
+ activeObjectTop + activeObjectHeight / 2
996
+ )
997
+ ) {
998
+ horizontalInTheRange = true;
999
+ horizontalLines = [];
1000
+ horizontalLines.push({
1001
+ y: objectTop - objectHeight / 2,
1002
+ x1:
1003
+ objectLeft < activeObjectLeft
1004
+ ? objectLeft -
1005
+ objectWidth / 2 -
1006
+ aligningLineOffset
1007
+ : objectLeft +
1008
+ objectWidth / 2 +
1009
+ aligningLineOffset,
1010
+ x2:
1011
+ activeObjectLeft > objectLeft
1012
+ ? activeObjectLeft +
1013
+ activeObjectWidth / 2 +
1014
+ aligningLineOffset
1015
+ : activeObjectLeft -
1016
+ activeObjectWidth / 2 -
1017
+ aligningLineOffset,
1018
+ });
1019
+
1020
+ let bottom = new Map([
1021
+ ['mb', 1],
1022
+ ['bl', 1],
1023
+ ['br', 1],
1024
+ ]);
1025
+ if (bottom.get(e.transform.corner)) {
1026
+ activeObject.set(
1027
+ 'scaleY',
1028
+ ((objectTop - objectHeight / 2 - activeObject.top) *
1029
+ activeObject.scaleY) /
1030
+ activeObject.getScaledHeight()
1031
+ );
1032
+ break;
1033
+ }
1034
+ }
1035
+ //下边线和上变线
1036
+ if (
1037
+ isInRange(
1038
+ objectTop + objectHeight / 2,
1039
+ activeObjectTop - activeObjectHeight / 2
1040
+ )
1041
+ ) {
1042
+ horizontalInTheRange = true;
1043
+ horizontalLines = [];
1044
+ horizontalLines.push({
1045
+ y: objectTop + objectHeight / 2,
1046
+ x1:
1047
+ objectLeft < activeObjectLeft
1048
+ ? objectLeft -
1049
+ objectWidth / 2 -
1050
+ aligningLineOffset
1051
+ : objectLeft +
1052
+ objectWidth / 2 +
1053
+ aligningLineOffset,
1054
+ x2:
1055
+ activeObjectLeft > objectLeft
1056
+ ? activeObjectLeft +
1057
+ activeObjectWidth / 2 +
1058
+ aligningLineOffset
1059
+ : activeObjectLeft -
1060
+ activeObjectWidth / 2 -
1061
+ aligningLineOffset,
1062
+ });
1063
+
1064
+ let bottomRight = new Map([
1065
+ ['tr', 1],
1066
+ ['tl', 1],
1067
+ ['mt', 1],
1068
+ ]);
1069
+
1070
+ if (bottomRight.get(e.transform.corner)) {
1071
+ activeObject.setPositionByOrigin(
1072
+ new fabric.Point(
1073
+ activeObjectLeft,
1074
+ objectTop +
1075
+ objectHeight / 2 +
1076
+ activeObjectHeight / 2
1077
+ ),
1078
+ 'center',
1079
+ 'center'
1080
+ );
1081
+
1082
+ activeObject.set(
1083
+ 'scaleY',
1084
+ ((activeTop +
1085
+ activeHeight -
1086
+ (objectTop + objectHeight / 2)) *
1087
+ activeObject.scaleY) /
1088
+ activeObject.getScaledHeight()
1089
+ );
1090
+
1091
+ break;
1092
+ }
1093
+ }
1094
+ }
1095
+
1096
+ if (!horizontalInTheRange) {
1097
+ horizontalLines.length = 0;
1098
+ }
1099
+
1100
+ if (!verticalInTheRange) {
1101
+ verticalLines.length = 0;
1102
+ }
1103
+ });
1104
+ canvas.on('after:render', () => {
1105
+ ctx.save();
1106
+ ctx.beginPath();
1107
+ ctx.lineWidth = This.defautOption.width;
1108
+ ctx.strokeStyle = This.defautOption.color;
1109
+
1110
+ for (let i = verticalLines.length; i--;) {
1111
+ if (verticalLines[i]) {
1112
+ drawVerticalLine(verticalLines[i]);
1113
+ }
1114
+ }
1115
+ for (let j = horizontalLines.length; j--;) {
1116
+ if (horizontalLines[j]) {
1117
+ drawHorizontalLine(horizontalLines[j]);
1118
+ }
1119
+ }
1120
+ ctx.stroke();
1121
+ ctx.restore();
1122
+
1123
+ // noinspection NestedAssignmentJS
1124
+ verticalLines.length = 0;
1125
+ horizontalLines.length = 0;
1126
+ });
1127
+
1128
+ canvas.on('mouse:up', (e) => {
1129
+ const activeObject = e.target;
1130
+ if (
1131
+ activeObject &&
1132
+ activeObject.selectable &&
1133
+ !activeObject.lockRotation
1134
+ ) {
1135
+ activeObject.set('hasControls', true);
1136
+ }
1137
+ verticalLines.length = 0;
1138
+ horizontalLines.length = 0;
1139
+ canvas.renderAll();
1140
+ });
1141
+ }
1142
+
1143
+ changeSwitch(switchStatus: boolean) {
1144
+ this.switch = switchStatus ?? !this.switch;
1145
+ }
1146
+
1147
+ destroy() {
1148
+ console.log('pluginDestroy');
1149
+ }
1150
+ }
1151
+
1152
+ export default AlignGuidLinePlugin;