Skip to content

Commit 60a3c4d

Browse files
committed
FaImagePreview 组件增加函数式调用
1 parent 00f7293 commit 60a3c4d

File tree

4 files changed

+170
-124
lines changed

4 files changed

+170
-124
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import Preview from './preview.vue'
2+
3+
export function useFaImagePreview() {
4+
function open(src: string) {
5+
const container = document.createElement('div')
6+
const app = createApp({
7+
render() {
8+
return h(Preview, {
9+
modelValue: true,
10+
src,
11+
})
12+
},
13+
})
14+
app.mount(container)
15+
}
16+
17+
return {
18+
open,
19+
}
20+
}
Lines changed: 2 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script setup lang="ts">
22
import type { HTMLAttributes } from 'vue'
33
import { cn } from '@/utils'
4-
import { Dialog, DialogContent } from './dialog'
4+
import Preview from './preview.vue'
55
66
defineOptions({
77
name: 'FaImagePreview',
@@ -21,13 +21,6 @@ const isLoading = ref(true)
2121
const isError = ref(false)
2222
const isOpen = ref(false)
2323
24-
// 图片操作相关状态
25-
const scale = ref(1)
26-
const rotate = ref(0)
27-
const position = ref({ x: 0, y: 0 })
28-
const isDragging = ref(false)
29-
const dragStart = ref({ x: 0, y: 0 })
30-
3124
function handleLoad() {
3225
isLoading.value = false
3326
emits('load')
@@ -44,87 +37,6 @@ function handleClick() {
4437
isOpen.value = true
4538
}
4639
}
47-
48-
// 图片操作函数
49-
function handleZoomIn() {
50-
scale.value = Math.min(scale.value + 0.25, 3)
51-
}
52-
53-
function handleZoomOut() {
54-
scale.value = Math.max(scale.value - 0.25, 0.5)
55-
}
56-
57-
function handleOriginalSize() {
58-
scale.value = 1
59-
}
60-
61-
function handleRotateLeft() {
62-
rotate.value = rotate.value - 90
63-
}
64-
65-
function handleRotateRight() {
66-
rotate.value = rotate.value + 90
67-
}
68-
69-
// 处理滚轮缩放
70-
function handleWheel(e: WheelEvent) {
71-
e.preventDefault()
72-
if (e.deltaY < 0) {
73-
handleZoomIn()
74-
}
75-
else {
76-
handleZoomOut()
77-
}
78-
}
79-
80-
// 处理鼠标按下事件
81-
function handleMouseDown(e: MouseEvent) {
82-
isDragging.value = true
83-
dragStart.value = {
84-
x: e.clientX - position.value.x,
85-
y: e.clientY - position.value.y,
86-
}
87-
}
88-
89-
// 处理鼠标移动事件
90-
function handleMouseMove(e: MouseEvent) {
91-
if (!isDragging.value) {
92-
return
93-
}
94-
position.value = {
95-
x: e.clientX - dragStart.value.x,
96-
y: e.clientY - dragStart.value.y,
97-
}
98-
}
99-
100-
// 处理鼠标松开事件
101-
function handleMouseUp() {
102-
isDragging.value = false
103-
}
104-
105-
// 重置图片状态
106-
function resetImageState() {
107-
scale.value = 1
108-
rotate.value = 0
109-
position.value = { x: 0, y: 0 }
110-
}
111-
112-
// 监听鼠标事件
113-
onMounted(() => {
114-
window.addEventListener('mousemove', handleMouseMove)
115-
window.addEventListener('mouseup', handleMouseUp)
116-
})
117-
118-
onUnmounted(() => {
119-
window.removeEventListener('mousemove', handleMouseMove)
120-
window.removeEventListener('mouseup', handleMouseUp)
121-
})
122-
123-
function handleAnimationEnd() {
124-
if (!isOpen.value) {
125-
resetImageState()
126-
}
127-
}
12840
</script>
12941

13042
<template>
@@ -148,40 +60,6 @@ function handleAnimationEnd() {
14860
<FaIcon name="i-ph:image-broken-duotone" class="size-8 text-secondary-foreground/50" />
14961
</slot>
15062
</div>
151-
<Dialog v-model:open="isOpen">
152-
<DialogContent class="size-full" @animation-end="handleAnimationEnd">
153-
<div class="relative size-full" @wheel="handleWheel">
154-
<img
155-
:src="src"
156-
class="mx-auto max-h-full max-w-full object-contain"
157-
:class="{
158-
'transition-all duration-300': !isDragging,
159-
}"
160-
:style="{
161-
transform: `translate(${position.x}px, ${position.y}px) scale(${scale}) rotate(${rotate}deg)`,
162-
cursor: isDragging ? 'grabbing' : 'grab',
163-
}"
164-
@mousedown.prevent="handleMouseDown"
165-
>
166-
<FaButtonGroup class="absolute bottom-4 left-1/2 scale-125 -translate-x-1/2">
167-
<FaButton variant="outline" size="icon" class="border-none bg-muted/50" @click="handleZoomIn">
168-
<FaIcon name="i-carbon:zoom-in" class="size-5" />
169-
</FaButton>
170-
<FaButton variant="outline" size="icon" class="border-none bg-muted/50" @click="handleZoomOut">
171-
<FaIcon name="i-carbon:zoom-out" class="size-5" />
172-
</FaButton>
173-
<FaButton variant="outline" size="icon" class="border-none bg-muted/50" @click="handleOriginalSize">
174-
<FaIcon name="i-lucide:maximize" class="size-5" />
175-
</FaButton>
176-
<FaButton variant="outline" size="icon" class="border-none bg-muted/50" @click="handleRotateLeft">
177-
<FaIcon name="i-carbon:rotate" class="size-5" />
178-
</FaButton>
179-
<FaButton variant="outline" size="icon" class="border-none bg-muted/50" @click="handleRotateRight">
180-
<FaIcon name="i-carbon:rotate-180" class="size-5" />
181-
</FaButton>
182-
</FaButtonGroup>
183-
</div>
184-
</DialogContent>
185-
</Dialog>
63+
<Preview v-model="isOpen" :src />
18664
</div>
18765
</template>
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
<script setup lang="ts">
2+
import { Dialog, DialogContent } from './dialog'
3+
4+
defineProps<{
5+
src: string
6+
}>()
7+
8+
const isOpen = defineModel<boolean>()
9+
10+
// 图片操作相关状态
11+
const scale = ref(1)
12+
const rotate = ref(0)
13+
const position = ref({ x: 0, y: 0 })
14+
const isDragging = ref(false)
15+
const dragStart = ref({ x: 0, y: 0 })
16+
17+
// 图片操作函数
18+
function handleZoomIn() {
19+
scale.value = Math.min(scale.value + 0.25, 3)
20+
}
21+
22+
function handleZoomOut() {
23+
scale.value = Math.max(scale.value - 0.25, 0.5)
24+
}
25+
26+
function handleOriginalSize() {
27+
scale.value = 1
28+
}
29+
30+
function handleRotateLeft() {
31+
rotate.value = rotate.value - 90
32+
}
33+
34+
function handleRotateRight() {
35+
rotate.value = rotate.value + 90
36+
}
37+
38+
// 处理滚轮缩放
39+
function handleWheel(e: WheelEvent) {
40+
e.preventDefault()
41+
if (e.deltaY < 0) {
42+
handleZoomIn()
43+
}
44+
else {
45+
handleZoomOut()
46+
}
47+
}
48+
49+
// 处理鼠标按下事件
50+
function handleMouseDown(e: MouseEvent) {
51+
isDragging.value = true
52+
dragStart.value = {
53+
x: e.clientX - position.value.x,
54+
y: e.clientY - position.value.y,
55+
}
56+
}
57+
58+
// 处理鼠标移动事件
59+
function handleMouseMove(e: MouseEvent) {
60+
if (!isDragging.value) {
61+
return
62+
}
63+
position.value = {
64+
x: e.clientX - dragStart.value.x,
65+
y: e.clientY - dragStart.value.y,
66+
}
67+
}
68+
69+
// 处理鼠标松开事件
70+
function handleMouseUp() {
71+
isDragging.value = false
72+
}
73+
74+
// 重置图片状态
75+
function resetImageState() {
76+
scale.value = 1
77+
rotate.value = 0
78+
position.value = { x: 0, y: 0 }
79+
}
80+
81+
// 监听鼠标事件
82+
onMounted(() => {
83+
window.addEventListener('mousemove', handleMouseMove)
84+
window.addEventListener('mouseup', handleMouseUp)
85+
})
86+
87+
onUnmounted(() => {
88+
window.removeEventListener('mousemove', handleMouseMove)
89+
window.removeEventListener('mouseup', handleMouseUp)
90+
})
91+
92+
function handleAnimationEnd() {
93+
if (!isOpen.value) {
94+
resetImageState()
95+
}
96+
}
97+
</script>
98+
99+
<template>
100+
<Dialog v-model:open="isOpen">
101+
<DialogContent class="size-full" @animation-end="handleAnimationEnd">
102+
<div class="relative size-full flex-center" @wheel="handleWheel">
103+
<img
104+
:src="src"
105+
class="mx-auto max-h-full max-w-full object-contain"
106+
:class="{
107+
'transition-all duration-300': !isDragging,
108+
}"
109+
:style="{
110+
transform: `translate(${position.x}px, ${position.y}px) scale(${scale}) rotate(${rotate}deg)`,
111+
cursor: isDragging ? 'grabbing' : 'grab',
112+
}"
113+
@mousedown.prevent="handleMouseDown"
114+
>
115+
<FaButtonGroup class="absolute bottom-4 left-1/2 scale-125 -translate-x-1/2">
116+
<FaButton variant="outline" size="icon" class="border-none bg-muted/50" @click="handleZoomIn">
117+
<FaIcon name="i-carbon:zoom-in" class="size-5" />
118+
</FaButton>
119+
<FaButton variant="outline" size="icon" class="border-none bg-muted/50" @click="handleZoomOut">
120+
<FaIcon name="i-carbon:zoom-out" class="size-5" />
121+
</FaButton>
122+
<FaButton variant="outline" size="icon" class="border-none bg-muted/50" @click="handleOriginalSize">
123+
<FaIcon name="i-lucide:maximize" class="size-5" />
124+
</FaButton>
125+
<FaButton variant="outline" size="icon" class="border-none bg-muted/50" @click="handleRotateLeft">
126+
<FaIcon name="i-carbon:rotate" class="size-5" />
127+
</FaButton>
128+
<FaButton variant="outline" size="icon" class="border-none bg-muted/50" @click="handleRotateRight">
129+
<FaIcon name="i-carbon:rotate-180" class="size-5" />
130+
</FaButton>
131+
</FaButtonGroup>
132+
</div>
133+
</DialogContent>
134+
</Dialog>
135+
</template>

src/views/component_built_in_example/imagepreview.vue

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@ meta:
33
enabled: false
44
</route>
55

6+
<script setup lang="ts">
7+
import { useFaImagePreview } from '@/ui/components/FaImagePreview'
8+
9+
function open() {
10+
useFaImagePreview().open('https://fantastic-admin.hurui.me/logo.svg')
11+
}
12+
</script>
13+
614
<template>
715
<div>
816
<FaPageHeader title="图片预览" description="FaImagePreview" />
@@ -24,5 +32,10 @@ meta:
2432
</FaImagePreview>
2533
</div>
2634
</FaPageMain>
35+
<FaPageMain title="函数式调用">
36+
<FaButton @click="open">
37+
预览
38+
</FaButton>
39+
</FaPageMain>
2740
</div>
2841
</template>

0 commit comments

Comments
 (0)