user_handbook/docs/.vuepress/components/longPic.vue
2026-03-30 10:23:03 +08:00

232 lines
5.5 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup>
import { ref, onMounted, watch, onUnmounted, getCurrentInstance } from 'vue'
const props = defineProps({
src: {
type: String,
required: true
},
alt: {
type: String,
default: '图片'
},
maxHeight: {
type: Number,
default: 500
},
// 新增:外部可配置宽度(支持数字/字符串,如 300 | '300px' | '100%'
width: {
type: [Number, String],
default: '100%'
}
})
const isExpanded = ref(false)
const showExpand = ref(false) // 仅当高度超maxHeight时显示展开/收起按钮
const isImageLoaded = ref(false)
const imgRef = ref(null)
const resizeObserver = ref(null)
const instance = getCurrentInstance()
// 全局通知图片加载/尺寸变化
const notifyImageLoaded = () => {
if (instance?.appContext.config.globalProperties.$notifyImageLoaded) {
instance.appContext.config.globalProperties.$notifyImageLoaded();
}
document.dispatchEvent(new CustomEvent('longPicImageLoaded'));
};
// 检查图片高度并控制展开按钮显示
// 核心修改未超过maxHeight时不显示按钮也不处理任何高度限制逻辑
const checkImageHeight = (img) => {
if (typeof window === 'undefined') return
const naturalHeight = img.naturalHeight || img.offsetHeight
console.log(props.alt, naturalHeight, '图片实际高度')
// 关键逻辑仅当图片高度超过maxHeight时才显示展开按钮
showExpand.value = naturalHeight > props.maxHeight
isImageLoaded.value = true
// 未超高度时直接展开(取消高度限制)
if (!showExpand.value) {
isExpanded.value = true
}
notifyImageLoaded()
}
// 图片加载完成处理
const handleImageLoad = (event) => {
if (isImageLoaded.value) return
checkImageHeight(event.target)
}
// 监听元素尺寸变化
const observeResize = () => {
if (typeof window === 'undefined' || !window.ResizeObserver) return
resizeObserver.value = new ResizeObserver((entries) => {
// 尺寸变化时重新检查高度(适配窗口缩放等场景)
const img = imgRef.value
if (img && isImageLoaded.value) {
checkImageHeight(img)
}
notifyImageLoaded()
})
if (imgRef.value) {
resizeObserver.value.observe(imgRef.value)
}
}
// 展开/收起切换仅当showExpand为true时触发
const toggleExpand = () => {
if (!showExpand.value) return // 未超高度时不执行切换逻辑
isExpanded.value = !isExpanded.value
setTimeout(() => notifyImageLoaded(), 100)
}
// 格式化宽度值(处理数字/字符串输入)
const formatWidth = () => {
if (typeof props.width === 'number') {
return `${props.width}px`
}
return props.width
}
// 生命周期
onMounted(() => {
if (typeof window === 'undefined') return
const img = imgRef.value
if (img && img.complete) {
checkImageHeight(img)
}
observeResize()
})
onUnmounted(() => {
if (resizeObserver.value) {
resizeObserver.value.disconnect()
}
})
// 监听width属性变化重新通知尺寸更新
watch(() => props.width, () => {
if (isImageLoaded.value) {
notifyImageLoaded()
}
})
</script>
<template>
<div class="image-container" :style="{ width: formatWidth() }">
<!-- 核心修改未超高度时取消maxHeight和aspectRatio限制 -->
<div
class="image-wrapper"
:class="{ 'expanded': isExpanded }"
:style="{
// 仅当需要限制高度时才设置maxHeight
maxHeight: showExpand && !isExpanded ? `${maxHeight}px` : 'none',
// 仅当需要限制高度时才设置aspectRatio
aspectRatio: showExpand && !isExpanded ? '16/9' : 'unset'
}"
>
<img
ref="imgRef"
:src="src"
:alt="alt"
@load="handleImageLoad"
class="responsive-image"
loading="eager"
decoding="async"
>
</div>
<!-- 仅当showExpand为true时显示展开/收起按钮 -->
<div v-if="showExpand" class="hint-wrapper">
<div class="expand-hint" @click="toggleExpand" v-show="!isExpanded">
<span>点击查看完整图片</span>
</div>
<div class="collapse-hint" @click="toggleExpand" v-show="isExpanded">
<span>收起图片</span>
</div>
</div>
</div>
</template>
<style scoped>
.image-container {
position: relative;
margin: 0 auto;
min-height: 100px; /* 兜底高度,防止加载前塌陷 */
/* 宽度由外部配置控制 */
}
.image-wrapper {
width: 100%;
overflow: hidden;
transition: max-height 0.3s ease-in-out;
position: relative;
}
/* 仅当未展开且需要限制高度时显示渐变遮罩 */
.image-wrapper:not(.expanded)::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 80px;
background: linear-gradient(transparent, rgba(255,255,255,0.95));
pointer-events: none;
}
/* 展开后隐藏遮罩 */
.image-wrapper.expanded::after {
display: none;
}
.responsive-image {
width: 100%;
height: auto;
display: block;
object-fit: contain; /* 确保图片完整显示,不拉伸 */
}
/* 按钮容器 */
.hint-wrapper {
width: 100%;
}
.expand-hint, .collapse-hint {
text-align: center;
padding: 8px;
border-radius: 4px;
font-size: 14px;
cursor: pointer;
margin-top: 8px;
transition: background-color 0.3s;
}
.expand-hint {
background: rgba(64, 158, 255, 0.1);
color: #409eff;
}
.expand-hint:hover {
background: rgba(64, 158, 255, 0.2);
}
.collapse-hint {
background: rgba(103, 194, 58, 0.1);
color: #67c23a;
}
.collapse-hint:hover {
background: rgba(103, 194, 58, 0.2);
}
</style>