feat: FaImagePreview 组件增加函数式调用

This commit is contained in:
Hooray
2025-07-02 01:45:44 +08:00
parent cc9bb1a194
commit ccb8bd9a18
3 changed files with 157 additions and 124 deletions
+20
View File
@@ -0,0 +1,20 @@
import Preview from './preview.vue'
export function useFaImagePreview() {
function open(src: string) {
const container = document.createElement('div')
const app = createApp({
render() {
return h(Preview, {
modelValue: true,
src,
})
},
})
app.mount(container)
}
return {
open,
}
}
+2 -124
View File
@@ -1,7 +1,7 @@
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { cn } from '@/utils'
import { Dialog, DialogContent } from './dialog'
import Preview from './preview.vue'
defineOptions({
name: 'FaImagePreview',
@@ -21,13 +21,6 @@ const isLoading = ref(true)
const isError = ref(false)
const isOpen = ref(false)
// 图片操作相关状态
const scale = ref(1)
const rotate = ref(0)
const position = ref({ x: 0, y: 0 })
const isDragging = ref(false)
const dragStart = ref({ x: 0, y: 0 })
function handleLoad() {
isLoading.value = false
emits('load')
@@ -44,87 +37,6 @@ function handleClick() {
isOpen.value = true
}
}
// 图片操作函数
function handleZoomIn() {
scale.value = Math.min(scale.value + 0.25, 3)
}
function handleZoomOut() {
scale.value = Math.max(scale.value - 0.25, 0.5)
}
function handleOriginalSize() {
scale.value = 1
}
function handleRotateLeft() {
rotate.value = rotate.value - 90
}
function handleRotateRight() {
rotate.value = rotate.value + 90
}
// 处理滚轮缩放
function handleWheel(e: WheelEvent) {
e.preventDefault()
if (e.deltaY < 0) {
handleZoomIn()
}
else {
handleZoomOut()
}
}
// 处理鼠标按下事件
function handleMouseDown(e: MouseEvent) {
isDragging.value = true
dragStart.value = {
x: e.clientX - position.value.x,
y: e.clientY - position.value.y,
}
}
// 处理鼠标移动事件
function handleMouseMove(e: MouseEvent) {
if (!isDragging.value) {
return
}
position.value = {
x: e.clientX - dragStart.value.x,
y: e.clientY - dragStart.value.y,
}
}
// 处理鼠标松开事件
function handleMouseUp() {
isDragging.value = false
}
// 重置图片状态
function resetImageState() {
scale.value = 1
rotate.value = 0
position.value = { x: 0, y: 0 }
}
// 监听鼠标事件
onMounted(() => {
window.addEventListener('mousemove', handleMouseMove)
window.addEventListener('mouseup', handleMouseUp)
})
onUnmounted(() => {
window.removeEventListener('mousemove', handleMouseMove)
window.removeEventListener('mouseup', handleMouseUp)
})
function handleAnimationEnd() {
if (!isOpen.value) {
resetImageState()
}
}
</script>
<template>
@@ -148,40 +60,6 @@ function handleAnimationEnd() {
<FaIcon name="i-ph:image-broken-duotone" class="size-8 text-secondary-foreground/50" />
</slot>
</div>
<Dialog v-model:open="isOpen">
<DialogContent class="size-full" @animation-end="handleAnimationEnd">
<div class="relative size-full" @wheel="handleWheel">
<img
:src="src"
class="mx-auto max-h-full max-w-full object-contain"
:class="{
'transition-all duration-300': !isDragging,
}"
:style="{
transform: `translate(${position.x}px, ${position.y}px) scale(${scale}) rotate(${rotate}deg)`,
cursor: isDragging ? 'grabbing' : 'grab',
}"
@mousedown.prevent="handleMouseDown"
>
<FaButtonGroup class="absolute bottom-4 left-1/2 scale-125 -translate-x-1/2">
<FaButton variant="outline" size="icon" class="border-none bg-muted/50" @click="handleZoomIn">
<FaIcon name="i-carbon:zoom-in" class="size-5" />
</FaButton>
<FaButton variant="outline" size="icon" class="border-none bg-muted/50" @click="handleZoomOut">
<FaIcon name="i-carbon:zoom-out" class="size-5" />
</FaButton>
<FaButton variant="outline" size="icon" class="border-none bg-muted/50" @click="handleOriginalSize">
<FaIcon name="i-lucide:maximize" class="size-5" />
</FaButton>
<FaButton variant="outline" size="icon" class="border-none bg-muted/50" @click="handleRotateLeft">
<FaIcon name="i-carbon:rotate" class="size-5" />
</FaButton>
<FaButton variant="outline" size="icon" class="border-none bg-muted/50" @click="handleRotateRight">
<FaIcon name="i-carbon:rotate-180" class="size-5" />
</FaButton>
</FaButtonGroup>
</div>
</DialogContent>
</Dialog>
<Preview v-model="isOpen" :src />
</div>
</template>
@@ -0,0 +1,135 @@
<script setup lang="ts">
import { Dialog, DialogContent } from './dialog'
defineProps<{
src: string
}>()
const isOpen = defineModel<boolean>()
// 图片操作相关状态
const scale = ref(1)
const rotate = ref(0)
const position = ref({ x: 0, y: 0 })
const isDragging = ref(false)
const dragStart = ref({ x: 0, y: 0 })
// 图片操作函数
function handleZoomIn() {
scale.value = Math.min(scale.value + 0.25, 3)
}
function handleZoomOut() {
scale.value = Math.max(scale.value - 0.25, 0.5)
}
function handleOriginalSize() {
scale.value = 1
}
function handleRotateLeft() {
rotate.value = rotate.value - 90
}
function handleRotateRight() {
rotate.value = rotate.value + 90
}
// 处理滚轮缩放
function handleWheel(e: WheelEvent) {
e.preventDefault()
if (e.deltaY < 0) {
handleZoomIn()
}
else {
handleZoomOut()
}
}
// 处理鼠标按下事件
function handleMouseDown(e: MouseEvent) {
isDragging.value = true
dragStart.value = {
x: e.clientX - position.value.x,
y: e.clientY - position.value.y,
}
}
// 处理鼠标移动事件
function handleMouseMove(e: MouseEvent) {
if (!isDragging.value) {
return
}
position.value = {
x: e.clientX - dragStart.value.x,
y: e.clientY - dragStart.value.y,
}
}
// 处理鼠标松开事件
function handleMouseUp() {
isDragging.value = false
}
// 重置图片状态
function resetImageState() {
scale.value = 1
rotate.value = 0
position.value = { x: 0, y: 0 }
}
// 监听鼠标事件
onMounted(() => {
window.addEventListener('mousemove', handleMouseMove)
window.addEventListener('mouseup', handleMouseUp)
})
onUnmounted(() => {
window.removeEventListener('mousemove', handleMouseMove)
window.removeEventListener('mouseup', handleMouseUp)
})
function handleAnimationEnd() {
if (!isOpen.value) {
resetImageState()
}
}
</script>
<template>
<Dialog v-model:open="isOpen">
<DialogContent class="size-full" @animation-end="handleAnimationEnd">
<div class="relative size-full flex-center" @wheel="handleWheel">
<img
:src="src"
class="mx-auto max-h-full max-w-full object-contain"
:class="{
'transition-all duration-300': !isDragging,
}"
:style="{
transform: `translate(${position.x}px, ${position.y}px) scale(${scale}) rotate(${rotate}deg)`,
cursor: isDragging ? 'grabbing' : 'grab',
}"
@mousedown.prevent="handleMouseDown"
>
<FaButtonGroup class="absolute bottom-4 left-1/2 scale-125 -translate-x-1/2">
<FaButton variant="outline" size="icon" class="border-none bg-muted/50" @click="handleZoomIn">
<FaIcon name="i-carbon:zoom-in" class="size-5" />
</FaButton>
<FaButton variant="outline" size="icon" class="border-none bg-muted/50" @click="handleZoomOut">
<FaIcon name="i-carbon:zoom-out" class="size-5" />
</FaButton>
<FaButton variant="outline" size="icon" class="border-none bg-muted/50" @click="handleOriginalSize">
<FaIcon name="i-lucide:maximize" class="size-5" />
</FaButton>
<FaButton variant="outline" size="icon" class="border-none bg-muted/50" @click="handleRotateLeft">
<FaIcon name="i-carbon:rotate" class="size-5" />
</FaButton>
<FaButton variant="outline" size="icon" class="border-none bg-muted/50" @click="handleRotateRight">
<FaIcon name="i-carbon:rotate-180" class="size-5" />
</FaButton>
</FaButtonGroup>
</div>
</DialogContent>
</Dialog>
</template>