设计

鼠标移动到商品图片时,根据鼠标移动的一块区域,在右侧放大并显示出来,鼠标移出后关闭放大的悬窗;
注:本文采用Vue3版本进行开发,原理和方法都一样,改为Vue2也很简单

效果图:

在这里插入图片描述

实现方法

关键词:鼠标移动。那么就联系到了鼠标事件mousemove(移动)、mouseleave(移出目标区域)
在这里插入图片描述
理清了思路,接下来就办了

  1. 创建目标区域的图片(ref=“moveDom”),和创建鼠标所在的区域(class=“border”),放大的图片显示(pop-image)
     <div class="pop">
    <div
      class="pop-box"
      :style="{ width: boxStyle.width + 'px', height: boxStyle.height + 'px', cursor: flag ? 'move' : '' }"
    >
      <img :src="imgsrc" ref="moveDom" alt />
      <div
        v-if="flag"
        class="border"
        :style="{ left: styleObj.left + 'px', top: styleObj.top + 'px' }"
      ></div>
    </div>
    <!-- 还可以利用background-position实现 -->
    <!--弹出来的放大区域图片 利用定位 -->
    <div class="pop-image" v-if="flag">
      <img :style="popStyle" :src="imgsrc" alt />
    </div>
  </div>
  1. 添加鼠标监听事件
 onMounted(() => {
      moveDom.value.addEventListener('mousemove', moveEvent)
      moveDom.value.addEventListener('mouseleave', mouseleaveEvent)
  })
  //不要忘了卸载
 onBeforeUnmount(() => {
      moveDom.value.removeEventListener('mouseleave', mouseleaveEvent)
      moveDom.value.removeEventListener('mousemove', moveEvent)
 })
  1. 最后是处理鼠标的坐标位置,计算出要放大的区域
    // 鼠标移动事件
    const moveEvent = rafThrottle(e => {
      let event = e || window.event;
      if (event.preventDefault) {
        event.preventDefault();
      } else {
        event.returnValue = false;
      }
      flag.value = true
      let { offsetX, offsetY } = e;
      let { height, width } = boxStyle.value
      styleObj.value = {
        left: (offsetX - watchWidth / 2) > 0 ? (offsetX + watchWidth / 2) >= width ? width - watchWidth : offsetX - watchWidth / 2 : 0,
        top: (offsetY - watchWidth / 2) > 0 ? (offsetY + watchWidth / 2) >= height ? height - watchWidth : offsetY - watchWidth / 2 : 0
      }
      let { left, top } = styleObj.value;
      popStyle.value = `left:-${left * (900 / watchWidth)}px;top:-${top * (900 / watchWidth)}px`
    })
  1. 可能看代码片段不太明白,最后放出完整的代码
<template>
  <div class="pop">
    <div
      class="pop-box"
      :style="{ width: boxStyle.width + 'px', height: boxStyle.height + 'px', cursor: flag ? 'move' : '' }"
    >
      <img :src="imgsrc" ref="moveDom" alt />
      <div
        v-if="flag"
        class="border"
        :style="{ left: styleObj.left + 'px', top: styleObj.top + 'px' }"
      ></div>
    </div>
    <!-- 还可以利用background-position实现 -->
    <!-- 利用定位 -->
    <div class="pop-image" v-if="flag">
      <img :style="popStyle" :src="imgsrc" alt />
    </div>
  </div>
</template>

<script>
import { ref, onBeforeUnmount, onMounted } from '@vue/runtime-core'
export default {
  name: 'Pop',
  setup() {
    const imgsrc = ref('http://zs.test//files/products/img/20220317/59fce583589a9463d6a1822d9ae693c9.jpg')
    const moveDom = ref(null)
    const img = new Image()
    const flag = ref(false)
    const popStyle = ref('')
    const watchWidth = 200;
    img.src = imgsrc.value
    const boxStyle = ref({
      width: 300,//固定了宽高
      height: 300//img.height * (300 / img.width)
    })
    const styleObj = ref({
      left: 0, top: 0
    })
    onMounted(() => {
      moveDom.value.addEventListener('mousemove', moveEvent)
      moveDom.value.addEventListener('mouseleave', mouseleaveEvent)
    })
    onBeforeUnmount(() => {
      moveDom.value.removeEventListener('mouseleave', mouseleaveEvent)
      moveDom.value.removeEventListener('mousemove', moveEvent)
    })
    const rafThrottle = (fn) => {
      let locked = false;
      return function (...args) {
        if (locked) return;
        locked = true;
        window.requestAnimationFrame(_ => {
          fn.apply(this, args);
          locked = false;
        });
      };
    }
    // 鼠标移动事件
    const moveEvent = rafThrottle(e => {
      let event = e || window.event;
      if (event.preventDefault) {
        event.preventDefault();
      } else {
        event.returnValue = false;
      }
      flag.value = true
      let { offsetX, offsetY } = e;
      let { height, width } = boxStyle.value
      //计算边界条件
      styleObj.value = {
        left: (offsetX - watchWidth / 2) > 0 ? (offsetX + watchWidth / 2) >= width ? width - watchWidth : offsetX - watchWidth / 2 : 0,
        top: (offsetY - watchWidth / 2) > 0 ? (offsetY + watchWidth / 2) >= height ? height - watchWidth : offsetY - watchWidth / 2 : 0
      }
      let { left, top } = styleObj.value;
      //根据比例计算放大的区域
      popStyle.value = `left:-${left * 3}px;top:-${top * 3}px`
    })
    // 鼠标移出
    const mouseleaveEvent = rafThrottle(e => {
      flag.value = false
    })
    return {
      flag,
      popStyle,
      moveDom,
      styleObj,
      imgsrc,
      boxStyle,
    }
  }
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
.pop {
  position: relative;
  &-box {
    position: relative;
    box-sizing: border-box;
    overflow: hidden;
    border: 1px dashed rgb(253, 253, 253);
    img {
      width: 100%;
      height: 100%;
    }
    .border {
      position: absolute;
      width: 200px;
      height: 200px;
      pointer-events: none;
      background-color: rgba(127, 255, 212, 0.315);
    }
  }
  &-image {
    width: 600px;
    height: 600px;
    position: absolute;
    top: 0;
    overflow: hidden;
    left: 300px;
    img {
      position: absolute;
      width: 900px;
      height: 900px;
    }
  }
}
</style>

结语

提示一下:使用vue3获取ref常常遇到的一个坑是,忘记return,会导致获取的ref是null
如果不明白为什么用offsetX, offsetY,建议查阅相关资料弄懂了再来看该文章

Logo

前往低代码交流专区

更多推荐