Bootstrap

vue3实现放大镜效果

1、使用@vueuser/core包实现

npm i @vueuse/core

 2、实现选择小图换大图

 

 代码实现

1、控制大图的src属性为小图列表中的一个

2、编写小图列表

  <!-- 大图 -->
  <div class="big" ref="target">
    <img :src="imglist[currIndex]" alt="">
    <!-- 遮罩层 -->
    <div class="layout" v-show="!isOutside" :style="{ left: `${left}px`, top: `${top}px` }"></div>
  </div>
  <!-- 小图 -->
  <div class="small">
    <div v-for="(item, i) in imglist" :key="i" @mouseenter="changeimg(i)" :class="{ active: i === currIndex }">
      <img :src="item" alt="">
    </div>
  </div>

3、编写小图列表

const imglist = ref([
  'https://ts1.cn.mm.bing.net/th/id/R-C.1ca2bda738ff9ce53c690cc0594e1946?rik=LKJAS5fM3DH2mA&riu=http%3a%2f%2fimg.mm4000.com%2ffile%2f0%2f5d%2fbe118211b8.jpg&ehk=2mXSxvTUSTYr3Cvn9KJwXbcnKmAo7T1b5a%2bCTcSRCjo%3d&risl=&pid=ImgRaw&r=0',
  'https://ts1.cn.mm.bing.net/th/id/R-C.1f57e1696b81e9b43b3668f326f31aa8?rik=S7FXlry6dGgDcA&riu=http%3a%2f%2fimg.mm4000.com%2ffile%2f3%2f50%2f1893738e93.jpg%3fdown&ehk=Y1ObY1WkhzdSbZn1i4%2fGCSscxBrZvY16e8Jnv7NBjbs%3d&risl=&pid=ImgRaw&r=0',
  'https://www.qqkw.com/d/file/p/2018/05-17/0515954b9ce3b4eee4774a73406ed5b6.jpg'
])

4、实现鼠标进入小图,大图展示对应图片

给小图for循环,添加属性,鼠标进入属性,执行changeimg函数,

const currIndex = ref(0)

const changeimg = (i) => {
  currIndex.value = i
}

动态绑定选中的样式 

:class="{ active: i === currIndex }"

3、放大图展示

引入useMouseInElement属性,用来表示鼠标当前位置(相对与选中的ref)

import { useMouseInElement } from '@vueuse/core'

 根据前面的大图div标签的ref="target"属性

// target大图目标
const target = ref(null)
const { elementX, elementY, isOutside } = useMouseInElement(target)

定义遮罩层

  <!-- 大图 -->
  <div class="big" ref="target">
    <img :src="imglist[currIndex]" alt="">
    <!-- 遮罩层 -->
    <div class="layout" v-show="!isOutside" :style="{ left: `${left}px`, top: `${top}px` }"></div>
  </div>

 定义left,top属性,用来判断遮罩层的位置

const left = ref(0)
const top = ref(0)

限制遮罩层移动(可移动区域,临界移动区域)

watch([elementX, elementY, isOutside], () => {
  if (isOutside.value){
    // console.log(target)
    // target.style.display='none'
    return 
  } 

  // 有限移动区间
  if (elementX.value > 50 && elementX.value < 150) {
    left.value = elementX.value - 50
  }
  if (elementY.value > 50 && elementX.value < 350) {
    top.value = elementY.value - 50
  }
  // 边界值
  if (elementX.value < 50) { left.value = 0 }
  if (elementX.value > 150) { left.value = 100 }
  if (elementY.value < 50) { top.value = 0 }
  if (elementY.value > 350) { top.value = 300 }

  positionX.value = -left.value 
  positionY.value = -top.value 
})

可移动区域(图片宽200px,高400px)(遮罩层宽100px,高100px)

当鼠标进入target中,

elementX>50px,elementX<150px,设置left为elementX-遮罩层宽的一半

elementY>50px,elementY<150px,设置top为elementY-遮罩层高的一半

达到鼠标中心在遮罩层中心的效果

临界值区域

elementX<50px,elementX>150px,设置left为0 或 图片的宽-遮罩层的宽

elementY>50px,elementY<150px,设置top为0 或 图片的高-遮罩层的高

遮罩层就不会跑出图片之外了

