user_handbook/docs/.vuepress/components/longPic.vue

232 lines
5.5 KiB
Vue
Raw Permalink Normal View History

2026-03-30 10:23:03 +08:00
<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>