@operato/image-marker 8.0.0-alpha.29 → 8.0.0-alpha.37

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.
@@ -10,6 +10,7 @@ let OxImageMarker = class OxImageMarker extends BaseImageMarker {
10
10
  super(...arguments);
11
11
  this.currentMode = 'edit';
12
12
  this.selectedShape = null;
13
+ // private drawingStarted: boolean = false
13
14
  this.drawing = false;
14
15
  this.startX = 0;
15
16
  this.startY = 0;
@@ -17,142 +18,14 @@ let OxImageMarker = class OxImageMarker extends BaseImageMarker {
17
18
  this.offsetY = 0;
18
19
  this.shapeMoveThreshold = 10;
19
20
  this.shapeMoveInitiated = false;
20
- this.onSvgTouchStart = (event) => {
21
- event.preventDefault();
22
- if (this.isDoubleTouch(event)) {
23
- this.resetZoom();
24
- }
25
- if (event.touches.length === 1) {
26
- this.setSelectedShape(null);
27
- if (this.currentMode === 'shape') {
28
- this.startDrawing(event);
29
- }
30
- else {
31
- this.startPanning(event);
32
- }
33
- }
34
- else if (event.touches.length === 2) {
35
- this.startPinchZooming(event);
36
- }
37
- };
38
- this.onMouseMoveForDrawing = (event) => {
39
- if (!this.drawing) {
40
- return;
41
- }
42
- event.preventDefault();
43
- const { x, y } = this.getSvgPoint(event);
44
- const minDistance = 5;
45
- const distance = Math.sqrt(Math.pow(x - this.startX, 2) + Math.pow(y - this.startY, 2));
46
- if (!this.selectedShape && distance >= minDistance) {
47
- let newShape;
48
- switch (this.currentShapeType) {
49
- case 'rectangle':
50
- newShape = {
51
- id: Date.now().toString(),
52
- type: 'rectangle',
53
- x: this.startX,
54
- y: this.startY,
55
- width: 0,
56
- height: 0
57
- };
58
- break;
59
- case 'circle':
60
- newShape = {
61
- id: Date.now().toString(),
62
- type: 'circle',
63
- cx: this.startX,
64
- cy: this.startY,
65
- r: 0
66
- };
67
- break;
68
- case 'triangle':
69
- newShape = {
70
- id: Date.now().toString(),
71
- type: 'triangle',
72
- x1: this.startX,
73
- y1: this.startY,
74
- width: 0,
75
- height: 0,
76
- points: `${this.startX},${this.startY} ${this.startX},${this.startY} ${this.startX},${this.startY}`
77
- };
78
- break;
79
- case 'line':
80
- newShape = {
81
- id: Date.now().toString(),
82
- type: 'line',
83
- x1: this.startX,
84
- y1: this.startY,
85
- width: 0,
86
- height: 0
87
- };
88
- break;
89
- case 'arrow':
90
- newShape = {
91
- id: Date.now().toString(),
92
- type: 'arrow',
93
- x1: this.startX,
94
- y1: this.startY,
95
- width: 0,
96
- height: 0
97
- };
98
- break;
99
- default:
100
- return;
101
- }
102
- this.shapes = [...this.shapes, newShape];
103
- this.setSelectedShape(newShape);
104
- }
105
- if (this.selectedShape) {
106
- switch (this.selectedShape.type) {
107
- case 'rectangle':
108
- this.selectedShape.x = Math.min(x, this.startX);
109
- this.selectedShape.y = Math.min(y, this.startY);
110
- this.selectedShape.width = Math.abs(x - this.startX);
111
- this.selectedShape.height = Math.abs(y - this.startY);
112
- break;
113
- case 'circle':
114
- const dx = x - this.selectedShape.cx;
115
- const dy = y - this.selectedShape.cy;
116
- this.selectedShape.r = Math.sqrt(dx * dx + dy * dy);
117
- break;
118
- case 'triangle':
119
- const x2 = x;
120
- const y2 = y;
121
- const x3 = this.startX - (x - this.startX);
122
- const y3 = y;
123
- this.selectedShape.points = `${this.startX},${this.startY} ${x2},${y2} ${x3},${y3}`;
124
- break;
125
- case 'line':
126
- case 'arrow':
127
- this.selectedShape.x2 = x;
128
- this.selectedShape.y2 = y;
129
- break;
130
- }
131
- this.requestUpdate();
132
- }
133
- };
134
- this.onMouseUpForDrawing = (event) => {
135
- event.preventDefault();
136
- this.drawing = false;
137
- const svg = this.svg;
138
- if ('touches' in event) {
139
- svg.removeEventListener('touchmove', this.onMouseMoveForDrawing);
140
- svg.removeEventListener('touchend', this.onMouseUpForDrawing);
141
- }
142
- else {
143
- svg.removeEventListener('mousemove', this.onMouseMoveForDrawing);
144
- svg.removeEventListener('mouseup', this.onMouseUpForDrawing);
145
- }
146
- this.imageMarkerMode.setMode('edit');
147
- this.requestUpdate();
148
- };
149
- this.onMouseMoveOnShape = (event) => {
21
+ this.onPointerMoveOnShape = (event) => {
150
22
  if (!this.selectedShape) {
151
23
  return;
152
24
  }
153
- if ('touches' in event && event.touches.length > 1) {
154
- return;
155
- }
25
+ // TODO check if single touch ... (멀티터치를 제외해야한다).
26
+ // if ('touches' in event && event.touches.length > 1) {
27
+ // return
28
+ // }
156
29
  event.preventDefault();
157
30
  const { x, y } = this.getSvgPoint(event);
158
31
  const dx = x - this.offsetX;
@@ -190,25 +63,19 @@ let OxImageMarker = class OxImageMarker extends BaseImageMarker {
190
63
  }
191
64
  this.requestUpdate();
192
65
  };
193
- this.onMouseUpOnShape = (event) => {
66
+ this.onPointerUpOnShape = (event) => {
194
67
  if (!this.selectedShape) {
195
68
  return;
196
69
  }
197
- if ('touches' in event && event.touches.length > 1) {
198
- return;
199
- }
70
+ // TODO
71
+ // if ('touches' in event && event.touches.length > 1) {
72
+ // return
73
+ // }
200
74
  event.preventDefault();
201
- const svg = this.svg;
202
- if ('touches' in event) {
203
- svg.removeEventListener('touchmove', this.onMouseMoveOnShape);
204
- svg.removeEventListener('touchend', this.onMouseUpOnShape);
205
- }
206
- else {
207
- svg.removeEventListener('mousemove', this.onMouseMoveOnShape);
208
- svg.removeEventListener('mouseup', this.onMouseUpOnShape);
209
- }
75
+ document.removeEventListener('pointermove', this.onPointerMoveOnShape);
76
+ document.removeEventListener('pointerup', this.onPointerUpOnShape);
210
77
  };
211
- this.onResizeHandleMouseMove = (event) => {
78
+ this.onResizeHandlePointerMove = (event) => {
212
79
  if (!this.selectedShape) {
213
80
  return;
214
81
  }
@@ -282,17 +149,10 @@ let OxImageMarker = class OxImageMarker extends BaseImageMarker {
282
149
  }
283
150
  this.requestUpdate();
284
151
  };
285
- this.onResizeHandleMouseUp = (event) => {
152
+ this.onResizeHandlePointerUp = (event) => {
286
153
  event.preventDefault();
287
- const svg = this.svg;
288
- if ('touches' in event) {
289
- svg.removeEventListener('touchmove', this.onResizeHandleMouseMove);
290
- svg.removeEventListener('touchend', this.onResizeHandleMouseUp);
291
- }
292
- else {
293
- svg.removeEventListener('mousemove', this.onResizeHandleMouseMove);
294
- svg.removeEventListener('mouseup', this.onResizeHandleMouseUp);
295
- }
154
+ document.removeEventListener('pointermove', this.onResizeHandlePointerMove);
155
+ document.removeEventListener('pointerup', this.onResizeHandlePointerUp);
296
156
  };
297
157
  this.onFocus = () => {
298
158
  this.addEventListener('keydown', this.handleKeyDown);
@@ -350,14 +210,15 @@ let OxImageMarker = class OxImageMarker extends BaseImageMarker {
350
210
  return html `
351
211
  <div class="container">
352
212
  <svg
353
- @mousedown=${this.onSvgMouseDown}
354
- @touchstart=${this.onSvgTouchStart}
355
213
  @wheel=${this.onSvgWheel}
356
- @dblclick=${this.onSvgDoubleClick}
214
+ @doubletap=${this.onSvgDoubleTap}
215
+ @pinch=${this.onSvgPinch}
216
+ @drag=${this.onSvgDrag}
217
+ @click=${this.onSvgClick}
357
218
  viewBox=${this.viewBox}
358
219
  ?drawing=${this.currentMode == 'shape'}
359
220
  >
360
- <image href=${this.imageUrl} @load=${this.onImageLoad}></image>
221
+ <image href=${this.currentImage} @load=${this.onImageLoad}></image>
361
222
  ${this.shapes.map(shape => keyed(shape.id, this.renderShape(shape)))}
362
223
  </svg>
363
224
  </div>
@@ -369,7 +230,7 @@ let OxImageMarker = class OxImageMarker extends BaseImageMarker {
369
230
  const { mode, shapeType } = e.detail;
370
231
  this.setMode(mode, shapeType);
371
232
  }}
372
- @tap-fit-size=${() => this.resetZoom()}
233
+ @tap-fit-size=${() => this.reset()}
373
234
  @tap-delete=${() => this.deleteSelectedShape()}
374
235
  @tap-save=${() => {
375
236
  this.dispatchEvent(new CustomEvent('shapes-changed', {
@@ -381,48 +242,152 @@ let OxImageMarker = class OxImageMarker extends BaseImageMarker {
381
242
  ></ox-image-marker-mode>
382
243
  `;
383
244
  }
384
- onSvgMouseDown(event) {
385
- event.preventDefault();
386
- this.setSelectedShape(null);
245
+ setMode(mode, shapeType) {
246
+ if (mode == 'view') {
247
+ this.setSelectedShape(null);
248
+ }
249
+ this.currentMode = mode;
250
+ this.currentShapeType = shapeType;
251
+ }
252
+ onSvgPinch(e) {
253
+ e.stopPropagation();
254
+ const { scale, centerX, centerY } = e.detail;
255
+ this.zoom(scale, centerX, centerY);
256
+ }
257
+ onSvgDrag(e) {
258
+ const { deltaX, deltaY, end } = e.detail;
387
259
  if (this.currentMode === 'shape') {
388
- this.startDrawing(event);
260
+ this.drawShape(e);
389
261
  }
390
262
  else {
391
- this.startPanning(event);
263
+ const [vbX, vbY, vbWidth, vbHeight] = this.viewBox.split(' ').map(Number);
264
+ const scaleX = vbWidth / this.svg.clientWidth;
265
+ const scaleY = vbHeight / this.svg.clientHeight;
266
+ const scale = Math.max(scaleX, scaleY);
267
+ const newVbX = vbX - deltaX * scale;
268
+ const newVbY = vbY - deltaY * scale;
269
+ this.viewBox = `${newVbX} ${newVbY} ${vbWidth} ${vbHeight}`;
270
+ this.debouncedUpdateImage();
392
271
  }
393
272
  }
394
- startDrawing(event) {
395
- const { x, y } = this.getSvgPoint(event);
396
- if (this.currentShapeType === 'text') {
273
+ onSvgClick(e) {
274
+ if (this.currentMode === 'shape' && this.currentShapeType === 'text') {
275
+ const { x, y } = this.getSvgPoint(e);
397
276
  const textElement = document.createElementNS('http://www.w3.org/2000/svg', 'text');
398
277
  textElement.setAttribute('x', x.toString());
399
278
  textElement.setAttribute('y', y.toString());
400
279
  textElement.textContent = 'ABC';
401
280
  this.svg.appendChild(textElement);
402
- requestAnimationFrame(() => {
403
- this.showTextInput(textElement, null, { x, y });
404
- });
281
+ /* 강제 지연은 iOS에서 input focus를 받지 못하는 현상이 있다. */
282
+ this.showTextInput(textElement, null, { x, y });
283
+ this.imageMarkerMode.setMode('edit');
405
284
  }
406
- else {
285
+ }
286
+ drawShape(e) {
287
+ const { end } = e.detail;
288
+ const { x, y } = this.getSvgPoint(e);
289
+ if (!this.drawing) {
407
290
  this.startX = x;
408
291
  this.startY = y;
292
+ this.setSelectedShape(null);
409
293
  this.drawing = true;
410
294
  }
411
- if ('touches' in event) {
412
- this.svg.addEventListener('touchmove', this.onMouseMoveForDrawing);
413
- this.svg.addEventListener('touchend', this.onMouseUpForDrawing);
414
- }
415
- else {
416
- this.svg.addEventListener('mousemove', this.onMouseMoveForDrawing);
417
- this.svg.addEventListener('mouseup', this.onMouseUpForDrawing);
418
- }
419
- }
420
- setMode(mode, shapeType) {
421
- if (mode == 'view') {
422
- this.setSelectedShape(null);
295
+ else if (this.drawing) {
296
+ const minDistance = 5;
297
+ const distance = Math.sqrt(Math.pow(x - this.startX, 2) + Math.pow(y - this.startY, 2));
298
+ if (!this.selectedShape && distance >= minDistance) {
299
+ let newShape;
300
+ switch (this.currentShapeType) {
301
+ case 'rectangle':
302
+ newShape = {
303
+ id: Date.now().toString(),
304
+ type: 'rectangle',
305
+ x: this.startX,
306
+ y: this.startY,
307
+ width: 0,
308
+ height: 0
309
+ };
310
+ break;
311
+ case 'circle':
312
+ newShape = {
313
+ id: Date.now().toString(),
314
+ type: 'circle',
315
+ cx: this.startX,
316
+ cy: this.startY,
317
+ r: 0
318
+ };
319
+ break;
320
+ case 'triangle':
321
+ newShape = {
322
+ id: Date.now().toString(),
323
+ type: 'triangle',
324
+ x1: this.startX,
325
+ y1: this.startY,
326
+ width: 0,
327
+ height: 0,
328
+ points: `${this.startX},${this.startY} ${this.startX},${this.startY} ${this.startX},${this.startY}`
329
+ };
330
+ break;
331
+ case 'line':
332
+ newShape = {
333
+ id: Date.now().toString(),
334
+ type: 'line',
335
+ x1: this.startX,
336
+ y1: this.startY,
337
+ width: 0,
338
+ height: 0
339
+ };
340
+ break;
341
+ case 'arrow':
342
+ newShape = {
343
+ id: Date.now().toString(),
344
+ type: 'arrow',
345
+ x1: this.startX,
346
+ y1: this.startY,
347
+ width: 0,
348
+ height: 0
349
+ };
350
+ break;
351
+ default:
352
+ return;
353
+ }
354
+ this.shapes = [...this.shapes, newShape];
355
+ this.setSelectedShape(newShape);
356
+ }
357
+ if (this.selectedShape) {
358
+ switch (this.selectedShape.type) {
359
+ case 'rectangle':
360
+ this.selectedShape.x = Math.min(x, this.startX);
361
+ this.selectedShape.y = Math.min(y, this.startY);
362
+ this.selectedShape.width = Math.abs(x - this.startX);
363
+ this.selectedShape.height = Math.abs(y - this.startY);
364
+ break;
365
+ case 'circle':
366
+ const dx = x - this.selectedShape.cx;
367
+ const dy = y - this.selectedShape.cy;
368
+ this.selectedShape.r = Math.sqrt(dx * dx + dy * dy);
369
+ break;
370
+ case 'triangle':
371
+ const x2 = x;
372
+ const y2 = y;
373
+ const x3 = this.startX - (x - this.startX);
374
+ const y3 = y;
375
+ this.selectedShape.points = `${this.startX},${this.startY} ${x2},${y2} ${x3},${y3}`;
376
+ break;
377
+ case 'line':
378
+ case 'arrow':
379
+ this.selectedShape.x2 = x;
380
+ this.selectedShape.y2 = y;
381
+ break;
382
+ }
383
+ this.requestUpdate();
384
+ }
385
+ if (end) {
386
+ this.drawing = false;
387
+ this.imageMarkerMode.setMode('edit');
388
+ this.requestUpdate();
389
+ }
423
390
  }
424
- this.currentMode = mode;
425
- this.currentShapeType = shapeType;
426
391
  }
427
392
  setSelectedShape(shape) {
428
393
  this.selectedShape = shape;
@@ -459,8 +424,7 @@ let OxImageMarker = class OxImageMarker extends BaseImageMarker {
459
424
  y="${shape.y}"
460
425
  fill="${commonAttrs.stroke}"
461
426
  style="cursor: ${commonAttrs.cursor}; font-size: 16px;"
462
- @mousedown="${(e) => this.onMouseDownOnShape(e, shape)}"
463
- @touchstart="${(e) => this.onMouseDownOnShape(e, shape)}"
427
+ @pointerdown="${(e) => this.onPointerDownOnShape(e, shape)}"
464
428
  @dblclick="${(e) => this.onTextDoubleClick(e, shape)}"
465
429
  >${shape.text}</text>
466
430
  `;
@@ -475,8 +439,7 @@ let OxImageMarker = class OxImageMarker extends BaseImageMarker {
475
439
  stroke-width="${commonAttrs['stroke-width']}"
476
440
  fill="${commonAttrs.fill}"
477
441
  style="cursor: ${commonAttrs.cursor}"
478
- @mousedown="${(e) => this.onMouseDownOnShape(e, shape)}"
479
- @touchstart="${(e) => this.onMouseDownOnShape(e, shape)}"
442
+ @pointerdown="${(e) => this.onPointerDownOnShape(e, shape)}"
480
443
  ></rect>
481
444
  ${shape.selected ? this.renderResizeHandles(shape) : ''}
482
445
  `;
@@ -490,8 +453,7 @@ let OxImageMarker = class OxImageMarker extends BaseImageMarker {
490
453
  stroke-width="${commonAttrs['stroke-width']}"
491
454
  fill="${commonAttrs.fill}"
492
455
  style="cursor: ${commonAttrs.cursor}"
493
- @mousedown="${(e) => this.onMouseDownOnShape(e, shape)}"
494
- @touchstart="${(e) => this.onMouseDownOnShape(e, shape)}"
456
+ @pointerdown="${(e) => this.onPointerDownOnShape(e, shape)}"
495
457
  ></circle>
496
458
  ${shape.selected ? this.renderResizeHandles(shape) : ''}
497
459
  `;
@@ -503,8 +465,7 @@ let OxImageMarker = class OxImageMarker extends BaseImageMarker {
503
465
  stroke-width="${commonAttrs['stroke-width']}"
504
466
  fill="${commonAttrs.fill}"
505
467
  style="cursor: ${commonAttrs.cursor}"
506
- @mousedown="${(e) => this.onMouseDownOnShape(e, shape)}"
507
- @touchstart="${(e) => this.onMouseDownOnShape(e, shape)}"
468
+ @pointerdown="${(e) => this.onPointerDownOnShape(e, shape)}"
508
469
  ></polygon>
509
470
  ${shape.selected ? this.renderResizeHandles(shape) : ''}
510
471
  `;
@@ -528,8 +489,7 @@ let OxImageMarker = class OxImageMarker extends BaseImageMarker {
528
489
  stroke-width="25"
529
490
  stroke-opacity="0"
530
491
  pointer-events="stroke"
531
- @mousedown="${(e) => this.onMouseDownOnShape(e, shape)}"
532
- @touchstart="${(e) => this.onMouseDownOnShape(e, shape)}"
492
+ @pointerdown="${(e) => this.onPointerDownOnShape(e, shape)}"
533
493
  @mouseenter="${() => (this.svg.style.cursor = 'pointer')}"
534
494
  @mouseleave="${() => (this.svg.style.cursor = 'default')}"
535
495
  ></line>
@@ -561,10 +521,10 @@ let OxImageMarker = class OxImageMarker extends BaseImageMarker {
561
521
  stroke-width="25"
562
522
  stroke-opacity="0"
563
523
  pointer-events="stroke"
564
- @mousedown="${(e) => this.onMouseDownOnShape(e, shape)}"
565
- @touchstart="${(e) => this.onMouseDownOnShape(e, shape)}"
524
+ @mousedown="${(e) => this.onPointerDownOnShape(e, shape)}"
566
525
  @mouseenter="${() => (this.svg.style.cursor = 'pointer')}"
567
526
  @mouseleave="${() => (this.svg.style.cursor = 'default')}"
527
+ @pointerdown="${(e) => this.currentMode == 'edit' && e.stopPropagation()}"
568
528
  ></line>
569
529
  ${shape.selected ? this.renderResizeHandles(shape) : ''}
570
530
  `;
@@ -572,13 +532,14 @@ let OxImageMarker = class OxImageMarker extends BaseImageMarker {
572
532
  return nothing;
573
533
  }
574
534
  }
575
- onMouseDownOnShape(event, shape) {
535
+ onPointerDownOnShape(event, shape) {
576
536
  if (this.currentMode !== 'edit') {
577
537
  return;
578
538
  }
579
- if ('touches' in event && event.touches.length > 1) {
580
- return;
581
- }
539
+ // TODO check if single touch ... (멀티터치를 제외해야한다).
540
+ // if ('touches' in event && event.touches.length > 1) {
541
+ // return
542
+ // }
582
543
  event.stopPropagation();
583
544
  event.preventDefault();
584
545
  this.shapes = this.shapes.map(s => ({
@@ -590,15 +551,8 @@ let OxImageMarker = class OxImageMarker extends BaseImageMarker {
590
551
  const { x, y } = this.getSvgPoint(event);
591
552
  this.offsetX = x;
592
553
  this.offsetY = y;
593
- const svg = this.svg;
594
- if ('touches' in event) {
595
- svg.addEventListener('touchmove', this.onMouseMoveOnShape);
596
- svg.addEventListener('touchend', this.onMouseUpOnShape);
597
- }
598
- else {
599
- svg.addEventListener('mousemove', this.onMouseMoveOnShape);
600
- svg.addEventListener('mouseup', this.onMouseUpOnShape);
601
- }
554
+ document.addEventListener('pointermove', this.onPointerMoveOnShape);
555
+ document.addEventListener('pointerup', this.onPointerUpOnShape);
602
556
  }
603
557
  renderResizeHandles(shape) {
604
558
  let handles = [];
@@ -647,28 +601,20 @@ let OxImageMarker = class OxImageMarker extends BaseImageMarker {
647
601
  stroke="black"
648
602
  class="resize-handle"
649
603
  style="cursor: ${handle.cursor};"
650
- @mousedown="${(e) => this.onResizeHandleMouseDown(e, shape, handle)}"
651
- @touchstart="${(e) => this.onResizeHandleMouseDown(e, shape, handle)}"
604
+ @pointerdown="${(e) => this.onResizeHandlePointerDown(e, shape, handle)}"
652
605
  ></rect>
653
606
  </g>
654
607
  `)}
655
608
  `;
656
609
  }
657
- onResizeHandleMouseDown(event, shape, handle) {
610
+ onResizeHandlePointerDown(event, shape, handle) {
658
611
  event.stopPropagation();
659
612
  event.preventDefault();
660
613
  this.setSelectedShape(shape);
661
614
  this.offsetX = handle.x;
662
615
  this.offsetY = handle.y;
663
- const svg = this.svg;
664
- if ('touches' in event) {
665
- svg.addEventListener('touchmove', this.onResizeHandleMouseMove);
666
- svg.addEventListener('touchend', this.onResizeHandleMouseUp);
667
- }
668
- else {
669
- svg.addEventListener('mousemove', this.onResizeHandleMouseMove);
670
- svg.addEventListener('mouseup', this.onResizeHandleMouseUp);
671
- }
616
+ document.addEventListener('pointermove', this.onResizeHandlePointerMove);
617
+ document.addEventListener('pointerup', this.onResizeHandlePointerUp);
672
618
  }
673
619
  onTextDoubleClick(event, shape) {
674
620
  event.stopPropagation();
@@ -707,7 +653,8 @@ let OxImageMarker = class OxImageMarker extends BaseImageMarker {
707
653
  textElement.style.visibility = 'hidden';
708
654
  document.body.appendChild(input);
709
655
  this.adjustInputWidth(input);
710
- requestAnimationFrame(() => input.focus());
656
+ /* iOS에서는 딜레이를 주었을 때 focus()를 받지 못하는 현상이 있으므로, delay 없이 바로 focus를 설정함. */
657
+ input.focus();
711
658
  input.addEventListener('input', () => {
712
659
  this.adjustInputWidth(input);
713
660
  });