设置放大后的图,style设置选中的图片,控制展示的位置 backgroundPositionX

  <!-- 放大的大图 -->
  <div v-show="!isOutside" class="large" :style="[{
    backgroundImage: `url(${imglist[currIndex]})`,
    backgroundPositionX: `${positionX}px`,
    backgroundPositionY: `${positionY}px`
  }]">

使用定位让大图在右边

.large {
  width: 200px;
  height: 400px;
  position: absolute;
  left: 210px;
  top: 7px;
  background-color: #ccc;
  background-size: 400px 800px;
}

4、完整代码实现

<template>
  <!-- 大图 -->
  <div class="big" ref="target">
    <img :src="imglist[currIndex]" alt="">
    <!-- 遮罩层 -->
    <div class="layout" v-show="!isOutside" :style="{ left: `${left}px`, top: `${top}px` }"></div>
  </div>

  <!-- 放大的大图 -->
  <div v-show="!isOutside" class="large" :style="[{
    backgroundImage: `url(${imglist[currIndex]})`,
    backgroundPositionX: `${positionX}px`,
    backgroundPositionY: `${positionY}px`
  }]">

  </div>
  <!-- 小图 -->
  <div class="small">
    <div v-for="(item, i) in imglist" :key="i" @mouseenter="changeimg(i)" :class="{ active: i === currIndex }">
      <img :src="item" alt="">
    </div>
  </div>
</template>

<script setup>
import { ref, watch } from 'vue'
import { useMouseInElement } from '@vueuse/core'

const imglist = ref([
  'https://ts1.cn.mm.bing.net/th/id/R-C.1ca2bda738ff9ce53c690cc0594e1946?rik=LKJAS5fM3DH2mA&riu=http%3a%2f%2fimg.mm4000.com%2ffile%2f0%2f5d%2fbe118211b8.jpg&ehk=2mXSxvTUSTYr3Cvn9KJwXbcnKmAo7T1b5a%2bCTcSRCjo%3d&risl=&pid=ImgRaw&r=0',
  'https://ts1.cn.mm.bing.net/th/id/R-C.1f57e1696b81e9b43b3668f326f31aa8?rik=S7FXlry6dGgDcA&riu=http%3a%2f%2fimg.mm4000.com%2ffile%2f3%2f50%2f1893738e93.jpg%3fdown&ehk=Y1ObY1WkhzdSbZn1i4%2fGCSscxBrZvY16e8Jnv7NBjbs%3d&risl=&pid=ImgRaw&r=0',
  'https://www.qqkw.com/d/file/p/2018/05-17/0515954b9ce3b4eee4774a73406ed5b6.jpg'
])
const currIndex = ref(0)

const changeimg = (i) => {
  currIndex.value = i
}

// target大图目标
const target = ref(null)
const { elementX, elementY, isOutside } = useMouseInElement(target)
const left = ref(0)
const top = ref(0)
const positionX = ref(0)
const positionY = ref(0)
// 根据xy,改变边界值和临界值
watch([elementX, elementY, isOutside], () => {
  if (isOutside.value){
    // console.log(target)
    // target.style.display='none'
    return 
  } 

  // 有限移动区间
  if (elementX.value > 50 && elementX.value < 150) {
    left.value = elementX.value - 50
  }
  if (elementY.value > 50 && elementX.value < 350) {
    top.value = elementY.value - 50
  }
  // 边界值
  if (elementX.value < 50) { left.value = 0 }
  if (elementX.value > 150) { left.value = 100 }
  if (elementY.value < 50) { top.value = 0 }
  if (elementY.value > 350) { top.value = 300 }

  positionX.value = -left.value 
  positionY.value = -top.value 
})
</script>

<style scoped lang="less">
.big {
  position: relative;
  display: inline-block;
  width: 200px;
  height: 400px;

  img {
    width: 100%;
    height: 100%;
  }

  .layout {
    position: absolute;
    opacity: 0.5;
    background-color: #999999;
    width: 100px;
    height: 100px;
  }
}

.small {
  display: flex;

  img {
    width: 100px;
    height: 200px;
  }
}

.large {
  width: 200px;
  height: 400px;
  position: absolute;
  left: 210px;
  top: 7px;
  background-color: #ccc;
  background-size: 400px 800px;
}

.active {
  border: 1px green solid;
}

</style>

;