mirror of
https://github.com/fantastic-admin/basic.git
synced 2026-10-07 11:34:10 +08:00
FaImagePreview 组件增加函数式调用
This commit is contained in:
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -3,6 +3,14 @@ meta:
|
||||
enabled: false
|
||||
</route>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useFaImagePreview } from '@/ui/components/FaImagePreview'
|
||||
|
||||
function open() {
|
||||
useFaImagePreview().open('https://fantastic-admin.hurui.me/logo.svg')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<FaPageHeader title="图片预览" description="FaImagePreview" />
|
||||
@@ -24,5 +32,10 @@ meta:
|
||||
</FaImagePreview>
|
||||
</div>
|
||||
</FaPageMain>
|
||||
<FaPageMain title="函数式调用">
|
||||
<FaButton @click="open">
|
||||
预览
|
||||
</FaButton>
|
||||
</FaPageMain>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user