@operato/mini-map 8.0.0 → 9.0.0-beta.10
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 +83 -6
- package/dist/src/ox-mini-map.js +61 -21
- package/dist/src/ox-mini-map.js.map +1 -1
- package/dist/src/ox-zoomable-image.d.ts +378 -14
- package/dist/src/ox-zoomable-image.js +129 -113
- package/dist/src/ox-zoomable-image.js.map +1 -1
- package/dist/stories/ox-zoomable-image.stories.d.ts +5 -1
- package/dist/stories/ox-zoomable-image.stories.js +11 -7
- package/dist/stories/ox-zoomable-image.stories.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +10 -10
|
@@ -1,17 +1,7 @@
|
|
|
1
1
|
import { __decorate } from "tslib";
|
|
2
2
|
import { css, LitElement } from 'lit';
|
|
3
3
|
import { customElement, property, state } from 'lit/decorators.js';
|
|
4
|
-
|
|
5
|
-
function getPoint(e) {
|
|
6
|
-
if (e instanceof MouseEvent && e.button === 0) {
|
|
7
|
-
return { x: e.clientX, y: e.clientY };
|
|
8
|
-
}
|
|
9
|
-
else if (e instanceof TouchEvent && e.touches.length === 1) {
|
|
10
|
-
const touch = e.touches[0];
|
|
11
|
-
return { x: touch.clientX, y: touch.clientY };
|
|
12
|
-
}
|
|
13
|
-
return undefined;
|
|
14
|
-
}
|
|
4
|
+
import { gesture } from '@operato/utils/gesture-mixin.js';
|
|
15
5
|
/**
|
|
16
6
|
* Example usage:
|
|
17
7
|
*
|
|
@@ -34,51 +24,113 @@ function getPoint(e) {
|
|
|
34
24
|
* </style>
|
|
35
25
|
* </head>
|
|
36
26
|
* <body>
|
|
37
|
-
* <ox-zoomable-image src="path/to/your/image.jpg"></ox-zoomable-image>
|
|
27
|
+
* <ox-zoomable-image src="path/to/your/image.jpg" restrict-boundary></ox-zoomable-image>
|
|
38
28
|
* </body>
|
|
39
29
|
* </html>
|
|
40
30
|
*/
|
|
41
|
-
let OxZoomableImage = class OxZoomableImage extends LitElement {
|
|
31
|
+
let OxZoomableImage = class OxZoomableImage extends gesture(LitElement) {
|
|
42
32
|
constructor() {
|
|
43
33
|
super(...arguments);
|
|
44
|
-
this
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
34
|
+
Object.defineProperty(this, "src", {
|
|
35
|
+
enumerable: true,
|
|
36
|
+
configurable: true,
|
|
37
|
+
writable: true,
|
|
38
|
+
value: void 0
|
|
39
|
+
}); // Source URL for the image
|
|
40
|
+
Object.defineProperty(this, "restrictBoundary", {
|
|
41
|
+
enumerable: true,
|
|
42
|
+
configurable: true,
|
|
43
|
+
writable: true,
|
|
44
|
+
value: false
|
|
45
|
+
}); // Ensure the image stays within bounds
|
|
46
|
+
Object.defineProperty(this, "zoom", {
|
|
47
|
+
enumerable: true,
|
|
48
|
+
configurable: true,
|
|
49
|
+
writable: true,
|
|
50
|
+
value: 1
|
|
51
|
+
}); // Current zoom level
|
|
52
|
+
Object.defineProperty(this, "left", {
|
|
53
|
+
enumerable: true,
|
|
54
|
+
configurable: true,
|
|
55
|
+
writable: true,
|
|
56
|
+
value: 0
|
|
57
|
+
}); // Horizontal position
|
|
58
|
+
Object.defineProperty(this, "top", {
|
|
59
|
+
enumerable: true,
|
|
60
|
+
configurable: true,
|
|
61
|
+
writable: true,
|
|
62
|
+
value: 0
|
|
63
|
+
}); // Vertical position
|
|
64
|
+
Object.defineProperty(this, "imageWidth", {
|
|
65
|
+
enumerable: true,
|
|
66
|
+
configurable: true,
|
|
67
|
+
writable: true,
|
|
68
|
+
value: 0
|
|
69
|
+
});
|
|
70
|
+
Object.defineProperty(this, "imageHeight", {
|
|
71
|
+
enumerable: true,
|
|
72
|
+
configurable: true,
|
|
73
|
+
writable: true,
|
|
74
|
+
value: 0
|
|
75
|
+
});
|
|
76
|
+
Object.defineProperty(this, "pinchHandler", {
|
|
77
|
+
enumerable: true,
|
|
78
|
+
configurable: true,
|
|
79
|
+
writable: true,
|
|
80
|
+
value: (e) => {
|
|
81
|
+
const { scale, centerX, centerY } = e.detail;
|
|
82
|
+
this.zoomAt(centerX, centerY, scale);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
Object.defineProperty(this, "wheelHandler", {
|
|
86
|
+
enumerable: true,
|
|
87
|
+
configurable: true,
|
|
88
|
+
writable: true,
|
|
89
|
+
value: (e) => {
|
|
90
|
+
// Handle zoom in and out based on mouse wheel events
|
|
91
|
+
e.preventDefault();
|
|
92
|
+
const delta = Math.sign(e.deltaY);
|
|
93
|
+
this.zoomAt(e.clientX, e.clientY, 1 - delta / 20);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
Object.defineProperty(this, "dragHandler", {
|
|
97
|
+
enumerable: true,
|
|
98
|
+
configurable: true,
|
|
99
|
+
writable: true,
|
|
100
|
+
value: (e) => {
|
|
101
|
+
const { deltaX, deltaY } = e.detail;
|
|
102
|
+
this.top += deltaY;
|
|
103
|
+
this.left += deltaX;
|
|
104
|
+
this.updateTransform();
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
Object.defineProperty(this, "doubleTapHandler", {
|
|
108
|
+
enumerable: true,
|
|
109
|
+
configurable: true,
|
|
110
|
+
writable: true,
|
|
111
|
+
value: (e) => {
|
|
112
|
+
// Reset zoom and position to initial state
|
|
113
|
+
this.zoom = 1;
|
|
114
|
+
this.left = 0;
|
|
115
|
+
this.top = 0;
|
|
116
|
+
}
|
|
117
|
+
});
|
|
56
118
|
}
|
|
57
119
|
connectedCallback() {
|
|
58
120
|
super.connectedCallback();
|
|
59
121
|
// Register event listeners for zooming and dragging
|
|
60
|
-
this.addEventListener('
|
|
61
|
-
this.addEventListener('
|
|
62
|
-
this.addEventListener('
|
|
63
|
-
this.addEventListener('
|
|
64
|
-
document.addEventListener('mouseup', this.dragEndHandler);
|
|
65
|
-
document.addEventListener('touchend', this.dragEndHandler);
|
|
66
|
-
document.addEventListener('touchcancel', this.dragEndHandler);
|
|
67
|
-
document.addEventListener('mousemove', this.dragMoveHandler);
|
|
68
|
-
document.addEventListener('touchmove', this.dragMoveHandler);
|
|
122
|
+
this.addEventListener('pinch', this.pinchHandler);
|
|
123
|
+
this.addEventListener('drag', this.dragHandler);
|
|
124
|
+
this.addEventListener('doubletap', this.doubleTapHandler);
|
|
125
|
+
this.addEventListener('wheel', this.wheelHandler);
|
|
69
126
|
this.calculateMinZoom(); // Calculate the minimum zoom level based on the image and container size
|
|
70
127
|
}
|
|
71
128
|
disconnectedCallback() {
|
|
72
129
|
// Remove event listeners when the component is disconnected
|
|
73
|
-
this.removeEventListener('
|
|
74
|
-
this.removeEventListener('
|
|
75
|
-
this.removeEventListener('
|
|
76
|
-
this.removeEventListener('
|
|
77
|
-
document.removeEventListener('mouseup', this.dragEndHandler);
|
|
78
|
-
document.removeEventListener('touchend', this.dragEndHandler);
|
|
79
|
-
document.removeEventListener('touchcancel', this.dragEndHandler);
|
|
80
|
-
document.removeEventListener('mousemove', this.dragMoveHandler);
|
|
81
|
-
document.removeEventListener('touchmove', this.dragMoveHandler);
|
|
130
|
+
this.removeEventListener('pinch', this.pinchHandler);
|
|
131
|
+
this.removeEventListener('drag', this.dragHandler);
|
|
132
|
+
this.removeEventListener('doubletap', this.doubleTapHandler);
|
|
133
|
+
this.removeEventListener('wheel', this.wheelHandler);
|
|
82
134
|
super.disconnectedCallback();
|
|
83
135
|
}
|
|
84
136
|
updated(changes) {
|
|
@@ -96,78 +148,31 @@ let OxZoomableImage = class OxZoomableImage extends LitElement {
|
|
|
96
148
|
img.onload = () => {
|
|
97
149
|
this.imageWidth = img.width;
|
|
98
150
|
this.imageHeight = img.height;
|
|
99
|
-
|
|
100
|
-
const hostAspectRatio = this.offsetWidth / this.offsetHeight;
|
|
101
|
-
if (aspectRatio < hostAspectRatio) {
|
|
102
|
-
this.minZoom = this.offsetHeight / img.height;
|
|
103
|
-
}
|
|
104
|
-
else {
|
|
105
|
-
this.minZoom = this.offsetWidth / img.width;
|
|
106
|
-
}
|
|
151
|
+
console.log(this.imageWidth, this.imageHeight);
|
|
107
152
|
this.zoom = 1; // this.minZoom
|
|
108
153
|
this.left = 0; // Center the image
|
|
109
154
|
this.top = 0; // Center the image
|
|
110
155
|
this.updateTransform();
|
|
111
156
|
};
|
|
112
157
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
const
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
this.
|
|
124
|
-
this.top -= offsetY * (zoomFactor - 1);
|
|
125
|
-
this.zoom = newZoom;
|
|
126
|
-
this.applyBounds(); // Ensure the image stays within bounds
|
|
127
|
-
this.updateTransform(); // Apply the new zoom and position
|
|
128
|
-
}
|
|
129
|
-
onDragStart(e) {
|
|
130
|
-
// Start dragging the image
|
|
131
|
-
const point = getPoint(e);
|
|
132
|
-
if (point) {
|
|
133
|
-
this.dragStart = point;
|
|
134
|
-
e.stopPropagation();
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
onDragMove(e) {
|
|
138
|
-
// Move the image based on drag events
|
|
139
|
-
if (!this.dragStart)
|
|
140
|
-
return;
|
|
141
|
-
const point = getPoint(e);
|
|
142
|
-
if (!point)
|
|
143
|
-
return;
|
|
144
|
-
e.stopPropagation();
|
|
145
|
-
e.preventDefault();
|
|
146
|
-
const { x, y } = point;
|
|
147
|
-
this.left += x - this.dragStart.x;
|
|
148
|
-
this.top += y - this.dragStart.y;
|
|
149
|
-
this.dragStart = point;
|
|
150
|
-
this.applyBounds(); // Ensure the image stays within bounds
|
|
151
|
-
this.updateTransform(); // Apply the new position
|
|
152
|
-
}
|
|
153
|
-
onDragEnd(e) {
|
|
154
|
-
// End the drag operation
|
|
155
|
-
if (this.dragStart) {
|
|
156
|
-
e.stopPropagation();
|
|
157
|
-
e.preventDefault();
|
|
158
|
-
this.dragStart = undefined;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
onReset(e) {
|
|
162
|
-
// Reset zoom and position to initial state
|
|
163
|
-
this.zoom = 1;
|
|
164
|
-
this.left = 0;
|
|
165
|
-
this.top = 0;
|
|
166
|
-
this.updateTransform();
|
|
158
|
+
zoomAt(clientX, clientY, scale) {
|
|
159
|
+
const { offsetLeft: left, offsetTop: top, offsetWidth: width, offsetHeight: height } = this;
|
|
160
|
+
const centerX = left + width / 2;
|
|
161
|
+
const centerY = top + height / 2;
|
|
162
|
+
// this.imageWidth
|
|
163
|
+
// this.imageHeight
|
|
164
|
+
const dx = ((centerX - clientX) / width) * 100 * (1 - scale);
|
|
165
|
+
const dy = ((centerY - clientY) / height) * 100 * (1 - scale);
|
|
166
|
+
this.left += dx;
|
|
167
|
+
this.top += dy;
|
|
168
|
+
this.zoom *= scale;
|
|
167
169
|
}
|
|
168
170
|
updateTransform() {
|
|
171
|
+
if (this.restrictBoundary) {
|
|
172
|
+
this.applyBounds(); // Ensure the image stays within bounds
|
|
173
|
+
}
|
|
169
174
|
// Apply the current zoom and position to the image
|
|
170
|
-
this.style.setProperty('--image-transform', `
|
|
175
|
+
this.style.setProperty('--image-transform', `translate(${this.left}px, ${this.top}px) scale(${this.zoom})`);
|
|
171
176
|
}
|
|
172
177
|
applyBounds() {
|
|
173
178
|
// Ensure the image stays within the bounds of the container
|
|
@@ -180,14 +185,14 @@ let OxZoomableImage = class OxZoomableImage extends LitElement {
|
|
|
180
185
|
var minX = (hostWidth * (1 - this.zoom)) / 2;
|
|
181
186
|
minX = minX > 0 ? -minX : minX;
|
|
182
187
|
var maxX = -minX;
|
|
183
|
-
var minY = (hostHeight - hostWidth * aspectRatio * this.zoom) / 2
|
|
188
|
+
var minY = (hostHeight - hostWidth * aspectRatio * this.zoom) / 2;
|
|
184
189
|
minY = Math.min(minY, -minY);
|
|
185
190
|
var maxY = -minY;
|
|
186
191
|
this.left = Math.max(minX, Math.min(this.left, maxX));
|
|
187
192
|
this.top = Math.max(minY, Math.min(this.top, maxY));
|
|
188
193
|
}
|
|
189
194
|
else {
|
|
190
|
-
var minX = (hostWidth - (hostHeight / aspectRatio) * this.zoom) / 2
|
|
195
|
+
var minX = (hostWidth - (hostHeight / aspectRatio) * this.zoom) / 2;
|
|
191
196
|
minX = Math.min(minX, -minX);
|
|
192
197
|
var maxX = -minX;
|
|
193
198
|
var minY = (-hostHeight * (1 - this.zoom)) / 2;
|
|
@@ -199,23 +204,31 @@ let OxZoomableImage = class OxZoomableImage extends LitElement {
|
|
|
199
204
|
}
|
|
200
205
|
};
|
|
201
206
|
// Define CSS styles for the component
|
|
202
|
-
OxZoomableImage
|
|
207
|
+
Object.defineProperty(OxZoomableImage, "styles", {
|
|
208
|
+
enumerable: true,
|
|
209
|
+
configurable: true,
|
|
210
|
+
writable: true,
|
|
211
|
+
value: css `
|
|
203
212
|
:host {
|
|
204
213
|
display: block;
|
|
205
214
|
position: relative;
|
|
206
215
|
width: 100%;
|
|
207
216
|
height: 100%;
|
|
208
217
|
overflow: hidden;
|
|
218
|
+
|
|
219
|
+
touch-action: none;
|
|
209
220
|
}
|
|
210
221
|
|
|
211
222
|
:host::before {
|
|
212
223
|
content: '';
|
|
213
224
|
display: block;
|
|
225
|
+
|
|
214
226
|
position: absolute;
|
|
215
227
|
top: 0;
|
|
216
228
|
right: 0;
|
|
217
229
|
width: 100%;
|
|
218
230
|
height: 100%;
|
|
231
|
+
|
|
219
232
|
background-image: var(
|
|
220
233
|
--background-image,
|
|
221
234
|
linear-gradient(var(--primary-color, lightgray), var(--primary-color, lightgray))
|
|
@@ -224,11 +237,17 @@ OxZoomableImage.styles = css `
|
|
|
224
237
|
background-repeat: no-repeat;
|
|
225
238
|
background-position: center;
|
|
226
239
|
background-size: contain;
|
|
240
|
+
|
|
241
|
+
touch-action: none;
|
|
227
242
|
}
|
|
228
|
-
|
|
243
|
+
`
|
|
244
|
+
});
|
|
229
245
|
__decorate([
|
|
230
246
|
property({ type: String })
|
|
231
247
|
], OxZoomableImage.prototype, "src", void 0);
|
|
248
|
+
__decorate([
|
|
249
|
+
property({ type: Boolean, attribute: 'restrict-boundary' })
|
|
250
|
+
], OxZoomableImage.prototype, "restrictBoundary", void 0);
|
|
232
251
|
__decorate([
|
|
233
252
|
state()
|
|
234
253
|
], OxZoomableImage.prototype, "zoom", void 0);
|
|
@@ -238,9 +257,6 @@ __decorate([
|
|
|
238
257
|
__decorate([
|
|
239
258
|
state()
|
|
240
259
|
], OxZoomableImage.prototype, "top", void 0);
|
|
241
|
-
__decorate([
|
|
242
|
-
state()
|
|
243
|
-
], OxZoomableImage.prototype, "minZoom", void 0);
|
|
244
260
|
__decorate([
|
|
245
261
|
state()
|
|
246
262
|
], OxZoomableImage.prototype, "imageWidth", void 0);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ox-zoomable-image.js","sourceRoot":"","sources":["../../src/ox-zoomable-image.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, LitElement, PropertyValues } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\n// Utility function to get the point from MouseEvent or TouchEvent\nfunction getPoint(e: MouseEvent | TouchEvent): { x: number; y: number } | undefined {\n if (e instanceof MouseEvent && e.button === 0) {\n return { x: e.clientX, y: e.clientY }\n } else if (e instanceof TouchEvent && e.touches.length === 1) {\n const touch = e.touches[0]\n return { x: touch.clientX, y: touch.clientY }\n }\n return undefined\n}\n\n/**\n * Example usage:\n *\n * <html>\n * <head>\n * <script type=\"module\" src=\"path/to/ox-zoomable-image.js\"></script>\n * <style>\n * body {\n * margin: 0;\n * overflow: hidden;\n * display: flex;\n * justify-content: center;\n * align-items: center;\n * height: 100vh;\n * }\n * ox-zoomable-image {\n * width: 80%;\n * height: 80%;\n * }\n * </style>\n * </head>\n * <body>\n * <ox-zoomable-image src=\"path/to/your/image.jpg\"></ox-zoomable-image>\n * </body>\n * </html>\n */\n\n@customElement('ox-zoomable-image')\nexport class OxZoomableImage extends LitElement {\n // Define CSS styles for the component\n static styles = css`\n :host {\n display: block;\n position: relative;\n width: 100%;\n height: 100%;\n overflow: hidden;\n }\n\n :host::before {\n content: '';\n display: block;\n position: absolute;\n top: 0;\n right: 0;\n width: 100%;\n height: 100%;\n background-image: var(\n --background-image,\n linear-gradient(var(--primary-color, lightgray), var(--primary-color, lightgray))\n );\n transform: var(--image-transform, '');\n background-repeat: no-repeat;\n background-position: center;\n background-size: contain;\n }\n `\n\n @property({ type: String }) src?: string // Source URL for the image\n @state() private zoom = 1 // Current zoom level\n @state() private left = 0 // Horizontal position\n @state() private top = 0 // Vertical position\n @state() private minZoom = 1 // Minimum zoom level\n @state() private imageWidth = 0\n @state() private imageHeight = 0\n\n private dragStart?: { x: number; y: number } // Starting point of drag\n\n // Event listeners bound to the component\n private readonly dragEndHandler = this.onDragEnd.bind(this) as EventListener\n private readonly dragMoveHandler = this.onDragMove.bind(this) as EventListener\n private readonly dragStartHandler = this.onDragStart.bind(this) as EventListener\n private readonly wheelEventHandler = this.onWheelEvent.bind(this) as EventListener\n private readonly resetHandler = this.onReset.bind(this) as EventListener\n\n connectedCallback(): void {\n super.connectedCallback()\n\n // Register event listeners for zooming and dragging\n this.addEventListener('wheel', this.wheelEventHandler)\n this.addEventListener('mousedown', this.dragStartHandler)\n this.addEventListener('touchstart', this.dragStartHandler)\n this.addEventListener('dblclick', this.resetHandler)\n\n document.addEventListener('mouseup', this.dragEndHandler)\n document.addEventListener('touchend', this.dragEndHandler)\n document.addEventListener('touchcancel', this.dragEndHandler)\n document.addEventListener('mousemove', this.dragMoveHandler)\n document.addEventListener('touchmove', this.dragMoveHandler)\n\n this.calculateMinZoom() // Calculate the minimum zoom level based on the image and container size\n }\n\n disconnectedCallback(): void {\n // Remove event listeners when the component is disconnected\n this.removeEventListener('wheel', this.wheelEventHandler)\n this.removeEventListener('mousedown', this.dragStartHandler)\n this.removeEventListener('touchstart', this.dragStartHandler)\n this.removeEventListener('dblclick', this.resetHandler)\n\n document.removeEventListener('mouseup', this.dragEndHandler)\n document.removeEventListener('touchend', this.dragEndHandler)\n document.removeEventListener('touchcancel', this.dragEndHandler)\n document.removeEventListener('mousemove', this.dragMoveHandler)\n document.removeEventListener('touchmove', this.dragMoveHandler)\n\n super.disconnectedCallback()\n }\n\n updated(changes: PropertyValues<this>): void {\n // Update the background image and transform style when properties change\n if (changes.has('src') && this.src) {\n this.style.setProperty('--background-image', `url('${this.src}')`)\n this.calculateMinZoom() // Recalculate the minimum zoom level when the source changes\n }\n this.updateTransform() // Apply the current zoom and position\n }\n\n private calculateMinZoom() {\n // Calculate the minimum zoom level to ensure the image always fills the container\n const img = new Image()\n img.src = this.src || ''\n img.onload = () => {\n this.imageWidth = img.width\n this.imageHeight = img.height\n\n const aspectRatio = img.width / img.height\n const hostAspectRatio = this.offsetWidth / this.offsetHeight\n if (aspectRatio < hostAspectRatio) {\n this.minZoom = this.offsetHeight / img.height\n } else {\n this.minZoom = this.offsetWidth / img.width\n }\n this.zoom = 1 // this.minZoom\n this.left = 0 // Center the image\n this.top = 0 // Center the image\n this.updateTransform()\n }\n }\n\n private onWheelEvent(e: WheelEvent): void {\n // Handle zoom in and out based on mouse wheel events\n e.preventDefault()\n const delta = Math.sign(e.deltaY)\n const newZoom = Math.max(1, this.zoom * (1 - delta / 20))\n const zoomFactor = newZoom / this.zoom\n\n const rect = this.getBoundingClientRect()\n const offsetX = e.clientX - rect.left - rect.width / 2\n const offsetY = e.clientY - rect.top - rect.height / 2\n\n // Adjust the position to zoom in/out relative to the center of the client rect\n this.left -= offsetX * (zoomFactor - 1)\n this.top -= offsetY * (zoomFactor - 1)\n\n this.zoom = newZoom\n this.applyBounds() // Ensure the image stays within bounds\n this.updateTransform() // Apply the new zoom and position\n }\n\n private onDragStart(e: MouseEvent | TouchEvent): void {\n // Start dragging the image\n const point = getPoint(e)\n if (point) {\n this.dragStart = point\n e.stopPropagation()\n }\n }\n\n private onDragMove(e: MouseEvent | TouchEvent): void {\n // Move the image based on drag events\n if (!this.dragStart) return\n\n const point = getPoint(e)\n if (!point) return\n\n e.stopPropagation()\n e.preventDefault()\n\n const { x, y } = point\n this.left += x - this.dragStart.x\n this.top += y - this.dragStart.y\n\n this.dragStart = point\n this.applyBounds() // Ensure the image stays within bounds\n this.updateTransform() // Apply the new position\n }\n\n private onDragEnd(e: Event): void {\n // End the drag operation\n if (this.dragStart) {\n e.stopPropagation()\n e.preventDefault()\n this.dragStart = undefined\n }\n }\n\n private onReset(e: Event): void {\n // Reset zoom and position to initial state\n this.zoom = 1\n this.left = 0\n this.top = 0\n this.updateTransform()\n }\n\n private updateTransform() {\n // Apply the current zoom and position to the image\n this.style.setProperty('--image-transform', `scale(${this.zoom}) translate(${this.left}px, ${this.top}px)`)\n }\n\n private applyBounds() {\n // Ensure the image stays within the bounds of the container\n const hostWidth = this.offsetWidth\n const hostHeight = this.offsetHeight\n\n const aspectRatio = this.imageHeight / this.imageWidth\n const hostAspectRatio = hostHeight / hostWidth\n\n if (aspectRatio < hostAspectRatio) {\n /* image ratio is wider than host */\n var minX = (hostWidth * (1 - this.zoom)) / 2\n minX = minX > 0 ? -minX : minX\n var maxX = -minX\n\n var minY = (hostHeight - hostWidth * aspectRatio * this.zoom) / 2 / this.zoom\n minY = Math.min(minY, -minY)\n var maxY = -minY\n\n this.left = Math.max(minX, Math.min(this.left, maxX))\n this.top = Math.max(minY, Math.min(this.top, maxY))\n } else {\n var minX = (hostWidth - (hostHeight / aspectRatio) * this.zoom) / 2 / this.zoom\n minX = Math.min(minX, -minX)\n var maxX = -minX\n\n var minY = (-hostHeight * (1 - this.zoom)) / 2\n minY = minY > 0 ? -minY : minY\n var maxY = -minY\n\n this.left = Math.max(minX, Math.min(this.left, maxX))\n this.top = Math.max(minY, Math.min(this.top, maxY))\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ox-zoomable-image.js","sourceRoot":"","sources":["../../src/ox-zoomable-image.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, LitElement, PropertyValues } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { gesture, GestureEventDoubleTap, GestureEventDrag, GestureEventPinch } from '@operato/utils/gesture-mixin.js'\n\n/**\n * Example usage:\n *\n * <html>\n * <head>\n * <script type=\"module\" src=\"path/to/ox-zoomable-image.js\"></script>\n * <style>\n * body {\n * margin: 0;\n * overflow: hidden;\n * display: flex;\n * justify-content: center;\n * align-items: center;\n * height: 100vh;\n * }\n * ox-zoomable-image {\n * width: 80%;\n * height: 80%;\n * }\n * </style>\n * </head>\n * <body>\n * <ox-zoomable-image src=\"path/to/your/image.jpg\" restrict-boundary></ox-zoomable-image>\n * </body>\n * </html>\n */\n\n@customElement('ox-zoomable-image')\nexport class OxZoomableImage extends gesture(LitElement) {\n // Define CSS styles for the component\n static styles = css`\n :host {\n display: block;\n position: relative;\n width: 100%;\n height: 100%;\n overflow: hidden;\n\n touch-action: none;\n }\n\n :host::before {\n content: '';\n display: block;\n\n position: absolute;\n top: 0;\n right: 0;\n width: 100%;\n height: 100%;\n\n background-image: var(\n --background-image,\n linear-gradient(var(--primary-color, lightgray), var(--primary-color, lightgray))\n );\n transform: var(--image-transform, '');\n background-repeat: no-repeat;\n background-position: center;\n background-size: contain;\n\n touch-action: none;\n }\n `\n\n @property({ type: String }) src?: string // Source URL for the image\n @property({ type: Boolean, attribute: 'restrict-boundary' }) restrictBoundary: boolean = false // Ensure the image stays within bounds\n\n @state() private zoom = 1 // Current zoom level\n @state() private left = 0 // Horizontal position\n @state() private top = 0 // Vertical position\n @state() private imageWidth = 0\n @state() private imageHeight = 0\n\n connectedCallback(): void {\n super.connectedCallback()\n\n // Register event listeners for zooming and dragging\n this.addEventListener('pinch', this.pinchHandler as EventListener)\n this.addEventListener('drag', this.dragHandler as EventListener)\n this.addEventListener('doubletap', this.doubleTapHandler as EventListener)\n this.addEventListener('wheel', this.wheelHandler as EventListener)\n\n this.calculateMinZoom() // Calculate the minimum zoom level based on the image and container size\n }\n\n disconnectedCallback(): void {\n // Remove event listeners when the component is disconnected\n this.removeEventListener('pinch', this.pinchHandler as EventListener)\n this.removeEventListener('drag', this.dragHandler as EventListener)\n this.removeEventListener('doubletap', this.doubleTapHandler as EventListener)\n this.removeEventListener('wheel', this.wheelHandler as EventListener)\n\n super.disconnectedCallback()\n }\n\n updated(changes: PropertyValues<this>): void {\n // Update the background image and transform style when properties change\n if (changes.has('src') && this.src) {\n this.style.setProperty('--background-image', `url('${this.src}')`)\n this.calculateMinZoom() // Recalculate the minimum zoom level when the source changes\n }\n\n this.updateTransform() // Apply the current zoom and position\n }\n\n private calculateMinZoom() {\n // Calculate the minimum zoom level to ensure the image always fills the container\n const img = new Image()\n img.src = this.src || ''\n img.onload = () => {\n this.imageWidth = img.width\n this.imageHeight = img.height\n\n console.log(this.imageWidth, this.imageHeight)\n\n this.zoom = 1 // this.minZoom\n this.left = 0 // Center the image\n this.top = 0 // Center the image\n\n this.updateTransform()\n }\n }\n\n private pinchHandler = (e: GestureEventPinch) => {\n const { scale, centerX, centerY } = e.detail\n this.zoomAt(centerX, centerY, scale)\n }\n\n private zoomAt(clientX: number, clientY: number, scale: number) {\n const { offsetLeft: left, offsetTop: top, offsetWidth: width, offsetHeight: height } = this\n\n const centerX = left + width / 2\n const centerY = top + height / 2\n\n // this.imageWidth\n // this.imageHeight\n const dx = ((centerX - clientX) / width) * 100 * (1 - scale)\n const dy = ((centerY - clientY) / height) * 100 * (1 - scale)\n\n this.left += dx\n this.top += dy\n\n this.zoom *= scale\n }\n\n private wheelHandler = (e: WheelEvent): void => {\n // Handle zoom in and out based on mouse wheel events\n e.preventDefault()\n\n const delta = Math.sign(e.deltaY)\n this.zoomAt(e.clientX, e.clientY, 1 - delta / 20)\n }\n\n private dragHandler = (e: GestureEventDrag): void => {\n const { deltaX, deltaY } = e.detail\n\n this.top += deltaY\n this.left += deltaX\n\n this.updateTransform()\n }\n\n private doubleTapHandler = (e: GestureEventDoubleTap): void => {\n // Reset zoom and position to initial state\n this.zoom = 1\n this.left = 0\n this.top = 0\n }\n\n private updateTransform() {\n if (this.restrictBoundary) {\n this.applyBounds() // Ensure the image stays within bounds\n }\n\n // Apply the current zoom and position to the image\n this.style.setProperty('--image-transform', `translate(${this.left}px, ${this.top}px) scale(${this.zoom})`)\n }\n\n private applyBounds() {\n // Ensure the image stays within the bounds of the container\n const hostWidth = this.offsetWidth\n const hostHeight = this.offsetHeight\n\n const aspectRatio = this.imageHeight / this.imageWidth\n const hostAspectRatio = hostHeight / hostWidth\n\n if (aspectRatio < hostAspectRatio) {\n /* image ratio is wider than host */\n var minX = (hostWidth * (1 - this.zoom)) / 2\n minX = minX > 0 ? -minX : minX\n var maxX = -minX\n\n var minY = (hostHeight - hostWidth * aspectRatio * this.zoom) / 2\n minY = Math.min(minY, -minY)\n var maxY = -minY\n\n this.left = Math.max(minX, Math.min(this.left, maxX))\n this.top = Math.max(minY, Math.min(this.top, maxY))\n } else {\n var minX = (hostWidth - (hostHeight / aspectRatio) * this.zoom) / 2\n minX = Math.min(minX, -minX)\n var maxX = -minX\n\n var minY = (-hostHeight * (1 - this.zoom)) / 2\n minY = minY > 0 ? -minY : minY\n var maxY = -minY\n\n this.left = Math.max(minX, Math.min(this.left, maxX))\n this.top = Math.max(minY, Math.min(this.top, maxY))\n }\n }\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { TemplateResult } from 'lit-html';
|
|
2
|
-
import '../src/ox-zoomable-image';
|
|
2
|
+
import '../src/ox-zoomable-image.js';
|
|
3
3
|
declare const _default: {
|
|
4
4
|
title: string;
|
|
5
5
|
component: string;
|
|
@@ -13,6 +13,9 @@ declare const _default: {
|
|
|
13
13
|
height: {
|
|
14
14
|
control: string;
|
|
15
15
|
};
|
|
16
|
+
restrictBoundary: {
|
|
17
|
+
control: string;
|
|
18
|
+
};
|
|
16
19
|
};
|
|
17
20
|
};
|
|
18
21
|
export default _default;
|
|
@@ -25,6 +28,7 @@ interface OxZoomableImageProps {
|
|
|
25
28
|
src: string;
|
|
26
29
|
width: number;
|
|
27
30
|
height: number;
|
|
31
|
+
restrictBoundary: boolean;
|
|
28
32
|
}
|
|
29
33
|
export declare const Default: Story<OxZoomableImageProps>;
|
|
30
34
|
export declare const Wide: Story<OxZoomableImageProps>;
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { html } from 'lit-html';
|
|
2
|
-
import '../src/ox-zoomable-image'; // 경로를 실제 경로로 변경하세요.
|
|
2
|
+
import '../src/ox-zoomable-image.js'; // 경로를 실제 경로로 변경하세요.
|
|
3
3
|
export default {
|
|
4
4
|
title: 'OxZoomableImage',
|
|
5
5
|
component: 'ox-zoomable-image',
|
|
6
6
|
argTypes: {
|
|
7
7
|
src: { control: 'text' },
|
|
8
8
|
width: { control: 'number' },
|
|
9
|
-
height: { control: 'number' }
|
|
9
|
+
height: { control: 'number' },
|
|
10
|
+
restrictBoundary: { control: 'boolean' }
|
|
10
11
|
}
|
|
11
12
|
};
|
|
12
|
-
const Template = ({ src, width, height }) => html `
|
|
13
|
+
const Template = ({ src, width, height, restrictBoundary }) => html `
|
|
13
14
|
<style>
|
|
14
15
|
ox-zoomable-image {
|
|
15
16
|
width: ${width}px;
|
|
@@ -19,24 +20,27 @@ const Template = ({ src, width, height }) => html `
|
|
|
19
20
|
margin: 100px;
|
|
20
21
|
}
|
|
21
22
|
</style>
|
|
22
|
-
<ox-zoomable-image src=${src}></ox-zoomable-image>
|
|
23
|
+
<ox-zoomable-image src=${src} ?restrict-boundary=${restrictBoundary}></ox-zoomable-image>
|
|
23
24
|
`;
|
|
24
25
|
export const Default = Template.bind({});
|
|
25
26
|
Default.args = {
|
|
26
27
|
src: 'https://via.placeholder.com/800x600.png', // 기본 이미지 URL을 실제 경로로 변경하세요.
|
|
27
28
|
width: 400,
|
|
28
|
-
height: 600
|
|
29
|
+
height: 600,
|
|
30
|
+
restrictBoundary: true
|
|
29
31
|
};
|
|
30
32
|
export const Wide = Template.bind({});
|
|
31
33
|
Wide.args = {
|
|
32
34
|
src: 'https://via.placeholder.com/800x600.png', // 기본 이미지 URL을 실제 경로로 변경하세요.
|
|
33
35
|
width: 600,
|
|
34
|
-
height: 300
|
|
36
|
+
height: 300,
|
|
37
|
+
restrictBoundary: true
|
|
35
38
|
};
|
|
36
39
|
export const CustomImage = Template.bind({});
|
|
37
40
|
CustomImage.args = {
|
|
38
41
|
src: 'https://via.placeholder.com/1200x800.png', // 다른 테스트 이미지 URL을 실제 경로로 변경하세요.
|
|
39
42
|
width: 400,
|
|
40
|
-
height: 600
|
|
43
|
+
height: 600,
|
|
44
|
+
restrictBoundary: true
|
|
41
45
|
};
|
|
42
46
|
//# sourceMappingURL=ox-zoomable-image.stories.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ox-zoomable-image.stories.js","sourceRoot":"","sources":["../../stories/ox-zoomable-image.stories.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAkB,MAAM,UAAU,CAAA;AAC/C,OAAO,
|
|
1
|
+
{"version":3,"file":"ox-zoomable-image.stories.js","sourceRoot":"","sources":["../../stories/ox-zoomable-image.stories.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAkB,MAAM,UAAU,CAAA;AAC/C,OAAO,6BAA6B,CAAA,CAAC,oBAAoB;AAEzD,eAAe;IACb,KAAK,EAAE,iBAAiB;IACxB,SAAS,EAAE,mBAAmB;IAC9B,QAAQ,EAAE;QACR,GAAG,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE;QACxB,KAAK,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE;QAC5B,MAAM,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE;QAC7B,gBAAgB,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE;KACzC;CACF,CAAA;AAeD,MAAM,QAAQ,GAAgC,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAwB,EAAE,EAAE,CAAC,IAAI,CAAA;;;eAGvG,KAAK;gBACJ,MAAM;;;;;;2BAMK,GAAG,uBAAuB,gBAAgB;CACpE,CAAA;AAED,MAAM,CAAC,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;AACxC,OAAO,CAAC,IAAI,GAAG;IACb,GAAG,EAAE,yCAAyC,EAAE,4BAA4B;IAC5E,KAAK,EAAE,GAAG;IACV,MAAM,EAAE,GAAG;IACX,gBAAgB,EAAE,IAAI;CACvB,CAAA;AAED,MAAM,CAAC,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;AACrC,IAAI,CAAC,IAAI,GAAG;IACV,GAAG,EAAE,yCAAyC,EAAE,4BAA4B;IAC5E,KAAK,EAAE,GAAG;IACV,MAAM,EAAE,GAAG;IACX,gBAAgB,EAAE,IAAI;CACvB,CAAA;AAED,MAAM,CAAC,MAAM,WAAW,GAAG,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;AAC5C,WAAW,CAAC,IAAI,GAAG;IACjB,GAAG,EAAE,0CAA0C,EAAE,gCAAgC;IACjF,KAAK,EAAE,GAAG;IACV,MAAM,EAAE,GAAG;IACX,gBAAgB,EAAE,IAAI;CACvB,CAAA","sourcesContent":["import { html, TemplateResult } from 'lit-html'\nimport '../src/ox-zoomable-image.js' // 경로를 실제 경로로 변경하세요.\n\nexport default {\n title: 'OxZoomableImage',\n component: 'ox-zoomable-image',\n argTypes: {\n src: { control: 'text' },\n width: { control: 'number' },\n height: { control: 'number' },\n restrictBoundary: { control: 'boolean' }\n }\n}\n\ninterface Story<T> {\n (args: T): TemplateResult\n args?: Partial<T>\n argTypes?: Record<string, unknown>\n}\n\ninterface OxZoomableImageProps {\n src: string\n width: number\n height: number\n restrictBoundary: boolean\n}\n\nconst Template: Story<OxZoomableImageProps> = ({ src, width, height, restrictBoundary }: OxZoomableImageProps) => html`\n <style>\n ox-zoomable-image {\n width: ${width}px;\n height: ${height}px;\n background-color: red;\n padding: 0;\n margin: 100px;\n }\n </style>\n <ox-zoomable-image src=${src} ?restrict-boundary=${restrictBoundary}></ox-zoomable-image>\n`\n\nexport const Default = Template.bind({})\nDefault.args = {\n src: 'https://via.placeholder.com/800x600.png', // 기본 이미지 URL을 실제 경로로 변경하세요.\n width: 400,\n height: 600,\n restrictBoundary: true\n}\n\nexport const Wide = Template.bind({})\nWide.args = {\n src: 'https://via.placeholder.com/800x600.png', // 기본 이미지 URL을 실제 경로로 변경하세요.\n width: 600,\n height: 300,\n restrictBoundary: true\n}\n\nexport const CustomImage = Template.bind({})\nCustomImage.args = {\n src: 'https://via.placeholder.com/1200x800.png', // 다른 테스트 이미지 URL을 실제 경로로 변경하세요.\n width: 400,\n height: 600,\n restrictBoundary: true\n}\n"]}
|