191 lines
4.2 KiB
Vue
191 lines
4.2 KiB
Vue
<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
|
||
}
|
||
})
|
||
|
||
const isExpanded = ref(false)
|
||
const showExpand = ref(false)
|
||
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'));
|
||
};
|
||
|
||
// 检查图片高度并通知外部
|
||
const checkImageHeight = (img) => {
|
||
if (typeof window === 'undefined') return
|
||
const naturalHeight = img.naturalHeight || img.offsetHeight
|
||
console.log(props.alt, naturalHeight, '图片实际高度')
|
||
// 仅初始化时判断是否显示展开按钮
|
||
showExpand.value = naturalHeight > props.maxHeight
|
||
isImageLoaded.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) => {
|
||
notifyImageLoaded()
|
||
})
|
||
if (imgRef.value) {
|
||
resizeObserver.value.observe(imgRef.value)
|
||
}
|
||
}
|
||
|
||
// 展开/收起切换
|
||
const toggleExpand = () => {
|
||
isExpanded.value = !isExpanded.value
|
||
setTimeout(() => notifyImageLoaded(), 100)
|
||
}
|
||
|
||
// 生命周期
|
||
onMounted(() => {
|
||
if (typeof window === 'undefined') return
|
||
const img = imgRef.value
|
||
if (img && img.complete) {
|
||
checkImageHeight(img)
|
||
}
|
||
observeResize()
|
||
})
|
||
|
||
onUnmounted(() => {
|
||
if (resizeObserver.value) {
|
||
resizeObserver.value.disconnect()
|
||
}
|
||
})
|
||
</script>
|
||
|
||
<template>
|
||
<div class="image-container">
|
||
<div
|
||
class="image-wrapper"
|
||
:class="{ 'expanded': isExpanded }"
|
||
:style="{
|
||
maxHeight: !isExpanded ? `${maxHeight}px` : 'none',
|
||
aspectRatio: !isExpanded ? '16/9' : 'unset' // 展开后取消宽高比限制
|
||
}"
|
||
>
|
||
<img
|
||
ref="imgRef"
|
||
:src="src"
|
||
:alt="alt"
|
||
@load="handleImageLoad"
|
||
class="responsive-image"
|
||
loading="eager"
|
||
decoding="async"
|
||
>
|
||
</div>
|
||
|
||
<!-- 按钮组容器 -->
|
||
<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;
|
||
width: 100%;
|
||
margin: 0 auto;
|
||
min-height: 100px; /* 兜底高度,防止加载前塌陷 */
|
||
}
|
||
|
||
.image-wrapper {
|
||
width: 100%;
|
||
overflow: hidden;
|
||
transition: max-height 0.3s ease-in-out;
|
||
position: relative;
|
||
/* 移除固定aspect-ratio,改为动态绑定 */
|
||
}
|
||
|
||
/* 未展开时添加渐变遮罩 */
|
||
.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>
|