- props接了图片和放大倍率
- 首先用到了vueuse中的useMouseInElement 方法监听dom
- 获取到isOutside elementX elementY
- 还需要获取到未放大前的图片原始宽高
- 由于是从列表页打开详情组件,所以需要在load完成后中初始化 否则会导致图片未完全渲染获取不到动态的高度
- 这里需要深度监听isOutside elementX elementY才能获取到变化后的值
- watch中xy的值首先需要减去layer选中区域的宽高的一半,然后再去做边缘控制减二倍layer宽高
- 鼠标hover到缩略图上就进行切换图片的操作,注意切换后要重新init获取图片的宽高,监听 因为每张图片的宽高都是不同的
<template>
<div class="preview-img">
<div class="preview-img-content">
<div class="preview-img-content-contents">
<img ref="target" :src="imgList[currentIndex]" alt="" @load="imgLoad"/>
<!-- 移动遮罩 -->
<div
v-show="!isOutsideP"
class="layer"
:style="{ left: positionX + 'px', top: positionY + 'px' }"
></div>
</div>
</div>
<div
v-show="!isOutsideP"
class="zoom"
:style="[
{
backgroundImage: 'url(' + imgList[currentIndex] + ')',
backgroundPosition: `-${positionX * scale}px -${positionY * scale}px`,
height: imgHeight * 2 + 'px',
backgroundSize: `${imgWidth * scale}px ${imgHeight * scale}px`,
},
]"
></div>
<div class="preview-img-list">
<div
v-for="(item, index) in imgList"
:key="index"
class="preview-img-list-item"
:class="{ active: index === currentIndex }"
@mouseenter="mouseEnterChange(index)"
>
<img :src="item" alt="" />
</div>
</div>
</div>
</template>
<script setup>
import { useMouseInElement } from '@vueuse/core'
import { nextTick, onMounted } from 'vue'
const props = defineProps(['imgList', 'scale'])
const currentIndex = ref(0)
const target = ref(null)
//图片的宽高
const imgWidth = ref()
const imgHeight = ref()
const isOutside = ref(false) //是否在target外面
const elementX = ref(0) //在图片内x轴位置
const elementY = ref(0) //在图片内y轴位置
//初始化获取图片的宽高 加setTimeout是为了等待图片渲染完成
const init = () => {
nextTick(() => {
setTimeout(() => {
imgWidth.value = target.value.clientWidth
imgHeight.value = target.value.clientHeight
const mouseInfo = useMouseInElement(target)
isOutside.value = mouseInfo.isOutside
elementX.value = mouseInfo.elementX
elementY.value = mouseInfo.elementY
}, 200)
})
}
const imgLoad = () => {
init()
}
const isOutsideP = ref()
watch(
isOutside,
(n) => {
isOutsideP.value = n.value
},
{ deep: true }
)
// 处理x
const positionX = ref(0)
watch(
elementX,
(n) => {
let x = n.value - 50
x = x < 0 ? 0 : x
x = x > imgWidth.value - 100 ? imgWidth.value - 100 : x
positionX.value = x
},
{ deep: true }
)
// 处理y
const positionY = ref(0)
watch(
elementY,
(n) => {
let y = n.value - 50
y = y < 0 ? 0 : y
y = y > imgHeight.value - 100 ? imgHeight.value - 100 : y
positionY.value = y
},
{ deep: true }
)
const mouseEnterChange = (index) => {
currentIndex.value = index
init()
}
</script>
<style lang="scss" scoped>
.preview-img {
width: 100%;
&-content {
height: 352px;
background-color: #f8f8f8;
text-align: center;
&-contents {
position: relative;
height: 100%;
display: inline-block;
img {
height: 100%;
}
.layer {
width: 100px;
height: 100px;
background: rgba(0, 0, 0, 0.2);
left: 0;
top: 0;
// 可以移动
position: absolute;
cursor: move;
}
}
}
.zoom {
width: 45vw;
position: fixed;
top: 0;
right: 0;
background-repeat: no-repeat;
}
&-list {
margin-top: 15px;
display: flex;
&-item {
width: 120px;
height: 90px;
border-radius: 5px;
overflow: hidden;
margin-right: 17px;
border: 1px solid transparent;
img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
.active {
border: 1px solid #2969ff;
}
}
}
</style>
效果图: