feat: 文章图片点击缩放
点击图片全屏遮罩放大,Esc 或点击遮罩关闭,纯自定义实现无额外依赖。 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -5,17 +5,91 @@ const props = defineProps<{
|
||||
}>()
|
||||
|
||||
const route = useRoute()
|
||||
const isZoomed = ref(false)
|
||||
|
||||
const resolvedSrc = computed(() => {
|
||||
if (!props.src) return ''
|
||||
// 绝对路径直接用
|
||||
if (props.src.startsWith('/') || props.src.startsWith('http')) return props.src
|
||||
// 相对路径:基于当前路由拼接
|
||||
const base = route.path.replace(/\/$/, '')
|
||||
return `${base}/${props.src}`
|
||||
})
|
||||
|
||||
function open() {
|
||||
isZoomed.value = true
|
||||
}
|
||||
|
||||
function close() {
|
||||
isZoomed.value = false
|
||||
}
|
||||
|
||||
function onKeydown(e: KeyboardEvent) {
|
||||
if (e.key === 'Escape') close()
|
||||
}
|
||||
|
||||
onMounted(() => document.addEventListener('keydown', onKeydown))
|
||||
onUnmounted(() => document.removeEventListener('keydown', onKeydown))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<img :src="resolvedSrc" :alt="alt" loading="lazy" decoding="async" />
|
||||
<img
|
||||
:src="resolvedSrc"
|
||||
:alt="alt"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
class="zoomable-img"
|
||||
@click="open"
|
||||
/>
|
||||
|
||||
<ClientOnly>
|
||||
<Teleport to="body">
|
||||
<Transition name="zoom-fade">
|
||||
<div
|
||||
v-if="isZoomed"
|
||||
class="zoom-overlay"
|
||||
@click="close"
|
||||
>
|
||||
<img
|
||||
:src="resolvedSrc"
|
||||
:alt="alt"
|
||||
class="zoom-img"
|
||||
/>
|
||||
</div>
|
||||
</Transition>
|
||||
</Teleport>
|
||||
</ClientOnly>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.zoomable-img {
|
||||
cursor: zoom-in;
|
||||
}
|
||||
|
||||
.zoom-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 9999;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
cursor: zoom-out;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.zoom-img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
object-fit: contain;
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
||||
.zoom-fade-enter-active,
|
||||
.zoom-fade-leave-active {
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.zoom-fade-enter-from,
|
||||
.zoom-fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user