图片通过滑块小图切换大图放大镜效果显示
实现目标:
-
显示一组图片列表,鼠标进入小图记录当下小图下标,通过小图下标在数组中对应图片显示到大图位置;
-
鼠标进入大图位置时,带动滑块移动,并将放大两倍的大图显示在原大图右边
实现过程:
- 实现小图切换大图:通过v-for将图片列表绑定在li标签上,同时为li标签添加鼠标监测事件,并绑定事件处理函数
enterHandler
,传入图片索引 i 。初始化响应数据activeIndex
记录当前激活索引。通过v-on
加强 li 标签的样式 ,active
激活条件为i=== activeIndex
即当前激活索引与小图索引相同时,左侧大图链接为激活图片索引imageList[sctiveIndex]
。 - 导入VueUse的
useMouseInElement
元素获取鼠标位置,记录鼠标相对位置elementX、elementY
,初始化放大图坐标positionX、positionY
,通过监听器watch
监听鼠标相对位置的变化,小滑块通过left 、 top
控制位置
图中红色正方形为滑块位置,蓝色正方形内为滑块控制的有效范围,需要处理边界(黑色正方形与蓝色正方形的差集)
控制滑块移动:
// 横向
if (elementX.value > 100 && elementX.value < 300) {
left.value = elementX.value - 100
}
// 纵向
if (elementY.value > 100 && elementY.value < 300) {
top.value = elementY.value - 100
}
// 处理边界
if (elementX.value < 100) {
left.value = 0
}
if (elementX.value > 300) {
left.value = 200
}
if (elementY.value < 100) {
top.value = 0
}
if (elementY.value > 300) {
top.value = 200
}
- 控制放大图显示
注意观察,当鼠标在左侧大图移动时,右侧放大图移动方向相反
控制放大图坐标:
// 控制大图显示
positionX.value = -left.value * 2
positionY.value = -top.value * 2
// 控制放大图图片样式
<div
class="large"
:style="[
{
backgroundImage: `url(${imageList[activeIndex]})`,
backgroundPositionX: `${positionX}px`,
backgroundPositionY: `${positionY}px`,
},
]"
v-show="!isOutside"
></div>
【注:图片源自淘宝,如有侵权立刻删除】
完整代码
<script setup>
import { useMouseInElement } from '@vueuse/core'
import { ref, watch } from 'vue'
// 图片列表
const imageList = [
'img/O1CN01HhRDRN1PLRFptsiNY_!!2210715771824.jpg_.webp',
'img/O1CN01TgHapK1PLRFzgigEa_!!2210715771824.jpg_.webp',
'img/O1CN015Lpahn1PLRFzgiLRv_!!2210715771824.jpg_.webp',
'img/O1CN017pJsbA1PLRG0Uk1UI_!!2210715771824.jpg_ (1).webp',
'img/O1CN017pJsbA1PLRG0Uk1UI_!!2210715771824.jpg_.webp',
]
// 小图切换大图
const activeIndex = ref(0)
const enterHandler = (i) => {
activeIndex.value = i
}
// 获取鼠标相对位置
const target = ref(null)
const { elementX, elementY, isOutside } = useMouseInElement(target)
// 控制滑块跟随鼠标移动(监听XY变化,变化则重新设计left/top)
const left = ref(0)
const top = ref(0)
const positionX = ref(0)
const positionY = ref(0)
watch([elementX, elementY, isOutside], () => {
if (isOutside.value) {
return
}
// 有效范围内控制滑块距离
// 横向
if (elementX.value > 100 && elementX.value < 300) {
left.value = elementX.value - 100
}
// 纵向
if (elementY.value > 100 && elementY.value < 300) {
top.value = elementY.value - 100
}
// 处理边界
if (elementX.value < 100) {
left.value = 0
}
if (elementX.value > 300) {
left.value = 200
}
if (elementY.value < 100) {
top.value = 0
}
if (elementY.value > 300) {
top.value = 200
}
// 控制大图显示
positionX.value = -left.value * 2
positionY.value = -top.value * 2
})
</script>
<template>
<div class="goods-image">
<!-- 左侧大图-->
<div class="middle" ref="target">
<img :src="imageList[activeIndex]" alt="" />
<!-- 蒙层小滑块 -->
<div class="layer" :style="{ left: `${left}px`, top: `${top}px` }"></div>
</div>
<!-- 小图列表 -->
<ul class="small">
<li
v-for="(img, i) in imageList"
:key="i"
@mouseenter="enterHandler(i)"
:class="{ active: i === activeIndex }"
>
<img :src="img" alt="" />
</li>
</ul>
<!-- 放大镜大图 -->
<div
class="large"
:style="[
{
backgroundImage: `url(${imageList[activeIndex]})`,
backgroundPositionX: `${positionX}px`,
backgroundPositionY: `${positionY}px`,
},
]"
v-show="!isOutside"
></div>
</div>
</template>
<style scoped lang="scss">
.goods-image {
width: 480px;
height: 400px;
position: relative;
display: flex;
.middle {
width: 400px;
height: 400px;
background: #f5f5f5;
}
img {
max-width: 100%;
max-height: 100%;
}
.large {
position: absolute;
top: 0;
left: 412px;
width: 400px;
height: 400px;
z-index: 500;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
background-repeat: no-repeat;
// 背景图:盒子的大小 = 2:1 控制背景图的移动实现放大的效果
background-size: 800px 800px;
background-color: #f8f8f8;
}
.layer {
width: 200px;
height: 200px;
background: rgba(0, 0, 0, 0.2);
// 绝对定位
left: 0;
top: 0;
position: absolute;
}
.small {
width: 80px;
list-style: none;
li {
width: 68px;
height: 68px;
margin-left: 12px;
margin-bottom: 15px;
cursor: pointer;
&:hover,
&.active {
border: 2px solid pink;
}
}
}
}
* {
box-sizing: border-box;
}
</style>