vue3中实现图片放大器效果

29 阅读2分钟
  • 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>

效果图:

图片.png