@operato/image-marker 8.0.0-alpha.33 → 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.
@@ -5,6 +5,8 @@ import { query, state, customElement } from 'lit/decorators.js'
5
5
  import { ifDefined } from 'lit/directives/if-defined.js'
6
6
  import { keyed } from 'lit/directives/keyed.js'
7
7
 
8
+ import { GestureEventDrag, GestureEventPinch } from '@operato/utils/gesture-helper.js'
9
+
8
10
  import { ImageMarkerMode, Shape, ShapeType } from './types'
9
11
  import { BaseImageMarker } from './base-image-marker'
10
12
  import { OxImageMarkerMode } from './ox-image-marker-mode'
@@ -47,12 +49,13 @@ export class OxImageMarker extends BaseImageMarker {
47
49
 
48
50
  @query('ox-image-marker-mode') imageMarkerMode!: OxImageMarkerMode
49
51
 
50
- private drawing = false
51
- private startX = 0
52
- private startY = 0
53
- private offsetX = 0
54
- private offsetY = 0
55
- private shapeMoveThreshold = 10
52
+ // private drawingStarted: boolean = false
53
+ private drawing: boolean = false
54
+ private startX: number = 0
55
+ private startY: number = 0
56
+ private offsetX: number = 0
57
+ private offsetY: number = 0
58
+ private shapeMoveThreshold: number = 10
56
59
  private shapeMoveInitiated = false
57
60
 
58
61
  connectedCallback() {
@@ -72,14 +75,15 @@ export class OxImageMarker extends BaseImageMarker {
72
75
  return html`
73
76
  <div class="container">
74
77
  <svg
75
- @mousedown=${this.onSvgMouseDown}
76
- @touchstart=${this.onSvgTouchStart}
77
78
  @wheel=${this.onSvgWheel}
78
- @dblclick=${this.onSvgDoubleClick}
79
+ @doubletap=${this.onSvgDoubleTap}
80
+ @pinch=${this.onSvgPinch}
81
+ @drag=${this.onSvgDrag}
82
+ @click=${this.onSvgClick}
79
83
  viewBox=${this.viewBox}
80
84
  ?drawing=${this.currentMode == 'shape'}
81
85
  >
82
- <image href=${this.imageUrl} @load=${this.onImageLoad}></image>
86
+ <image href=${this.currentImage} @load=${this.onImageLoad}></image>
83
87
  ${this.shapes.map(shape => keyed(shape.id, this.renderShape(shape)))}
84
88
  </svg>
85
89
  </div>
@@ -91,7 +95,7 @@ export class OxImageMarker extends BaseImageMarker {
91
95
  const { mode, shapeType } = e.detail
92
96
  this.setMode(mode, shapeType)
93
97
  }}
94
- @tap-fit-size=${() => this.resetZoom()}
98
+ @tap-fit-size=${() => this.reset()}
95
99
  @tap-delete=${() => this.deleteSelectedShape()}
96
100
  @tap-save=${() => {
97
101
  this.dispatchEvent(
@@ -106,42 +110,48 @@ export class OxImageMarker extends BaseImageMarker {
106
110
  `
107
111
  }
108
112
 
109
- protected onSvgMouseDown(event: MouseEvent) {
110
- event.preventDefault()
113
+ private setMode(mode: ImageMarkerMode, shapeType: ShapeType) {
114
+ if (mode == 'view') {
115
+ this.setSelectedShape(null)
116
+ }
111
117
 
112
- this.setSelectedShape(null)
118
+ this.currentMode = mode
119
+ this.currentShapeType = shapeType
120
+ }
121
+
122
+ protected onSvgPinch(e: GestureEventPinch) {
123
+ e.stopPropagation()
124
+
125
+ const { scale, centerX, centerY } = e.detail
126
+
127
+ this.zoom(scale, centerX, centerY)
128
+ }
129
+
130
+ protected onSvgDrag(e: GestureEventDrag) {
131
+ const { deltaX, deltaY, end } = e.detail
113
132
 
114
133
  if (this.currentMode === 'shape') {
115
- this.startDrawing(event)
134
+ this.drawShape(e)
116
135
  } else {
117
- this.startPanning(event)
118
- }
119
- }
136
+ const [vbX, vbY, vbWidth, vbHeight] = this.viewBox.split(' ').map(Number)
120
137
 
121
- protected onSvgTouchStart = (event: TouchEvent) => {
122
- event.preventDefault()
138
+ const scaleX = vbWidth / this.svg.clientWidth
139
+ const scaleY = vbHeight / this.svg.clientHeight
140
+ const scale = Math.max(scaleX, scaleY)
123
141
 
124
- if (this.isDoubleTouch(event)) {
125
- this.resetZoom()
126
- }
142
+ const newVbX = vbX - deltaX * scale
143
+ const newVbY = vbY - deltaY * scale
127
144
 
128
- if (event.touches.length === 1) {
129
- this.setSelectedShape(null)
145
+ this.viewBox = `${newVbX} ${newVbY} ${vbWidth} ${vbHeight}`
130
146
 
131
- if (this.currentMode === 'shape') {
132
- this.startDrawing(event)
133
- } else {
134
- this.startPanning(event)
135
- }
136
- } else if (event.touches.length === 2) {
137
- this.startPinchZooming(event)
147
+ this.debouncedUpdateImage()
138
148
  }
139
149
  }
140
150
 
141
- private startDrawing(event: MouseEvent | TouchEvent) {
142
- const { x, y } = this.getSvgPoint(event)
151
+ protected onSvgClick(e: MouseEvent) {
152
+ if (this.currentMode === 'shape' && this.currentShapeType === 'text') {
153
+ const { x, y } = this.getSvgPoint(e)
143
154
 
144
- if (this.currentShapeType === 'text') {
145
155
  const textElement = document.createElementNS('http://www.w3.org/2000/svg', 'text')
146
156
 
147
157
  textElement.setAttribute('x', x.toString())
@@ -150,156 +160,126 @@ export class OxImageMarker extends BaseImageMarker {
150
160
 
151
161
  this.svg.appendChild(textElement)
152
162
 
153
- requestAnimationFrame(() => {
154
- this.showTextInput(textElement, null, { x, y })
155
- })
156
- } else {
157
- this.startX = x
158
- this.startY = y
159
- this.drawing = true
160
- }
163
+ /* 강제 지연은 iOS에서 input focus를 받지 못하는 현상이 있다. */
164
+ this.showTextInput(textElement, null, { x, y })
161
165
 
162
- if ('touches' in event) {
163
- this.svg.addEventListener('touchmove', this.onMouseMoveForDrawing)
164
- this.svg.addEventListener('touchend', this.onMouseUpForDrawing)
165
- } else {
166
- this.svg.addEventListener('mousemove', this.onMouseMoveForDrawing)
167
- this.svg.addEventListener('mouseup', this.onMouseUpForDrawing)
166
+ this.imageMarkerMode.setMode('edit')
168
167
  }
169
168
  }
170
169
 
171
- private setMode(mode: ImageMarkerMode, shapeType: ShapeType) {
172
- if (mode == 'view') {
173
- this.setSelectedShape(null)
174
- }
175
-
176
- this.currentMode = mode
177
- this.currentShapeType = shapeType
178
- }
170
+ private drawShape(e: GestureEventDrag) {
171
+ const { end } = e.detail
172
+ const { x, y } = this.getSvgPoint(e)
179
173
 
180
- private onMouseMoveForDrawing = (event: MouseEvent | TouchEvent) => {
181
174
  if (!this.drawing) {
182
- return
183
- }
175
+ this.startX = x
176
+ this.startY = y
177
+ this.setSelectedShape(null)
184
178
 
185
- event.preventDefault()
179
+ this.drawing = true
180
+ } else if (this.drawing) {
181
+ const minDistance = 5
182
+ const distance = Math.sqrt(Math.pow(x - this.startX, 2) + Math.pow(y - this.startY, 2))
186
183
 
187
- const { x, y } = this.getSvgPoint(event)
184
+ if (!this.selectedShape && distance >= minDistance) {
185
+ let newShape: Shape
188
186
 
189
- const minDistance = 5
190
- const distance = Math.sqrt(Math.pow(x - this.startX, 2) + Math.pow(y - this.startY, 2))
191
-
192
- if (!this.selectedShape && distance >= minDistance) {
193
- let newShape: Shape
194
-
195
- switch (this.currentShapeType) {
196
- case 'rectangle':
197
- newShape = {
198
- id: Date.now().toString(),
199
- type: 'rectangle',
200
- x: this.startX,
201
- y: this.startY,
202
- width: 0,
203
- height: 0
204
- }
205
- break
206
- case 'circle':
207
- newShape = {
208
- id: Date.now().toString(),
209
- type: 'circle',
210
- cx: this.startX,
211
- cy: this.startY,
212
- r: 0
213
- }
214
- break
215
- case 'triangle':
216
- newShape = {
217
- id: Date.now().toString(),
218
- type: 'triangle',
219
- x1: this.startX,
220
- y1: this.startY,
221
- width: 0,
222
- height: 0,
223
- points: `${this.startX},${this.startY} ${this.startX},${this.startY} ${this.startX},${this.startY}`
224
- }
225
- break
226
- case 'line':
227
- newShape = {
228
- id: Date.now().toString(),
229
- type: 'line',
230
- x1: this.startX,
231
- y1: this.startY,
232
- width: 0,
233
- height: 0
234
- }
235
- break
236
- case 'arrow':
237
- newShape = {
238
- id: Date.now().toString(),
239
- type: 'arrow',
240
- x1: this.startX,
241
- y1: this.startY,
242
- width: 0,
243
- height: 0
244
- }
245
- break
246
- default:
247
- return
248
- }
249
-
250
- this.shapes = [...this.shapes, newShape]
251
- this.setSelectedShape(newShape)
252
- }
187
+ switch (this.currentShapeType) {
188
+ case 'rectangle':
189
+ newShape = {
190
+ id: Date.now().toString(),
191
+ type: 'rectangle',
192
+ x: this.startX,
193
+ y: this.startY,
194
+ width: 0,
195
+ height: 0
196
+ }
197
+ break
198
+ case 'circle':
199
+ newShape = {
200
+ id: Date.now().toString(),
201
+ type: 'circle',
202
+ cx: this.startX,
203
+ cy: this.startY,
204
+ r: 0
205
+ }
206
+ break
207
+ case 'triangle':
208
+ newShape = {
209
+ id: Date.now().toString(),
210
+ type: 'triangle',
211
+ x1: this.startX,
212
+ y1: this.startY,
213
+ width: 0,
214
+ height: 0,
215
+ points: `${this.startX},${this.startY} ${this.startX},${this.startY} ${this.startX},${this.startY}`
216
+ }
217
+ break
218
+ case 'line':
219
+ newShape = {
220
+ id: Date.now().toString(),
221
+ type: 'line',
222
+ x1: this.startX,
223
+ y1: this.startY,
224
+ width: 0,
225
+ height: 0
226
+ }
227
+ break
228
+ case 'arrow':
229
+ newShape = {
230
+ id: Date.now().toString(),
231
+ type: 'arrow',
232
+ x1: this.startX,
233
+ y1: this.startY,
234
+ width: 0,
235
+ height: 0
236
+ }
237
+ break
238
+ default:
239
+ return
240
+ }
253
241
 
254
- if (this.selectedShape) {
255
- switch (this.selectedShape.type) {
256
- case 'rectangle':
257
- this.selectedShape.x = Math.min(x, this.startX)
258
- this.selectedShape.y = Math.min(y, this.startY)
259
- this.selectedShape.width = Math.abs(x - this.startX)
260
- this.selectedShape.height = Math.abs(y - this.startY)
261
- break
262
- case 'circle':
263
- const dx = x - this.selectedShape.cx!
264
- const dy = y - this.selectedShape.cy!
265
- this.selectedShape.r = Math.sqrt(dx * dx + dy * dy)
266
- break
267
- case 'triangle':
268
- const x2 = x
269
- const y2 = y
270
- const x3 = this.startX - (x - this.startX)
271
- const y3 = y
272
- this.selectedShape.points = `${this.startX},${this.startY} ${x2},${y2} ${x3},${y3}`
273
- break
274
- case 'line':
275
- case 'arrow':
276
- this.selectedShape.x2 = x
277
- this.selectedShape.y2 = y
278
- break
242
+ this.shapes = [...this.shapes, newShape]
243
+ this.setSelectedShape(newShape)
279
244
  }
280
245
 
281
- this.requestUpdate()
282
- }
283
- }
284
-
285
- private onMouseUpForDrawing = (event: MouseEvent | TouchEvent) => {
286
- event.preventDefault()
246
+ if (this.selectedShape) {
247
+ switch (this.selectedShape.type) {
248
+ case 'rectangle':
249
+ this.selectedShape.x = Math.min(x, this.startX)
250
+ this.selectedShape.y = Math.min(y, this.startY)
251
+ this.selectedShape.width = Math.abs(x - this.startX)
252
+ this.selectedShape.height = Math.abs(y - this.startY)
253
+ break
254
+ case 'circle':
255
+ const dx = x - this.selectedShape.cx!
256
+ const dy = y - this.selectedShape.cy!
257
+ this.selectedShape.r = Math.sqrt(dx * dx + dy * dy)
258
+ break
259
+ case 'triangle':
260
+ const x2 = x
261
+ const y2 = y
262
+ const x3 = this.startX - (x - this.startX)
263
+ const y3 = y
264
+ this.selectedShape.points = `${this.startX},${this.startY} ${x2},${y2} ${x3},${y3}`
265
+ break
266
+ case 'line':
267
+ case 'arrow':
268
+ this.selectedShape.x2 = x
269
+ this.selectedShape.y2 = y
270
+ break
271
+ }
287
272
 
288
- this.drawing = false
273
+ this.requestUpdate()
274
+ }
289
275
 
290
- const svg = this.svg as SVGSVGElement
276
+ if (end) {
277
+ this.drawing = false
278
+ this.imageMarkerMode.setMode('edit')
291
279
 
292
- if ('touches' in event) {
293
- svg.removeEventListener('touchmove', this.onMouseMoveForDrawing)
294
- svg.removeEventListener('touchend', this.onMouseUpForDrawing)
295
- } else {
296
- svg.removeEventListener('mousemove', this.onMouseMoveForDrawing)
297
- svg.removeEventListener('mouseup', this.onMouseUpForDrawing)
280
+ this.requestUpdate()
281
+ }
298
282
  }
299
-
300
- this.imageMarkerMode.setMode('edit')
301
-
302
- this.requestUpdate()
303
283
  }
304
284
 
305
285
  private setSelectedShape(shape: Shape | null) {
@@ -314,7 +294,7 @@ export class OxImageMarker extends BaseImageMarker {
314
294
  }
315
295
  }
316
296
 
317
- private getSvgPoint(event: MouseEvent | TouchEvent) {
297
+ private getSvgPoint(event: MouseEvent | PointerEvent | GestureEventDrag) {
318
298
  const { x, y } = this.getPointFromEvent(event)
319
299
 
320
300
  const svg = this.svg
@@ -342,8 +322,7 @@ export class OxImageMarker extends BaseImageMarker {
342
322
  y="${shape.y!}"
343
323
  fill="${commonAttrs.stroke}"
344
324
  style="cursor: ${commonAttrs.cursor}; font-size: 16px;"
345
- @mousedown="${(e: MouseEvent) => this.onMouseDownOnShape(e, shape)}"
346
- @touchstart="${(e: TouchEvent) => this.onMouseDownOnShape(e, shape)}"
325
+ @pointerdown="${(e: PointerEvent) => this.onPointerDownOnShape(e, shape)}"
347
326
  @dblclick="${(e: MouseEvent) => this.onTextDoubleClick(e, shape)}"
348
327
  >${shape.text}</text>
349
328
  `
@@ -359,8 +338,7 @@ export class OxImageMarker extends BaseImageMarker {
359
338
  stroke-width="${commonAttrs['stroke-width']}"
360
339
  fill="${commonAttrs.fill}"
361
340
  style="cursor: ${commonAttrs.cursor}"
362
- @mousedown="${(e: MouseEvent) => this.onMouseDownOnShape(e, shape)}"
363
- @touchstart="${(e: TouchEvent) => this.onMouseDownOnShape(e, shape)}"
341
+ @pointerdown="${(e: PointerEvent) => this.onPointerDownOnShape(e, shape)}"
364
342
  ></rect>
365
343
  ${shape.selected ? this.renderResizeHandles(shape) : ''}
366
344
  `
@@ -375,8 +353,7 @@ export class OxImageMarker extends BaseImageMarker {
375
353
  stroke-width="${commonAttrs['stroke-width']}"
376
354
  fill="${commonAttrs.fill}"
377
355
  style="cursor: ${commonAttrs.cursor}"
378
- @mousedown="${(e: MouseEvent) => this.onMouseDownOnShape(e, shape)}"
379
- @touchstart="${(e: TouchEvent) => this.onMouseDownOnShape(e, shape)}"
356
+ @pointerdown="${(e: PointerEvent) => this.onPointerDownOnShape(e, shape)}"
380
357
  ></circle>
381
358
  ${shape.selected ? this.renderResizeHandles(shape) : ''}
382
359
  `
@@ -389,8 +366,7 @@ export class OxImageMarker extends BaseImageMarker {
389
366
  stroke-width="${commonAttrs['stroke-width']}"
390
367
  fill="${commonAttrs.fill}"
391
368
  style="cursor: ${commonAttrs.cursor}"
392
- @mousedown="${(e: MouseEvent) => this.onMouseDownOnShape(e, shape)}"
393
- @touchstart="${(e: TouchEvent) => this.onMouseDownOnShape(e, shape)}"
369
+ @pointerdown="${(e: PointerEvent) => this.onPointerDownOnShape(e, shape)}"
394
370
  ></polygon>
395
371
  ${shape.selected ? this.renderResizeHandles(shape) : ''}
396
372
  `
@@ -415,8 +391,7 @@ export class OxImageMarker extends BaseImageMarker {
415
391
  stroke-width="25"
416
392
  stroke-opacity="0"
417
393
  pointer-events="stroke"
418
- @mousedown="${(e: MouseEvent) => this.onMouseDownOnShape(e, shape)}"
419
- @touchstart="${(e: TouchEvent) => this.onMouseDownOnShape(e, shape)}"
394
+ @pointerdown="${(e: PointerEvent) => this.onPointerDownOnShape(e, shape)}"
420
395
  @mouseenter="${() => (this.svg.style.cursor = 'pointer')}"
421
396
  @mouseleave="${() => (this.svg.style.cursor = 'default')}"
422
397
  ></line>
@@ -449,10 +424,10 @@ export class OxImageMarker extends BaseImageMarker {
449
424
  stroke-width="25"
450
425
  stroke-opacity="0"
451
426
  pointer-events="stroke"
452
- @mousedown="${(e: MouseEvent) => this.onMouseDownOnShape(e, shape)}"
453
- @touchstart="${(e: TouchEvent) => this.onMouseDownOnShape(e, shape)}"
427
+ @mousedown="${(e: PointerEvent) => this.onPointerDownOnShape(e, shape)}"
454
428
  @mouseenter="${() => (this.svg.style.cursor = 'pointer')}"
455
429
  @mouseleave="${() => (this.svg.style.cursor = 'default')}"
430
+ @pointerdown="${(e: PointerEvent) => this.currentMode == 'edit' && e.stopPropagation()}"
456
431
  ></line>
457
432
  ${shape.selected ? this.renderResizeHandles(shape) : ''}
458
433
  `
@@ -462,14 +437,15 @@ export class OxImageMarker extends BaseImageMarker {
462
437
  }
463
438
  }
464
439
 
465
- private onMouseDownOnShape(event: MouseEvent | TouchEvent, shape: Shape) {
440
+ private onPointerDownOnShape(event: PointerEvent, shape: Shape) {
466
441
  if (this.currentMode !== 'edit') {
467
442
  return
468
443
  }
469
444
 
470
- if ('touches' in event && event.touches.length > 1) {
471
- return
472
- }
445
+ // TODO check if single touch ... (멀티터치를 제외해야한다).
446
+ // if ('touches' in event && event.touches.length > 1) {
447
+ // return
448
+ // }
473
449
 
474
450
  event.stopPropagation()
475
451
  event.preventDefault()
@@ -487,25 +463,19 @@ export class OxImageMarker extends BaseImageMarker {
487
463
  this.offsetX = x
488
464
  this.offsetY = y
489
465
 
490
- const svg = this.svg as SVGSVGElement
491
-
492
- if ('touches' in event) {
493
- svg.addEventListener('touchmove', this.onMouseMoveOnShape)
494
- svg.addEventListener('touchend', this.onMouseUpOnShape)
495
- } else {
496
- svg.addEventListener('mousemove', this.onMouseMoveOnShape)
497
- svg.addEventListener('mouseup', this.onMouseUpOnShape)
498
- }
466
+ document.addEventListener('pointermove', this.onPointerMoveOnShape)
467
+ document.addEventListener('pointerup', this.onPointerUpOnShape)
499
468
  }
500
469
 
501
- private onMouseMoveOnShape = (event: MouseEvent | TouchEvent) => {
470
+ private onPointerMoveOnShape = (event: PointerEvent) => {
502
471
  if (!this.selectedShape) {
503
472
  return
504
473
  }
505
474
 
506
- if ('touches' in event && event.touches.length > 1) {
507
- return
508
- }
475
+ // TODO check if single touch ... (멀티터치를 제외해야한다).
476
+ // if ('touches' in event && event.touches.length > 1) {
477
+ // return
478
+ // }
509
479
 
510
480
  event.preventDefault()
511
481
 
@@ -554,26 +524,20 @@ export class OxImageMarker extends BaseImageMarker {
554
524
  this.requestUpdate()
555
525
  }
556
526
 
557
- private onMouseUpOnShape = (event: MouseEvent | TouchEvent) => {
527
+ private onPointerUpOnShape = (event: PointerEvent) => {
558
528
  if (!this.selectedShape) {
559
529
  return
560
530
  }
561
531
 
562
- if ('touches' in event && event.touches.length > 1) {
563
- return
564
- }
532
+ // TODO
533
+ // if ('touches' in event && event.touches.length > 1) {
534
+ // return
535
+ // }
565
536
 
566
537
  event.preventDefault()
567
538
 
568
- const svg = this.svg as SVGSVGElement
569
-
570
- if ('touches' in event) {
571
- svg.removeEventListener('touchmove', this.onMouseMoveOnShape)
572
- svg.removeEventListener('touchend', this.onMouseUpOnShape)
573
- } else {
574
- svg.removeEventListener('mousemove', this.onMouseMoveOnShape)
575
- svg.removeEventListener('mouseup', this.onMouseUpOnShape)
576
- }
539
+ document.removeEventListener('pointermove', this.onPointerMoveOnShape)
540
+ document.removeEventListener('pointerup', this.onPointerUpOnShape)
577
541
  }
578
542
 
579
543
  private renderResizeHandles(shape: Shape) {
@@ -626,8 +590,7 @@ export class OxImageMarker extends BaseImageMarker {
626
590
  stroke="black"
627
591
  class="resize-handle"
628
592
  style="cursor: ${handle.cursor};"
629
- @mousedown="${(e: MouseEvent) => this.onResizeHandleMouseDown(e, shape, handle)}"
630
- @touchstart="${(e: TouchEvent) => this.onResizeHandleMouseDown(e, shape, handle)}"
593
+ @pointerdown="${(e: PointerEvent) => this.onResizeHandlePointerDown(e, shape, handle)}"
631
594
  ></rect>
632
595
  </g>
633
596
  `
@@ -635,7 +598,7 @@ export class OxImageMarker extends BaseImageMarker {
635
598
  `
636
599
  }
637
600
 
638
- private onResizeHandleMouseDown(event: MouseEvent | TouchEvent, shape: Shape, handle: any) {
601
+ private onResizeHandlePointerDown(event: PointerEvent, shape: Shape, handle: any) {
639
602
  event.stopPropagation()
640
603
  event.preventDefault()
641
604
 
@@ -643,18 +606,12 @@ export class OxImageMarker extends BaseImageMarker {
643
606
 
644
607
  this.offsetX = handle.x
645
608
  this.offsetY = handle.y
646
- const svg = this.svg as SVGSVGElement
647
609
 
648
- if ('touches' in event) {
649
- svg.addEventListener('touchmove', this.onResizeHandleMouseMove)
650
- svg.addEventListener('touchend', this.onResizeHandleMouseUp)
651
- } else {
652
- svg.addEventListener('mousemove', this.onResizeHandleMouseMove)
653
- svg.addEventListener('mouseup', this.onResizeHandleMouseUp)
654
- }
610
+ document.addEventListener('pointermove', this.onResizeHandlePointerMove)
611
+ document.addEventListener('pointerup', this.onResizeHandlePointerUp)
655
612
  }
656
613
 
657
- private onResizeHandleMouseMove = (event: MouseEvent | TouchEvent) => {
614
+ private onResizeHandlePointerMove = (event: PointerEvent) => {
658
615
  if (!this.selectedShape) {
659
616
  return
660
617
  }
@@ -733,18 +690,11 @@ export class OxImageMarker extends BaseImageMarker {
733
690
  this.requestUpdate()
734
691
  }
735
692
 
736
- private onResizeHandleMouseUp = (event: MouseEvent | TouchEvent) => {
693
+ private onResizeHandlePointerUp = (event: PointerEvent) => {
737
694
  event.preventDefault()
738
695
 
739
- const svg = this.svg as SVGSVGElement
740
-
741
- if ('touches' in event) {
742
- svg.removeEventListener('touchmove', this.onResizeHandleMouseMove)
743
- svg.removeEventListener('touchend', this.onResizeHandleMouseUp)
744
- } else {
745
- svg.removeEventListener('mousemove', this.onResizeHandleMouseMove)
746
- svg.removeEventListener('mouseup', this.onResizeHandleMouseUp)
747
- }
696
+ document.removeEventListener('pointermove', this.onResizeHandlePointerMove)
697
+ document.removeEventListener('pointerup', this.onResizeHandlePointerUp)
748
698
  }
749
699
 
750
700
  private onTextDoubleClick(event: MouseEvent, shape: Shape) {
@@ -795,7 +745,9 @@ export class OxImageMarker extends BaseImageMarker {
795
745
 
796
746
  document.body.appendChild(input)
797
747
  this.adjustInputWidth(input)
798
- requestAnimationFrame(() => input.focus())
748
+
749
+ /* iOS에서는 딜레이를 주었을 때 focus()를 받지 못하는 현상이 있으므로, delay 없이 바로 focus를 설정함. */
750
+ input.focus()
799
751
 
800
752
  input.addEventListener('input', () => {
801
753
  this.adjustInputWidth(input)
package/src/types.ts CHANGED
@@ -18,3 +18,15 @@ export interface Shape {
18
18
  selected?: boolean
19
19
  text?: string
20
20
  }
21
+
22
+ export interface ImageOptions {
23
+ id: string
24
+ scale: number
25
+ offsetX: number
26
+ offsetY: number
27
+ currentImage?: string // 기존 이미지 객체
28
+ }
29
+
30
+ export interface ImageProvider {
31
+ getImage(options: ImageOptions): Promise<string>
32
+ }