@christianriedl/media 1.0.282 → 1.0.283
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/package.json +1 -1
- package/src/components/ImageZoomer.vue +282 -0
- package/src/views/PhotoAlbumPage.vue +41 -20
package/package.json
CHANGED
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { ref, reactive, computed, onMounted, onUnmounted, nextTick } from 'vue'
|
|
3
|
+
|
|
4
|
+
const props = defineProps<{ contentW: number, contentH: number, maxScale?: number, wheelSensitivity?: number}>()
|
|
5
|
+
|
|
6
|
+
interface ZoomPanState {
|
|
7
|
+
scale: number
|
|
8
|
+
tx: number // translateX
|
|
9
|
+
ty: number // translateY
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const containerRef = ref<HTMLElement>()
|
|
13
|
+
const imgRef = ref<HTMLImageElement>()
|
|
14
|
+
const state = reactive<ZoomPanState>({ scale: 1, tx: 0, ty: 0 })
|
|
15
|
+
|
|
16
|
+
// Damit Touch-Pan nicht mit scale=1 kollidiert
|
|
17
|
+
//const isPanning = computed(() => state.scale > 1.001)
|
|
18
|
+
const isPanning = computed(() => {
|
|
19
|
+
const fitScale = _imgW > 0 ? Math.min(_containerW / _imgW, _containerH / _imgH) : 1
|
|
20
|
+
return state.scale > fitScale + 0.001
|
|
21
|
+
})
|
|
22
|
+
const transform = computed(() => `translate(${state.tx}px, ${state.ty}px) scale(${state.scale})`)
|
|
23
|
+
const dragging = ref(false);
|
|
24
|
+
const cursor = computed(() => {
|
|
25
|
+
if (dragging.value) return 'grabbing'
|
|
26
|
+
if (isPanning.value) return 'grab' // scale > 1, Maus oben
|
|
27
|
+
return 'default'
|
|
28
|
+
})
|
|
29
|
+
let maxScale = props.maxScale ?? 1; // 1:1 Pixel
|
|
30
|
+
let wheelSensitivity = props.wheelSensitivity ?? 0.001;
|
|
31
|
+
|
|
32
|
+
// ── Touch (Pinch + Pan) ───────────────────────────────────────────────
|
|
33
|
+
let lastTouchDist = 0
|
|
34
|
+
let lastTouchMid = { x: 0, y: 0 }
|
|
35
|
+
let lastPan = { x: 0, y: 0 }
|
|
36
|
+
|
|
37
|
+
let _containerW = 0, _containerH = 0, _imgW = 0, _imgH = 0
|
|
38
|
+
|
|
39
|
+
// Mouse-Drag Pan (Desktop)
|
|
40
|
+
let lastMouse = { x: 0, y: 0 }
|
|
41
|
+
let _resizeTimer: ReturnType<typeof setTimeout> | null = null
|
|
42
|
+
|
|
43
|
+
// If not working : Handle Fullscreen seperately
|
|
44
|
+
function setDimensionsDebounced(cw: number, ch: number, iw: number, ih: number) {
|
|
45
|
+
if (_resizeTimer) clearTimeout(_resizeTimer)
|
|
46
|
+
_resizeTimer = setTimeout(() => {
|
|
47
|
+
_resizeTimer = null
|
|
48
|
+
console.log(`${cw}x${ch} at ${new Date().getTime()}`);
|
|
49
|
+
setDimensions(cw, ch, iw, ih)
|
|
50
|
+
}, 50)
|
|
51
|
+
}
|
|
52
|
+
onMounted(() => {
|
|
53
|
+
const ro = new ResizeObserver(([entry]) => {
|
|
54
|
+
const { width, height } = entry.contentRect
|
|
55
|
+
setDimensionsDebounced(width, height, props.contentW, props.contentH)
|
|
56
|
+
})
|
|
57
|
+
ro.observe(containerRef.value!)
|
|
58
|
+
onUnmounted(() => {
|
|
59
|
+
if (_resizeTimer) clearTimeout(_resizeTimer)
|
|
60
|
+
ro.disconnect()
|
|
61
|
+
onMouseUp();
|
|
62
|
+
})
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
function onMouseDown(e: MouseEvent) {
|
|
66
|
+
if (e.button !== 0) return // nur linke Taste
|
|
67
|
+
if (!isPanning.value) return
|
|
68
|
+
dragging.value = true
|
|
69
|
+
lastMouse = { x: e.clientX, y: e.clientY }
|
|
70
|
+
window.addEventListener('mousemove', onMouseMove)
|
|
71
|
+
window.addEventListener('mouseup', onMouseUp)
|
|
72
|
+
console.log ("mousedown")
|
|
73
|
+
}
|
|
74
|
+
function onMouseMove(e: MouseEvent) {
|
|
75
|
+
if (!dragging.value) return
|
|
76
|
+
pan(e.clientX - lastMouse.x, e.clientY - lastMouse.y)
|
|
77
|
+
lastMouse.x = e.clientX;
|
|
78
|
+
lastMouse.y = e.clientY;
|
|
79
|
+
console.log ("mousemove")
|
|
80
|
+
}
|
|
81
|
+
function onMouseUp() {
|
|
82
|
+
if (!dragging.value) return
|
|
83
|
+
console.log ("mouseup")
|
|
84
|
+
dragging.value = false
|
|
85
|
+
window.removeEventListener('mousemove', onMouseMove)
|
|
86
|
+
window.removeEventListener('mouseup', onMouseUp)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ── Pivot-Zoom (Kern-Arithmetik) ──────────────────────────────────────
|
|
90
|
+
/*
|
|
91
|
+
function zoomAt(pivotX: number, pivotY: number, delta: number) {
|
|
92
|
+
const newScale = Math.min(maxScale, Math.max(minScale, state.scale * delta))
|
|
93
|
+
const ratio = newScale / state.scale
|
|
94
|
+
|
|
95
|
+
// Pivot bleibt fix: Punkt unter Maus/Finger darf sich nicht verschieben
|
|
96
|
+
state.tx = pivotX - (pivotX - state.tx) * ratio
|
|
97
|
+
state.ty = pivotY - (pivotY - state.ty) * ratio
|
|
98
|
+
state.scale = newScale
|
|
99
|
+
|
|
100
|
+
clampTranslate()
|
|
101
|
+
}
|
|
102
|
+
*/
|
|
103
|
+
function zoomAt(pivotX: number, pivotY: number, delta: number) {
|
|
104
|
+
const fitScale = Math.min(_containerW / _imgW, _containerH / _imgH)
|
|
105
|
+
const newScale = Math.min(maxScale, Math.max(fitScale, state.scale * delta))
|
|
106
|
+
const ratio = newScale / state.scale
|
|
107
|
+
|
|
108
|
+
// Pivot von Container-Koordinaten auf center/center umrechnen
|
|
109
|
+
const px = pivotX - _containerW / 2
|
|
110
|
+
const py = pivotY - _containerH / 2
|
|
111
|
+
|
|
112
|
+
state.tx = px - (px - state.tx) * ratio
|
|
113
|
+
state.ty = py - (py - state.ty) * ratio
|
|
114
|
+
state.scale = newScale
|
|
115
|
+
|
|
116
|
+
clampTranslate()
|
|
117
|
+
}
|
|
118
|
+
// Pan
|
|
119
|
+
function pan(dx: number, dy: number) {
|
|
120
|
+
state.tx += dx
|
|
121
|
+
state.ty += dy
|
|
122
|
+
clampTranslate()
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// ── Wheel ─────────────────────────────────────────────────────────────
|
|
126
|
+
function onWheel(e: WheelEvent) {
|
|
127
|
+
e.preventDefault()
|
|
128
|
+
const rect = (e.currentTarget as HTMLElement).getBoundingClientRect()
|
|
129
|
+
const pivotX = e.clientX - rect.left
|
|
130
|
+
const pivotY = e.clientY - rect.top
|
|
131
|
+
const delta = 1 - e.deltaY * wheelSensitivity
|
|
132
|
+
zoomAt(pivotX, pivotY, delta)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function getTouchMid(touches: TouchList, rect: DOMRect) {
|
|
136
|
+
const t0 = touches[0], t1 = touches[1]
|
|
137
|
+
return {
|
|
138
|
+
x: ((t0.clientX + t1.clientX) / 2) - rect.left,
|
|
139
|
+
y: ((t0.clientY + t1.clientY) / 2) - rect.top,
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function getTouchDist(touches: TouchList) : number {
|
|
144
|
+
const dx = touches[0].clientX - touches[1].clientX
|
|
145
|
+
const dy = touches[0].clientY - touches[1].clientY
|
|
146
|
+
return Math.hypot(dx, dy)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function onTouchStart(e: TouchEvent) {
|
|
150
|
+
const rect = (e.currentTarget as HTMLElement).getBoundingClientRect()
|
|
151
|
+
if (e.touches.length === 2) {
|
|
152
|
+
lastTouchDist = getTouchDist(e.touches)
|
|
153
|
+
lastTouchMid = getTouchMid(e.touches, rect)
|
|
154
|
+
}
|
|
155
|
+
else if (e.touches.length === 1 && isPanning.value) {
|
|
156
|
+
lastPan = { x: e.touches[0].clientX, y: e.touches[0].clientY }
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function onTouchMove(e: TouchEvent) {
|
|
161
|
+
const rect = (e.currentTarget as HTMLElement).getBoundingClientRect()
|
|
162
|
+
|
|
163
|
+
if (e.touches.length === 2) {
|
|
164
|
+
e.preventDefault() // kein Browser-Scroll
|
|
165
|
+
const dist = getTouchDist(e.touches)
|
|
166
|
+
const mid = getTouchMid(e.touches, rect)
|
|
167
|
+
zoomAt(mid.x, mid.y, dist / lastTouchDist) // Pinch-Zoom
|
|
168
|
+
// Gleichzeitig Pan (Mittelpunkt hat sich verschoben)
|
|
169
|
+
state.tx += mid.x - lastTouchMid.x
|
|
170
|
+
state.ty += mid.y - lastTouchMid.y
|
|
171
|
+
lastTouchDist = dist
|
|
172
|
+
lastTouchMid = mid
|
|
173
|
+
clampTranslate()
|
|
174
|
+
}
|
|
175
|
+
else if (e.touches.length === 1 && isPanning.value) {
|
|
176
|
+
e.preventDefault() // kein Carousel-Swipe
|
|
177
|
+
state.tx += e.touches[0].clientX - lastPan.x
|
|
178
|
+
state.ty += e.touches[0].clientY - lastPan.y
|
|
179
|
+
lastPan = { x: e.touches[0].clientX, y: e.touches[0].clientY }
|
|
180
|
+
clampTranslate()
|
|
181
|
+
}
|
|
182
|
+
// scale === 1 → kein preventDefault() → Carousel-Swipe funktioniert
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function fitToContainer() {
|
|
186
|
+
const scaleX = _containerW / _imgW
|
|
187
|
+
const scaleY = _containerH / _imgH
|
|
188
|
+
state.scale = Math.min(scaleX, scaleY)
|
|
189
|
+
state.tx = 0 // center/center zentriert automatisch
|
|
190
|
+
state.ty = 0
|
|
191
|
+
}
|
|
192
|
+
// ── Translate begrenzen (Bild darf Container nicht verlassen) ─────────
|
|
193
|
+
/*
|
|
194
|
+
function setDimensions(cw: number, ch: number, iw: number, ih: number) {
|
|
195
|
+
_containerW = cw; _containerH = ch; _imgW = iw; _imgH = ih
|
|
196
|
+
}
|
|
197
|
+
*/
|
|
198
|
+
function setDimensions(cw: number, ch: number, iw: number, ih: number) {
|
|
199
|
+
const wasInitialized = _containerW > 0
|
|
200
|
+
|
|
201
|
+
if (wasInitialized) {
|
|
202
|
+
// Bildmitte in center/center-Koordinaten — tx/ty ist bereits relativ zur Mitte
|
|
203
|
+
const cx = state.tx / state.scale
|
|
204
|
+
const cy = state.ty / state.scale
|
|
205
|
+
|
|
206
|
+
const fitScale = Math.min(_containerW / _imgW, _containerH / _imgH)
|
|
207
|
+
if (state.scale < fitScale) state.scale = fitScale
|
|
208
|
+
|
|
209
|
+
state.tx = cx * state.scale
|
|
210
|
+
state.ty = cy * state.scale
|
|
211
|
+
clampTranslate()
|
|
212
|
+
} else {
|
|
213
|
+
_containerW = cw; _containerH = ch; _imgW = iw; _imgH = ih
|
|
214
|
+
fitToContainer()
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
/*
|
|
218
|
+
function clampTranslate() {
|
|
219
|
+
const scaledW = _imgW * state.scale
|
|
220
|
+
const scaledH = _imgH * state.scale
|
|
221
|
+
const maxTx = Math.max(0, (scaledW - _containerW) / 2)
|
|
222
|
+
const maxTy = Math.max(0, (scaledH - _containerH) / 2)
|
|
223
|
+
console.log (`before scale:${state.scale} x:${state.tx} y:${state.ty}` )
|
|
224
|
+
state.tx = Math.min(maxTx, Math.max(-maxTx, state.tx))
|
|
225
|
+
state.ty = Math.min(maxTy, Math.max(-maxTy, state.ty))
|
|
226
|
+
console.log (`after scale:${state.scale} x:${state.tx} y:${state.ty}` )
|
|
227
|
+
}
|
|
228
|
+
*/
|
|
229
|
+
function clampTranslate() {
|
|
230
|
+
const scaledW = _imgW * state.scale
|
|
231
|
+
const scaledH = _imgH * state.scale
|
|
232
|
+
|
|
233
|
+
console.log (`before scale:${state.scale} x:${state.tx} y:${state.ty}` )
|
|
234
|
+
if (scaledW >= _containerW) {
|
|
235
|
+
const maxTx = (scaledW - _containerW) / 2
|
|
236
|
+
state.tx = Math.min(maxTx, Math.max(-maxTx, state.tx))
|
|
237
|
+
} else {
|
|
238
|
+
state.tx = 0 // kleiner als Container → zentriert = 0
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (scaledH >= _containerH) {
|
|
242
|
+
const maxTy = (scaledH - _containerH) / 2
|
|
243
|
+
state.ty = Math.min(maxTy, Math.max(-maxTy, state.ty))
|
|
244
|
+
} else {
|
|
245
|
+
state.ty = 0
|
|
246
|
+
}
|
|
247
|
+
console.log (`after scale:${state.scale} x:${state.tx} y:${state.ty}` )
|
|
248
|
+
}
|
|
249
|
+
</script>
|
|
250
|
+
|
|
251
|
+
<template>
|
|
252
|
+
<div
|
|
253
|
+
ref="containerRef"
|
|
254
|
+
class="zoom-pan-container"
|
|
255
|
+
:style="{ cursor }"
|
|
256
|
+
@wheel.prevent="onWheel"
|
|
257
|
+
@touchstart="onTouchStart"
|
|
258
|
+
@touchmove="onTouchMove"
|
|
259
|
+
@mousedown="onMouseDown"
|
|
260
|
+
>
|
|
261
|
+
<!--
|
|
262
|
+
<div :style="{ transform, transformOrigin: 'center center', width: contentW + 'px', height: contentH + 'px' }">
|
|
263
|
+
<slot />
|
|
264
|
+
</div>
|
|
265
|
+
-->
|
|
266
|
+
<!-- Dieses div ist immer 100% des Containers — transformOrigin: center center stimmt -->
|
|
267
|
+
<div :style="{ transform, transformOrigin: 'center center', width: '100%', height: '100%' }">
|
|
268
|
+
<!-- Slot-Inhalt absolut zentriert -->
|
|
269
|
+
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%)">
|
|
270
|
+
<slot />
|
|
271
|
+
</div>
|
|
272
|
+
</div>
|
|
273
|
+
</div>
|
|
274
|
+
</template>
|
|
275
|
+
|
|
276
|
+
<style scoped>
|
|
277
|
+
.zoom-pan-container {
|
|
278
|
+
overflow: hidden;
|
|
279
|
+
width: 100%;
|
|
280
|
+
height: 100%;
|
|
281
|
+
}
|
|
282
|
+
</style>
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
import PhotoMetaData from '../components/PhotoMetaData.vue';
|
|
12
12
|
import PhotoMap from '../components/PhotoMap.vue';
|
|
13
13
|
import Mail from '@christianriedl/office/src/components/Mail.vue';
|
|
14
|
+
import ImageZoomer from "@christianriedl/media/src/components/ImageZoomer.vue";
|
|
14
15
|
|
|
15
16
|
const router = useRouter();
|
|
16
17
|
const route = useRoute();
|
|
@@ -42,9 +43,15 @@
|
|
|
42
43
|
const showSendMail = ref(false);
|
|
43
44
|
const attachment = ref("");
|
|
44
45
|
const smallAttachment = ref("");
|
|
46
|
+
const showName = ref(false);
|
|
45
47
|
const zoom = 14;
|
|
46
48
|
let album: IMediaFolder;
|
|
49
|
+
let currentItem: IPictureFile = {} as IPictureFile;
|
|
50
|
+
let currentHeight = 0;
|
|
51
|
+
let currentWidth = 0;
|
|
47
52
|
let origUrl = "";
|
|
53
|
+
let windowWidth = 0;
|
|
54
|
+
let windowHeight = 0;
|
|
48
55
|
let location: string;
|
|
49
56
|
let mouseDownTime: number = 0;
|
|
50
57
|
let mouseTimer: number = -1;
|
|
@@ -52,15 +59,18 @@
|
|
|
52
59
|
let nextIndex: number = -1;
|
|
53
60
|
let keepBlobs: boolean = true;
|
|
54
61
|
let mediaUrlLength = mediaService.mediaUrl.length;
|
|
55
|
-
const showName = ref(false);
|
|
56
62
|
|
|
57
63
|
watch([appState.bodyHeight, appState.pageWidth], () => {
|
|
64
|
+
windowHeight = window.screen.height;
|
|
65
|
+
windowWidth = window.screen.width;
|
|
58
66
|
width.value = appState.pageWidth.value;
|
|
59
67
|
height.value = appState.bodyHeight.value;
|
|
60
68
|
landscape.value = width.value > height.value;
|
|
61
69
|
}, { immediate: true });
|
|
62
70
|
|
|
63
71
|
async function start(): Promise<boolean> {
|
|
72
|
+
windowHeight = window.screen.height;
|
|
73
|
+
windowWidth = window.screen.width;
|
|
64
74
|
window.document.addEventListener('keydown', onKey);
|
|
65
75
|
appState.navigate.value = onNavigate;
|
|
66
76
|
if (carousel.value)
|
|
@@ -97,7 +107,7 @@
|
|
|
97
107
|
items.splice(0, items.length, ...list);
|
|
98
108
|
if (route.query.start) {
|
|
99
109
|
const start = route.query.start.toString();
|
|
100
|
-
const idx = items.findIndex((
|
|
110
|
+
const idx = items.findIndex((it) => it.dlnaid == start);
|
|
101
111
|
if (idx >= 0) {
|
|
102
112
|
index.value = idx;
|
|
103
113
|
}
|
|
@@ -246,11 +256,10 @@
|
|
|
246
256
|
showInfo.value = false;
|
|
247
257
|
}
|
|
248
258
|
else {
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
headerText.value = "Metadaten - " + item.name;
|
|
259
|
+
const folder = album ? album : mediaService.getFolder(currentItem.dlnaParentId);
|
|
260
|
+
origUrl = mediaService.getPhotoUrl(folder.url, currentItem.url, '0x0x0');
|
|
261
|
+
hasCoordinates.value = !!(currentItem.flags & EPictureFlags.Has_Gps);
|
|
262
|
+
headerText.value = "Metadaten - " + currentItem.name;
|
|
254
263
|
showInfo.value = true;
|
|
255
264
|
}
|
|
256
265
|
break;
|
|
@@ -318,11 +327,10 @@
|
|
|
318
327
|
showInfo.value = false;
|
|
319
328
|
}
|
|
320
329
|
async function onSendEmail() {
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
const info = await mediaService.getMediaInfo(item.dlnaParentId, item.dlnaid);
|
|
330
|
+
smallAttachment.value = getUrl(currentItem);
|
|
331
|
+
const info = await mediaService.getMediaInfo(currentItem.dlnaParentId, currentItem.dlnaid);
|
|
324
332
|
if (!info)
|
|
325
|
-
window.alert("Not found: " +
|
|
333
|
+
window.alert("Not found: " + currentItem.url);
|
|
326
334
|
else {
|
|
327
335
|
attachment.value = info.name;
|
|
328
336
|
showSendMail.value = true;
|
|
@@ -339,26 +347,36 @@
|
|
|
339
347
|
function onUpdate() { // Carousel updated
|
|
340
348
|
showInfo.value = false;
|
|
341
349
|
clearName();
|
|
342
|
-
|
|
350
|
+
currentItem = items[index.value];
|
|
351
|
+
if (MediaHelper.mustRotate(currentItem)) {
|
|
352
|
+
currentWidth = currentItem.height;
|
|
353
|
+
currentHeight = currentItem.width;
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
356
|
+
currentWidth = currentItem.width;
|
|
357
|
+
currentHeight = currentItem.height;
|
|
358
|
+
}
|
|
359
|
+
const folder = album ? album : mediaService.getFolder(currentItem.dlnaParentId);
|
|
360
|
+
origUrl = mediaService.getPhotoUrl(folder.url, currentItem.url, '0x0x0');
|
|
343
361
|
if (mediaAppConfig.useImagePreload) {
|
|
344
|
-
if (index.value != nextIndex && !
|
|
345
|
-
createBlob(
|
|
362
|
+
if (index.value != nextIndex && !currentItem.blob) {
|
|
363
|
+
createBlob(currentItem);
|
|
346
364
|
}
|
|
347
365
|
}
|
|
348
366
|
else {
|
|
349
|
-
|
|
367
|
+
currentItem.blob = getUrl(currentItem);
|
|
350
368
|
}
|
|
351
369
|
if (playerService.currentPlayer) {
|
|
352
370
|
const request = {
|
|
353
371
|
playerName: playerService.currentPlayer.playerName,
|
|
354
|
-
folderId:
|
|
355
|
-
mediaId:
|
|
372
|
+
folderId: currentItem.dlnaParentId,
|
|
373
|
+
mediaId: currentItem.dlnaid,
|
|
356
374
|
trackNo: -1,
|
|
357
375
|
withStreamTitle: false
|
|
358
376
|
};
|
|
359
377
|
const rc = playerService!.play(request).then(() => { });
|
|
360
378
|
}
|
|
361
|
-
nameTimer = window.setTimeout(() => { onLoad(
|
|
379
|
+
nameTimer = window.setTimeout(() => { onLoad(currentItem); }, 200);
|
|
362
380
|
}
|
|
363
381
|
function onLoad(item: IPictureFile) { // img loaded or cached
|
|
364
382
|
if (mediaAppConfig.useImagePreload) {
|
|
@@ -391,8 +409,11 @@
|
|
|
391
409
|
</script>
|
|
392
410
|
|
|
393
411
|
<template>
|
|
394
|
-
<v-container fluid :style="heightStyle" class="bg-grey-darken-3 pa-0">
|
|
395
|
-
<v-
|
|
412
|
+
<v-container ref="carousel" fluid :style="heightStyle" class="bg-grey-darken-3 pa-0">
|
|
413
|
+
<image-zoomer v-if="appState.fullScreen.value" :contentW="currentWidth" :contentH="currentHeight" >
|
|
414
|
+
<img :src="origUrl" :width="currentWidth" :height="currentHeight" draggable="false">
|
|
415
|
+
</image-zoomer>
|
|
416
|
+
<v-carousel v-else hide-delimiters :show-arrows="false" v-model="index"
|
|
396
417
|
:width="width" :height="height" :cycle="cycle" :interval="interval"
|
|
397
418
|
@click="onClick" @mousedown="onMouseDown" @update:modelValue="onUpdate">
|
|
398
419
|
<v-carousel-item v-for="item in items" :key="item.dlnaid" :src="item.blob" :alt="item.name" :width="getWidth(item)" :height="getHeight(item)"
|