mirror of
https://github.com/Kuingsmile/PicList.git
synced 2026-05-06 20:42:57 +08:00
✨ Feature(custom): optimize image preview for gallery page
ISSUES CLOSED: #386
This commit is contained in:
@@ -312,6 +312,7 @@
|
|||||||
:alt="currentPreviewImage?.intro"
|
:alt="currentPreviewImage?.intro"
|
||||||
class="preview-image"
|
class="preview-image"
|
||||||
:style="imageTransformStyle"
|
:style="imageTransformStyle"
|
||||||
|
@load="onPreviewImageLoad"
|
||||||
@dragstart.prevent
|
@dragstart.prevent
|
||||||
@contextmenu.prevent
|
@contextmenu.prevent
|
||||||
/>
|
/>
|
||||||
@@ -674,10 +675,24 @@ const currentPreviewImage = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const imageTransformStyle = computed(() => {
|
const imageTransformStyle = computed(() => {
|
||||||
|
// Check if image overflows the viewport
|
||||||
|
const imageElement = previewImageRef.value
|
||||||
|
let isDraggable = false
|
||||||
|
|
||||||
|
if (imageElement && imageElement.naturalWidth && imageElement.naturalHeight) {
|
||||||
|
const viewerElement = imageElement.parentElement
|
||||||
|
if (viewerElement) {
|
||||||
|
const viewerRect = viewerElement.getBoundingClientRect()
|
||||||
|
const currentImageWidth = imageElement.naturalWidth * imagePreviewState.scale
|
||||||
|
const currentImageHeight = imageElement.naturalHeight * imagePreviewState.scale
|
||||||
|
isDraggable = currentImageWidth > viewerRect.width + 1 || currentImageHeight > viewerRect.height + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
transform: `translate(${imagePreviewState.translateX}px, ${imagePreviewState.translateY}px) scale(${imagePreviewState.scale})`,
|
transform: `translate(${imagePreviewState.translateX}px, ${imagePreviewState.translateY}px) scale(${imagePreviewState.scale})`,
|
||||||
cursor: imagePreviewState.isDragging ? 'grabbing' : imagePreviewState.scale > 1 ? 'grab' : 'default',
|
cursor: imagePreviewState.isDragging ? 'grabbing' : isDraggable ? 'grab' : 'default',
|
||||||
transition: imagePreviewState.isDragging ? 'none' : 'transform 0.2s ease-out'
|
transition: 'none'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -690,6 +705,12 @@ function onImageError(id: string) {
|
|||||||
imageErrorStates[id] = true
|
imageErrorStates[id] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onPreviewImageLoad() {
|
||||||
|
nextTick(() => {
|
||||||
|
resetImageTransform()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function togglePicBedDropdown(event?: Event) {
|
function togglePicBedDropdown(event?: Event) {
|
||||||
picBedDropdownOpen.value = !picBedDropdownOpen.value
|
picBedDropdownOpen.value = !picBedDropdownOpen.value
|
||||||
if (sortDropdownOpen.value) sortDropdownOpen.value = false
|
if (sortDropdownOpen.value) sortDropdownOpen.value = false
|
||||||
@@ -735,24 +756,60 @@ function navigateImage(direction: number) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function resetImageTransform() {
|
function resetImageTransform() {
|
||||||
imagePreviewState.scale = 1
|
const optimalScale = calculateOptimalScale()
|
||||||
|
imagePreviewState.scale = optimalScale
|
||||||
imagePreviewState.translateX = 0
|
imagePreviewState.translateX = 0
|
||||||
imagePreviewState.translateY = 0
|
imagePreviewState.translateY = 0
|
||||||
imagePreviewState.isDragging = false
|
imagePreviewState.isDragging = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function calculateOptimalScale(): number {
|
||||||
|
const imageElement = previewImageRef.value
|
||||||
|
if (!imageElement) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if (!imageElement.naturalWidth || !imageElement.naturalHeight) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
const viewerElement = imageElement.parentElement
|
||||||
|
if (!viewerElement) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
const viewerRect = viewerElement.getBoundingClientRect()
|
||||||
|
const viewerWidth = viewerRect.width
|
||||||
|
const viewerHeight = viewerRect.height
|
||||||
|
|
||||||
|
const imageWidth = imageElement.naturalWidth
|
||||||
|
const imageHeight = imageElement.naturalHeight
|
||||||
|
const scaleX = viewerWidth / imageWidth
|
||||||
|
const scaleY = viewerHeight / imageHeight
|
||||||
|
const optimalScale = Math.min(scaleX, scaleY, 1)
|
||||||
|
|
||||||
|
return optimalScale
|
||||||
|
}
|
||||||
|
|
||||||
function zoomIn() {
|
function zoomIn() {
|
||||||
const newScale = Math.min(imagePreviewState.scale * 1.2, 5)
|
zoomToScale(Math.min(imagePreviewState.scale * 1.2, 5))
|
||||||
imagePreviewState.scale = newScale
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function zoomOut() {
|
function zoomOut() {
|
||||||
const newScale = Math.max(imagePreviewState.scale / 1.2, 0.1)
|
const newScale = Math.max(imagePreviewState.scale / 1.2, 0.1)
|
||||||
|
zoomToScale(newScale)
|
||||||
|
}
|
||||||
|
|
||||||
|
function zoomToScale(newScale: number) {
|
||||||
|
const oldScale = imagePreviewState.scale
|
||||||
imagePreviewState.scale = newScale
|
imagePreviewState.scale = newScale
|
||||||
|
|
||||||
if (newScale === 1) {
|
const optimalScale = calculateOptimalScale()
|
||||||
|
if (newScale <= optimalScale) {
|
||||||
imagePreviewState.translateX = 0
|
imagePreviewState.translateX = 0
|
||||||
imagePreviewState.translateY = 0
|
imagePreviewState.translateY = 0
|
||||||
|
} else {
|
||||||
|
const scaleDiff = newScale / oldScale
|
||||||
|
imagePreviewState.translateX *= scaleDiff
|
||||||
|
imagePreviewState.translateY *= scaleDiff
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -763,12 +820,7 @@ function handleImageWheel(event: WheelEvent) {
|
|||||||
const newScale =
|
const newScale =
|
||||||
delta > 0 ? Math.min(imagePreviewState.scale * zoomFactor, 5) : Math.max(imagePreviewState.scale / zoomFactor, 0.1)
|
delta > 0 ? Math.min(imagePreviewState.scale * zoomFactor, 5) : Math.max(imagePreviewState.scale / zoomFactor, 0.1)
|
||||||
|
|
||||||
imagePreviewState.scale = newScale
|
zoomToScale(newScale)
|
||||||
|
|
||||||
if (newScale === 1) {
|
|
||||||
imagePreviewState.translateX = 0
|
|
||||||
imagePreviewState.translateY = 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleKeydown(event: KeyboardEvent) {
|
function handleKeydown(event: KeyboardEvent) {
|
||||||
@@ -802,7 +854,20 @@ function handleKeydown(event: KeyboardEvent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleImageMouseDown(event: MouseEvent) {
|
function handleImageMouseDown(event: MouseEvent) {
|
||||||
if (imagePreviewState.scale <= 1) {
|
const imageElement = previewImageRef.value
|
||||||
|
let isImageLargerThanViewer = false
|
||||||
|
|
||||||
|
if (imageElement && imageElement.naturalWidth && imageElement.naturalHeight) {
|
||||||
|
const viewerElement = imageElement.parentElement
|
||||||
|
if (viewerElement) {
|
||||||
|
const viewerRect = viewerElement.getBoundingClientRect()
|
||||||
|
const currentImageWidth = imageElement.naturalWidth * imagePreviewState.scale
|
||||||
|
const currentImageHeight = imageElement.naturalHeight * imagePreviewState.scale
|
||||||
|
isImageLargerThanViewer = currentImageWidth > viewerRect.width + 1 || currentImageHeight > viewerRect.height + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isImageLargerThanViewer) {
|
||||||
imagePreviewState.isSwipeMode = true
|
imagePreviewState.isSwipeMode = true
|
||||||
imagePreviewState.swipeStartX = event.clientX
|
imagePreviewState.swipeStartX = event.clientX
|
||||||
} else {
|
} else {
|
||||||
@@ -816,7 +881,7 @@ function handleImageMouseDown(event: MouseEvent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleImageMouseMove(event: MouseEvent) {
|
function handleImageMouseMove(event: MouseEvent) {
|
||||||
if (imagePreviewState.isDragging && imagePreviewState.scale > 1) {
|
if (imagePreviewState.isDragging) {
|
||||||
const deltaX = event.clientX - imagePreviewState.startX
|
const deltaX = event.clientX - imagePreviewState.startX
|
||||||
const deltaY = event.clientY - imagePreviewState.startY
|
const deltaY = event.clientY - imagePreviewState.startY
|
||||||
imagePreviewState.translateX = imagePreviewState.startTranslateX + deltaX
|
imagePreviewState.translateX = imagePreviewState.startTranslateX + deltaX
|
||||||
@@ -841,7 +906,20 @@ function handleImageMouseUp(event: MouseEvent) {
|
|||||||
|
|
||||||
function handleImageTouchStart(event: TouchEvent) {
|
function handleImageTouchStart(event: TouchEvent) {
|
||||||
const touch = event.touches[0]
|
const touch = event.touches[0]
|
||||||
if (imagePreviewState.scale <= 1) {
|
const imageElement = previewImageRef.value
|
||||||
|
let isImageLargerThanViewer = false
|
||||||
|
|
||||||
|
if (imageElement && imageElement.naturalWidth && imageElement.naturalHeight) {
|
||||||
|
const viewerElement = imageElement.parentElement
|
||||||
|
if (viewerElement) {
|
||||||
|
const viewerRect = viewerElement.getBoundingClientRect()
|
||||||
|
const currentImageWidth = imageElement.naturalWidth * imagePreviewState.scale
|
||||||
|
const currentImageHeight = imageElement.naturalHeight * imagePreviewState.scale
|
||||||
|
isImageLargerThanViewer = currentImageWidth > viewerRect.width + 1 || currentImageHeight > viewerRect.height + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isImageLargerThanViewer) {
|
||||||
imagePreviewState.isSwipeMode = true
|
imagePreviewState.isSwipeMode = true
|
||||||
imagePreviewState.swipeStartX = touch.clientX
|
imagePreviewState.swipeStartX = touch.clientX
|
||||||
} else {
|
} else {
|
||||||
@@ -855,7 +933,7 @@ function handleImageTouchStart(event: TouchEvent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleImageTouchMove(event: TouchEvent) {
|
function handleImageTouchMove(event: TouchEvent) {
|
||||||
if (imagePreviewState.isDragging && imagePreviewState.scale > 1) {
|
if (imagePreviewState.isDragging) {
|
||||||
const touch = event.touches[0]
|
const touch = event.touches[0]
|
||||||
const deltaX = touch.clientX - imagePreviewState.startX
|
const deltaX = touch.clientX - imagePreviewState.startX
|
||||||
const deltaY = touch.clientY - imagePreviewState.startY
|
const deltaY = touch.clientY - imagePreviewState.startY
|
||||||
|
|||||||
Reference in New Issue
Block a user