@operato/mini-map 7.1.1 → 8.0.0-alpha.29
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 +17 -0
- package/dist/src/ox-mini-map.js +12 -12
- package/dist/src/ox-mini-map.js.map +1 -1
- package/dist/src/ox-zoomable-image.d.ts +374 -14
- package/dist/src/ox-zoomable-image.js +63 -108
- package/dist/src/ox-zoomable-image.js.map +1 -1
- package/dist/stories/ox-zoomable-image.stories.d.ts +4 -0
- package/dist/stories/ox-zoomable-image.stories.js +10 -6
- package/dist/stories/ox-zoomable-image.stories.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/ox-mini-map.ts +12 -12
- package/src/ox-zoomable-image.ts +60 -101
- package/stories/ox-zoomable-image.stories.ts +11 -6
package/src/ox-mini-map.ts
CHANGED
|
@@ -44,8 +44,8 @@ class OxMiniMapComponent extends LitElement {
|
|
|
44
44
|
connectedCallback() {
|
|
45
45
|
super.connectedCallback()
|
|
46
46
|
window.addEventListener('resize', this.handleResize.bind(this))
|
|
47
|
-
this.addEventListener('
|
|
48
|
-
this.addEventListener('
|
|
47
|
+
this.addEventListener('pointermove', this.handlePointerMove.bind(this))
|
|
48
|
+
this.addEventListener('pointerup', this.handlePointerUp.bind(this))
|
|
49
49
|
if (this.target) {
|
|
50
50
|
this.observeTarget(this.target)
|
|
51
51
|
}
|
|
@@ -54,8 +54,8 @@ class OxMiniMapComponent extends LitElement {
|
|
|
54
54
|
disconnectedCallback() {
|
|
55
55
|
super.disconnectedCallback()
|
|
56
56
|
window.removeEventListener('resize', this.handleResize.bind(this))
|
|
57
|
-
window.removeEventListener('
|
|
58
|
-
window.removeEventListener('
|
|
57
|
+
window.removeEventListener('pointermove', this.handlePointerMove.bind(this))
|
|
58
|
+
window.removeEventListener('pointerup', this.handlePointerUp.bind(this))
|
|
59
59
|
if (this.observer) {
|
|
60
60
|
this.observer.disconnect()
|
|
61
61
|
this.observer = null
|
|
@@ -154,7 +154,7 @@ class OxMiniMapComponent extends LitElement {
|
|
|
154
154
|
viewport.style.transform = `scale(${scale}) translate(${left * scaleMinimap}px, ${top * scaleMinimap}px)`
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
-
|
|
157
|
+
handlePointerDown(event: MouseEvent) {
|
|
158
158
|
this.isDragging = true
|
|
159
159
|
this.startX = event.clientX
|
|
160
160
|
this.startY = event.clientY
|
|
@@ -165,13 +165,13 @@ class OxMiniMapComponent extends LitElement {
|
|
|
165
165
|
this.initialScrollTop = target.scrollTop
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
-
window.addEventListener('
|
|
169
|
-
window.addEventListener('
|
|
168
|
+
window.addEventListener('pointermove', this.handlePointerMove.bind(this))
|
|
169
|
+
window.addEventListener('pointerup', this.handlePointerUp.bind(this))
|
|
170
170
|
|
|
171
171
|
event.preventDefault()
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
-
|
|
174
|
+
handlePointerMove(event: MouseEvent) {
|
|
175
175
|
if (!this.isDragging) return
|
|
176
176
|
|
|
177
177
|
const deltaX = event.clientX - this.startX
|
|
@@ -197,17 +197,17 @@ class OxMiniMapComponent extends LitElement {
|
|
|
197
197
|
event.preventDefault()
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
-
|
|
200
|
+
handlePointerUp(event: MouseEvent) {
|
|
201
201
|
this.isDragging = false
|
|
202
|
-
window.removeEventListener('
|
|
203
|
-
window.removeEventListener('
|
|
202
|
+
window.removeEventListener('pointermove', this.handlePointerMove.bind(this))
|
|
203
|
+
window.removeEventListener('pointerup', this.handlePointerUp.bind(this))
|
|
204
204
|
event.preventDefault()
|
|
205
205
|
}
|
|
206
206
|
|
|
207
207
|
render() {
|
|
208
208
|
return html`
|
|
209
209
|
<div id="minimap">
|
|
210
|
-
<div id="viewport" @
|
|
210
|
+
<div id="viewport" @pointerdown=${this.handlePointerDown}></div>
|
|
211
211
|
</div>
|
|
212
212
|
`
|
|
213
213
|
}
|
package/src/ox-zoomable-image.ts
CHANGED
|
@@ -1,16 +1,7 @@
|
|
|
1
1
|
import { css, LitElement, PropertyValues } from 'lit'
|
|
2
2
|
import { customElement, property, state } from 'lit/decorators.js'
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
function getPoint(e: MouseEvent | TouchEvent): { x: number; y: number } | undefined {
|
|
6
|
-
if (e instanceof MouseEvent && e.button === 0) {
|
|
7
|
-
return { x: e.clientX, y: e.clientY }
|
|
8
|
-
} else if (e instanceof TouchEvent && e.touches.length === 1) {
|
|
9
|
-
const touch = e.touches[0]
|
|
10
|
-
return { x: touch.clientX, y: touch.clientY }
|
|
11
|
-
}
|
|
12
|
-
return undefined
|
|
13
|
-
}
|
|
4
|
+
import { gesture, GestureEventDoubleTap, GestureEventDrag, GestureEventPinch } from '@operato/utils/gesture-mixin.js'
|
|
14
5
|
|
|
15
6
|
/**
|
|
16
7
|
* Example usage:
|
|
@@ -34,13 +25,13 @@ function getPoint(e: MouseEvent | TouchEvent): { x: number; y: number } | undefi
|
|
|
34
25
|
* </style>
|
|
35
26
|
* </head>
|
|
36
27
|
* <body>
|
|
37
|
-
* <ox-zoomable-image src="path/to/your/image.jpg"></ox-zoomable-image>
|
|
28
|
+
* <ox-zoomable-image src="path/to/your/image.jpg" restrict-boundary></ox-zoomable-image>
|
|
38
29
|
* </body>
|
|
39
30
|
* </html>
|
|
40
31
|
*/
|
|
41
32
|
|
|
42
33
|
@customElement('ox-zoomable-image')
|
|
43
|
-
export class OxZoomableImage extends LitElement {
|
|
34
|
+
export class OxZoomableImage extends gesture(LitElement) {
|
|
44
35
|
// Define CSS styles for the component
|
|
45
36
|
static styles = css`
|
|
46
37
|
:host {
|
|
@@ -49,16 +40,20 @@ export class OxZoomableImage extends LitElement {
|
|
|
49
40
|
width: 100%;
|
|
50
41
|
height: 100%;
|
|
51
42
|
overflow: hidden;
|
|
43
|
+
|
|
44
|
+
touch-action: none;
|
|
52
45
|
}
|
|
53
46
|
|
|
54
47
|
:host::before {
|
|
55
48
|
content: '';
|
|
56
49
|
display: block;
|
|
50
|
+
|
|
57
51
|
position: absolute;
|
|
58
52
|
top: 0;
|
|
59
53
|
right: 0;
|
|
60
54
|
width: 100%;
|
|
61
55
|
height: 100%;
|
|
56
|
+
|
|
62
57
|
background-image: var(
|
|
63
58
|
--background-image,
|
|
64
59
|
linear-gradient(var(--primary-color, lightgray), var(--primary-color, lightgray))
|
|
@@ -67,56 +62,38 @@ export class OxZoomableImage extends LitElement {
|
|
|
67
62
|
background-repeat: no-repeat;
|
|
68
63
|
background-position: center;
|
|
69
64
|
background-size: contain;
|
|
65
|
+
|
|
66
|
+
touch-action: none;
|
|
70
67
|
}
|
|
71
68
|
`
|
|
72
69
|
|
|
73
70
|
@property({ type: String }) src?: string // Source URL for the image
|
|
71
|
+
@property({ type: Boolean, attribute: 'restrict-boundary' }) restrictBoundary: boolean = false // Ensure the image stays within bounds
|
|
72
|
+
|
|
74
73
|
@state() private zoom = 1 // Current zoom level
|
|
75
74
|
@state() private left = 0 // Horizontal position
|
|
76
75
|
@state() private top = 0 // Vertical position
|
|
77
|
-
@state() private minZoom = 1 // Minimum zoom level
|
|
78
76
|
@state() private imageWidth = 0
|
|
79
77
|
@state() private imageHeight = 0
|
|
80
78
|
|
|
81
|
-
private dragStart?: { x: number; y: number } // Starting point of drag
|
|
82
|
-
|
|
83
|
-
// Event listeners bound to the component
|
|
84
|
-
private readonly dragEndHandler = this.onDragEnd.bind(this) as EventListener
|
|
85
|
-
private readonly dragMoveHandler = this.onDragMove.bind(this) as EventListener
|
|
86
|
-
private readonly dragStartHandler = this.onDragStart.bind(this) as EventListener
|
|
87
|
-
private readonly wheelEventHandler = this.onWheelEvent.bind(this) as EventListener
|
|
88
|
-
private readonly resetHandler = this.onReset.bind(this) as EventListener
|
|
89
|
-
|
|
90
79
|
connectedCallback(): void {
|
|
91
80
|
super.connectedCallback()
|
|
92
81
|
|
|
93
82
|
// Register event listeners for zooming and dragging
|
|
94
|
-
this.addEventListener('
|
|
95
|
-
this.addEventListener('
|
|
96
|
-
this.addEventListener('
|
|
97
|
-
this.addEventListener('
|
|
98
|
-
|
|
99
|
-
document.addEventListener('mouseup', this.dragEndHandler)
|
|
100
|
-
document.addEventListener('touchend', this.dragEndHandler)
|
|
101
|
-
document.addEventListener('touchcancel', this.dragEndHandler)
|
|
102
|
-
document.addEventListener('mousemove', this.dragMoveHandler)
|
|
103
|
-
document.addEventListener('touchmove', this.dragMoveHandler)
|
|
83
|
+
this.addEventListener('pinch', this.pinchHandler as EventListener)
|
|
84
|
+
this.addEventListener('drag', this.dragHandler as EventListener)
|
|
85
|
+
this.addEventListener('doubletap', this.doubleTapHandler as EventListener)
|
|
86
|
+
this.addEventListener('wheel', this.wheelHandler as EventListener)
|
|
104
87
|
|
|
105
88
|
this.calculateMinZoom() // Calculate the minimum zoom level based on the image and container size
|
|
106
89
|
}
|
|
107
90
|
|
|
108
91
|
disconnectedCallback(): void {
|
|
109
92
|
// Remove event listeners when the component is disconnected
|
|
110
|
-
this.removeEventListener('
|
|
111
|
-
this.removeEventListener('
|
|
112
|
-
this.removeEventListener('
|
|
113
|
-
this.removeEventListener('
|
|
114
|
-
|
|
115
|
-
document.removeEventListener('mouseup', this.dragEndHandler)
|
|
116
|
-
document.removeEventListener('touchend', this.dragEndHandler)
|
|
117
|
-
document.removeEventListener('touchcancel', this.dragEndHandler)
|
|
118
|
-
document.removeEventListener('mousemove', this.dragMoveHandler)
|
|
119
|
-
document.removeEventListener('touchmove', this.dragMoveHandler)
|
|
93
|
+
this.removeEventListener('pinch', this.pinchHandler as EventListener)
|
|
94
|
+
this.removeEventListener('drag', this.dragHandler as EventListener)
|
|
95
|
+
this.removeEventListener('doubletap', this.doubleTapHandler as EventListener)
|
|
96
|
+
this.removeEventListener('wheel', this.wheelHandler as EventListener)
|
|
120
97
|
|
|
121
98
|
super.disconnectedCallback()
|
|
122
99
|
}
|
|
@@ -127,6 +104,7 @@ export class OxZoomableImage extends LitElement {
|
|
|
127
104
|
this.style.setProperty('--background-image', `url('${this.src}')`)
|
|
128
105
|
this.calculateMinZoom() // Recalculate the minimum zoom level when the source changes
|
|
129
106
|
}
|
|
107
|
+
|
|
130
108
|
this.updateTransform() // Apply the current zoom and position
|
|
131
109
|
}
|
|
132
110
|
|
|
@@ -138,88 +116,69 @@ export class OxZoomableImage extends LitElement {
|
|
|
138
116
|
this.imageWidth = img.width
|
|
139
117
|
this.imageHeight = img.height
|
|
140
118
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
if (aspectRatio < hostAspectRatio) {
|
|
144
|
-
this.minZoom = this.offsetHeight / img.height
|
|
145
|
-
} else {
|
|
146
|
-
this.minZoom = this.offsetWidth / img.width
|
|
147
|
-
}
|
|
119
|
+
console.log(this.imageWidth, this.imageHeight)
|
|
120
|
+
|
|
148
121
|
this.zoom = 1 // this.minZoom
|
|
149
122
|
this.left = 0 // Center the image
|
|
150
123
|
this.top = 0 // Center the image
|
|
124
|
+
|
|
151
125
|
this.updateTransform()
|
|
152
126
|
}
|
|
153
127
|
}
|
|
154
128
|
|
|
155
|
-
private
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
const newZoom = Math.max(1, this.zoom * (1 - delta / 20))
|
|
160
|
-
const zoomFactor = newZoom / this.zoom
|
|
161
|
-
|
|
162
|
-
const rect = this.getBoundingClientRect()
|
|
163
|
-
const offsetX = e.clientX - rect.left - rect.width / 2
|
|
164
|
-
const offsetY = e.clientY - rect.top - rect.height / 2
|
|
129
|
+
private pinchHandler = (e: GestureEventPinch) => {
|
|
130
|
+
const { scale, centerX, centerY } = e.detail
|
|
131
|
+
this.zoomAt(centerX, centerY, scale)
|
|
132
|
+
}
|
|
165
133
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
this.top -= offsetY * (zoomFactor - 1)
|
|
134
|
+
private zoomAt(clientX: number, clientY: number, scale: number) {
|
|
135
|
+
const { offsetLeft: left, offsetTop: top, offsetWidth: width, offsetHeight: height } = this
|
|
169
136
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
this.updateTransform() // Apply the new zoom and position
|
|
173
|
-
}
|
|
137
|
+
const centerX = left + width / 2
|
|
138
|
+
const centerY = top + height / 2
|
|
174
139
|
|
|
175
|
-
|
|
176
|
-
//
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
this.dragStart = point
|
|
180
|
-
e.stopPropagation()
|
|
181
|
-
}
|
|
182
|
-
}
|
|
140
|
+
// this.imageWidth
|
|
141
|
+
// this.imageHeight
|
|
142
|
+
const dx = ((centerX - clientX) / width) * 100 * (1 - scale)
|
|
143
|
+
const dy = ((centerY - clientY) / height) * 100 * (1 - scale)
|
|
183
144
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
if (!this.dragStart) return
|
|
145
|
+
this.left += dx
|
|
146
|
+
this.top += dy
|
|
187
147
|
|
|
188
|
-
|
|
189
|
-
|
|
148
|
+
this.zoom *= scale
|
|
149
|
+
}
|
|
190
150
|
|
|
191
|
-
|
|
151
|
+
private wheelHandler = (e: WheelEvent): void => {
|
|
152
|
+
// Handle zoom in and out based on mouse wheel events
|
|
192
153
|
e.preventDefault()
|
|
193
154
|
|
|
194
|
-
const
|
|
195
|
-
this.
|
|
196
|
-
this.top += y - this.dragStart.y
|
|
197
|
-
|
|
198
|
-
this.dragStart = point
|
|
199
|
-
this.applyBounds() // Ensure the image stays within bounds
|
|
200
|
-
this.updateTransform() // Apply the new position
|
|
155
|
+
const delta = Math.sign(e.deltaY)
|
|
156
|
+
this.zoomAt(e.clientX, e.clientY, 1 - delta / 20)
|
|
201
157
|
}
|
|
202
158
|
|
|
203
|
-
private
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
159
|
+
private dragHandler = (e: GestureEventDrag): void => {
|
|
160
|
+
const { deltaX, deltaY } = e.detail
|
|
161
|
+
|
|
162
|
+
this.top += deltaY
|
|
163
|
+
this.left += deltaX
|
|
164
|
+
|
|
165
|
+
this.updateTransform()
|
|
210
166
|
}
|
|
211
167
|
|
|
212
|
-
private
|
|
168
|
+
private doubleTapHandler = (e: GestureEventDoubleTap): void => {
|
|
213
169
|
// Reset zoom and position to initial state
|
|
214
170
|
this.zoom = 1
|
|
215
171
|
this.left = 0
|
|
216
172
|
this.top = 0
|
|
217
|
-
this.updateTransform()
|
|
218
173
|
}
|
|
219
174
|
|
|
220
175
|
private updateTransform() {
|
|
176
|
+
if (this.restrictBoundary) {
|
|
177
|
+
this.applyBounds() // Ensure the image stays within bounds
|
|
178
|
+
}
|
|
179
|
+
|
|
221
180
|
// Apply the current zoom and position to the image
|
|
222
|
-
this.style.setProperty('--image-transform', `
|
|
181
|
+
this.style.setProperty('--image-transform', `translate(${this.left}px, ${this.top}px) scale(${this.zoom})`)
|
|
223
182
|
}
|
|
224
183
|
|
|
225
184
|
private applyBounds() {
|
|
@@ -232,21 +191,21 @@ export class OxZoomableImage extends LitElement {
|
|
|
232
191
|
|
|
233
192
|
if (aspectRatio < hostAspectRatio) {
|
|
234
193
|
/* image ratio is wider than host */
|
|
235
|
-
var minX = Math.min(0, (-hostWidth * (this.zoom
|
|
194
|
+
var minX = Math.min(0, (-hostWidth * (1 - this.zoom)) / 2)
|
|
236
195
|
var maxX = -minX
|
|
237
196
|
|
|
238
|
-
var minY = (hostHeight - hostWidth * aspectRatio * this.zoom) / 2
|
|
197
|
+
var minY = (hostHeight - hostWidth * aspectRatio * this.zoom) / 2
|
|
239
198
|
minY = Math.min(minY, -minY)
|
|
240
199
|
var maxY = -minY
|
|
241
200
|
|
|
242
201
|
this.left = Math.max(minX, Math.min(this.left, maxX))
|
|
243
202
|
this.top = Math.max(minY, Math.min(this.top, maxY))
|
|
244
203
|
} else {
|
|
245
|
-
var minX = (hostWidth - (hostHeight / aspectRatio) * this.zoom) / 2
|
|
204
|
+
var minX = (hostWidth - (hostHeight / aspectRatio) * this.zoom) / 2
|
|
246
205
|
minX = Math.min(minX, -minX)
|
|
247
206
|
var maxX = -minX
|
|
248
207
|
|
|
249
|
-
var minY = Math.min(0, (-hostHeight * (this.zoom
|
|
208
|
+
var minY = Math.min(0, (-hostHeight * (1 - this.zoom)) / 2)
|
|
250
209
|
var maxY = -minY
|
|
251
210
|
|
|
252
211
|
this.left = Math.max(minX, Math.min(this.left, maxX))
|
|
@@ -7,7 +7,8 @@ export default {
|
|
|
7
7
|
argTypes: {
|
|
8
8
|
src: { control: 'text' },
|
|
9
9
|
width: { control: 'number' },
|
|
10
|
-
height: { control: 'number' }
|
|
10
|
+
height: { control: 'number' },
|
|
11
|
+
restrictBoundary: { control: 'boolean' }
|
|
11
12
|
}
|
|
12
13
|
}
|
|
13
14
|
|
|
@@ -21,9 +22,10 @@ interface OxZoomableImageProps {
|
|
|
21
22
|
src: string
|
|
22
23
|
width: number
|
|
23
24
|
height: number
|
|
25
|
+
restrictBoundary: boolean
|
|
24
26
|
}
|
|
25
27
|
|
|
26
|
-
const Template: Story<OxZoomableImageProps> = ({ src, width, height }: OxZoomableImageProps) => html`
|
|
28
|
+
const Template: Story<OxZoomableImageProps> = ({ src, width, height, restrictBoundary }: OxZoomableImageProps) => html`
|
|
27
29
|
<style>
|
|
28
30
|
ox-zoomable-image {
|
|
29
31
|
width: ${width}px;
|
|
@@ -33,26 +35,29 @@ const Template: Story<OxZoomableImageProps> = ({ src, width, height }: OxZoomabl
|
|
|
33
35
|
margin: 100px;
|
|
34
36
|
}
|
|
35
37
|
</style>
|
|
36
|
-
<ox-zoomable-image src=${src}></ox-zoomable-image>
|
|
38
|
+
<ox-zoomable-image src=${src} ?restrict-boundary=${restrictBoundary}></ox-zoomable-image>
|
|
37
39
|
`
|
|
38
40
|
|
|
39
41
|
export const Default = Template.bind({})
|
|
40
42
|
Default.args = {
|
|
41
43
|
src: 'https://via.placeholder.com/800x600.png', // 기본 이미지 URL을 실제 경로로 변경하세요.
|
|
42
44
|
width: 400,
|
|
43
|
-
height: 600
|
|
45
|
+
height: 600,
|
|
46
|
+
restrictBoundary: true
|
|
44
47
|
}
|
|
45
48
|
|
|
46
49
|
export const Wide = Template.bind({})
|
|
47
50
|
Wide.args = {
|
|
48
51
|
src: 'https://via.placeholder.com/800x600.png', // 기본 이미지 URL을 실제 경로로 변경하세요.
|
|
49
52
|
width: 600,
|
|
50
|
-
height: 300
|
|
53
|
+
height: 300,
|
|
54
|
+
restrictBoundary: true
|
|
51
55
|
}
|
|
52
56
|
|
|
53
57
|
export const CustomImage = Template.bind({})
|
|
54
58
|
CustomImage.args = {
|
|
55
59
|
src: 'https://via.placeholder.com/1200x800.png', // 다른 테스트 이미지 URL을 실제 경로로 변경하세요.
|
|
56
60
|
width: 400,
|
|
57
|
-
height: 600
|
|
61
|
+
height: 600,
|
|
62
|
+
restrictBoundary: true
|
|
58
63
|
}
|