Drag to move in file preview

This commit is contained in:
2025-11-29 23:12:25 +08:00
parent be35bd24c2
commit 72ca5e9b77
2 changed files with 94 additions and 28 deletions

View File

@@ -68,13 +68,21 @@
@touchstart="handleTouchStart" @touchstart="handleTouchStart"
@touchmove="handleTouchMove" @touchmove="handleTouchMove"
@touchend="handleTouchEnd" @touchend="handleTouchEnd"
@mousedown="handleMouseDown"
@mousemove="handleMouseMove"
@mouseup="handleMouseUp"
@mouseleave="handleMouseUp"
@dblclick="handleDoubleClick" @dblclick="handleDoubleClick"
> >
<img <img
v-if="fileType === 'image'" v-if="fileType === 'image'"
:src="fileSource" :src="fileSource"
class="preview-image" class="preview-image"
:style="{ transform: `scale(${zoomLevel})` }" :style="{
transform: `translate(${translateX}px, ${translateY}px) scale(${zoomLevel})`,
cursor:
zoomLevel > 1 ? (isDragging ? 'grabbing' : 'grab') : 'default'
}"
alt="Image preview" alt="Image preview"
/> />
<video <video
@@ -251,6 +259,13 @@ const zoomLevel = ref<number>(1)
const initialDistance = ref<number>(0) const initialDistance = ref<number>(0)
const isPinching = ref<boolean>(false) const isPinching = ref<boolean>(false)
// Drag functionality
const translateX = ref(0)
const translateY = ref(0)
const isDragging = ref(false)
const startX = ref(0)
const startY = ref(0)
// View transition state // View transition state
const isTransitioning = ref<boolean>(false) const isTransitioning = ref<boolean>(false)
const transitionImage = ref<string>("") const transitionImage = ref<string>("")
@@ -397,52 +412,101 @@ function handleZoom(event: WheelEvent) {
event.preventDefault() event.preventDefault()
const delta = event.deltaY > 0 ? -0.1 : 0.1 const delta = event.deltaY > 0 ? -0.1 : 0.1
zoomLevel.value = Math.max(0.1, Math.min(5, zoomLevel.value + delta)) const newZoom = Math.max(0.1, Math.min(5, zoomLevel.value + delta))
if (newZoom <= 1) {
translateX.value = 0
translateY.value = 0
}
zoomLevel.value = newZoom
} }
function handleTouchStart(event: TouchEvent) { function handleTouchStart(event: TouchEvent) {
if (fileType.value !== "image" || event.touches.length !== 2) return if (fileType.value !== "image") return
event.preventDefault() if (event.touches.length === 2) {
isPinching.value = true event.preventDefault()
const touch1 = event.touches[0]! isPinching.value = true
const touch2 = event.touches[1]! const touch1 = event.touches[0]!
initialDistance.value = Math.sqrt( const touch2 = event.touches[1]!
Math.pow(touch2.clientX - touch1.clientX, 2) + initialDistance.value = Math.sqrt(
Math.pow(touch2.clientY - touch1.clientY, 2) Math.pow(touch2.clientX - touch1.clientX, 2) +
) Math.pow(touch2.clientY - touch1.clientY, 2)
)
} else if (event.touches.length === 1 && zoomLevel.value > 1) {
isDragging.value = true
startX.value = event.touches[0]!.clientX - translateX.value
startY.value = event.touches[0]!.clientY - translateY.value
}
} }
function handleTouchMove(event: TouchEvent) { function handleTouchMove(event: TouchEvent) {
if ( if (fileType.value !== "image") return
fileType.value !== "image" ||
!isPinching.value ||
event.touches.length !== 2
)
return
event.preventDefault() if (isPinching.value && event.touches.length === 2) {
const touch1 = event.touches[0]! event.preventDefault()
const touch2 = event.touches[1]! const touch1 = event.touches[0]!
const currentDistance = Math.sqrt( const touch2 = event.touches[1]!
Math.pow(touch2.clientX - touch1.clientX, 2) + const currentDistance = Math.sqrt(
Math.pow(touch2.clientY - touch1.clientY, 2) Math.pow(touch2.clientX - touch1.clientX, 2) +
) Math.pow(touch2.clientY - touch1.clientY, 2)
)
const scale = currentDistance / initialDistance.value const scale = currentDistance / initialDistance.value
zoomLevel.value = Math.max(0.1, Math.min(5, zoomLevel.value * scale)) const newZoom = Math.max(0.1, Math.min(5, zoomLevel.value * scale))
if (newZoom <= 1) {
translateX.value = 0
translateY.value = 0
}
zoomLevel.value = newZoom
} else if (isDragging.value && event.touches.length === 1) {
event.preventDefault()
translateX.value = event.touches[0]!.clientX - startX.value
translateY.value = event.touches[0]!.clientY - startY.value
}
} }
function handleTouchEnd(_event: TouchEvent) { function handleTouchEnd(_event: TouchEvent) {
if (fileType.value !== "image") return if (fileType.value !== "image") return
isPinching.value = false isPinching.value = false
isDragging.value = false
}
function handleMouseDown(event: MouseEvent) {
if (fileType.value !== "image" || zoomLevel.value <= 1) return
event.preventDefault()
isDragging.value = true
startX.value = event.clientX - translateX.value
startY.value = event.clientY - translateY.value
}
function handleMouseMove(event: MouseEvent) {
if (!isDragging.value) return
event.preventDefault()
translateX.value = event.clientX - startX.value
translateY.value = event.clientY - startY.value
}
function handleMouseUp() {
isDragging.value = false
} }
function handleDoubleClick() { function handleDoubleClick() {
if (fileType.value !== "image") return if (fileType.value !== "image") return
zoomLevel.value = zoomLevel.value > 1 ? 1 : 2 if (zoomLevel.value > 1) {
zoomLevel.value = 1
translateX.value = 0
translateY.value = 0
} else {
zoomLevel.value = 2
}
} }
const breakpoints = useBreakpoints(breakpointsTailwind) const breakpoints = useBreakpoints(breakpointsTailwind)

View File

@@ -49,6 +49,8 @@
"@types/node": "^24.10.1", "@types/node": "^24.10.1",
"daisyui": "^5.5.5", "daisyui": "^5.5.5",
"naive-ui": "^2.43.2", "naive-ui": "^2.43.2",
"tailwindcss": "^4.1.17" "tailwindcss": "^4.1.17",
"unplugin-auto-import": "^20.3.0",
"unplugin-vue-components": "^30.0.0"
} }
} }