@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.
- package/CHANGELOG.md +10 -0
- package/dist/src/base-image-marker.d.ts +19 -26
- package/dist/src/base-image-marker.js +93 -141
- package/dist/src/base-image-marker.js.map +1 -1
- package/dist/src/default-image-provider.d.ts +4 -0
- package/dist/src/default-image-provider.js +6 -0
- package/dist/src/default-image-provider.js.map +1 -0
- package/dist/src/ox-image-marker-view.js +4 -4
- package/dist/src/ox-image-marker-view.js.map +1 -1
- package/dist/src/ox-image-marker.d.ts +11 -11
- package/dist/src/ox-image-marker.js +173 -226
- package/dist/src/ox-image-marker.js.map +1 -1
- package/dist/src/types.d.ts +10 -0
- package/dist/src/types.js.map +1 -1
- package/dist/stories/test-image-provider.d.ts +7 -0
- package/dist/stories/test-image-provider.js +41 -0
- package/dist/stories/test-image-provider.js.map +1 -0
- package/dist/stories/test-image-provider.stories.d.ts +33 -0
- package/dist/stories/test-image-provider.stories.js +119 -0
- package/dist/stories/test-image-provider.stories.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +15 -14
- package/src/base-image-marker.ts +105 -160
- package/src/default-image-provider.ts +7 -0
- package/src/ox-image-marker-view.ts +4 -4
- package/src/ox-image-marker.ts +187 -235
- package/src/types.ts +12 -0
- package/stories/test-image-provider.stories.ts +135 -0
- package/stories/test-image-provider.ts +49 -0
package/src/ox-image-marker.ts
CHANGED
|
@@ -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
|
|
51
|
-
private
|
|
52
|
-
private
|
|
53
|
-
private
|
|
54
|
-
private
|
|
55
|
-
private
|
|
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
|
-
@
|
|
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.
|
|
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.
|
|
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
|
-
|
|
110
|
-
|
|
113
|
+
private setMode(mode: ImageMarkerMode, shapeType: ShapeType) {
|
|
114
|
+
if (mode == 'view') {
|
|
115
|
+
this.setSelectedShape(null)
|
|
116
|
+
}
|
|
111
117
|
|
|
112
|
-
this.
|
|
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.
|
|
134
|
+
this.drawShape(e)
|
|
116
135
|
} else {
|
|
117
|
-
this.
|
|
118
|
-
}
|
|
119
|
-
}
|
|
136
|
+
const [vbX, vbY, vbWidth, vbHeight] = this.viewBox.split(' ').map(Number)
|
|
120
137
|
|
|
121
|
-
|
|
122
|
-
|
|
138
|
+
const scaleX = vbWidth / this.svg.clientWidth
|
|
139
|
+
const scaleY = vbHeight / this.svg.clientHeight
|
|
140
|
+
const scale = Math.max(scaleX, scaleY)
|
|
123
141
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
142
|
+
const newVbX = vbX - deltaX * scale
|
|
143
|
+
const newVbY = vbY - deltaY * scale
|
|
127
144
|
|
|
128
|
-
|
|
129
|
-
this.setSelectedShape(null)
|
|
145
|
+
this.viewBox = `${newVbX} ${newVbY} ${vbWidth} ${vbHeight}`
|
|
130
146
|
|
|
131
|
-
|
|
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
|
-
|
|
142
|
-
|
|
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
|
-
|
|
154
|
-
|
|
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
|
-
|
|
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
|
|
172
|
-
|
|
173
|
-
|
|
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
|
-
|
|
183
|
-
|
|
175
|
+
this.startX = x
|
|
176
|
+
this.startY = y
|
|
177
|
+
this.setSelectedShape(null)
|
|
184
178
|
|
|
185
|
-
|
|
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
|
-
|
|
184
|
+
if (!this.selectedShape && distance >= minDistance) {
|
|
185
|
+
let newShape: Shape
|
|
188
186
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
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
|
-
|
|
255
|
-
|
|
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.
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
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
|
-
|
|
273
|
+
this.requestUpdate()
|
|
274
|
+
}
|
|
289
275
|
|
|
290
|
-
|
|
276
|
+
if (end) {
|
|
277
|
+
this.drawing = false
|
|
278
|
+
this.imageMarkerMode.setMode('edit')
|
|
291
279
|
|
|
292
|
-
|
|
293
|
-
|
|
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 |
|
|
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
|
-
@
|
|
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
|
-
@
|
|
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
|
-
@
|
|
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
|
-
@
|
|
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
|
-
@
|
|
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:
|
|
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
|
|
440
|
+
private onPointerDownOnShape(event: PointerEvent, shape: Shape) {
|
|
466
441
|
if (this.currentMode !== 'edit') {
|
|
467
442
|
return
|
|
468
443
|
}
|
|
469
444
|
|
|
470
|
-
|
|
471
|
-
|
|
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
|
-
|
|
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
|
|
470
|
+
private onPointerMoveOnShape = (event: PointerEvent) => {
|
|
502
471
|
if (!this.selectedShape) {
|
|
503
472
|
return
|
|
504
473
|
}
|
|
505
474
|
|
|
506
|
-
|
|
507
|
-
|
|
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
|
|
527
|
+
private onPointerUpOnShape = (event: PointerEvent) => {
|
|
558
528
|
if (!this.selectedShape) {
|
|
559
529
|
return
|
|
560
530
|
}
|
|
561
531
|
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
532
|
+
// TODO
|
|
533
|
+
// if ('touches' in event && event.touches.length > 1) {
|
|
534
|
+
// return
|
|
535
|
+
// }
|
|
565
536
|
|
|
566
537
|
event.preventDefault()
|
|
567
538
|
|
|
568
|
-
|
|
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
|
-
@
|
|
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
|
|
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
|
-
|
|
649
|
-
|
|
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
|
|
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
|
|
693
|
+
private onResizeHandlePointerUp = (event: PointerEvent) => {
|
|
737
694
|
event.preventDefault()
|
|
738
695
|
|
|
739
|
-
|
|
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
|
-
|
|
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
|
+
}
|