Skip to content
On this page

放大镜

放大镜

效果

思路

遮罩层

首先是遮罩层 mask 的实现
外层容器即父元素相对定位,mask 绝对定位,通过移动 left/top 改变 mask 的位置,mask的水平移动的最大距离只能是 0-外层容器的宽度-mask的宽度,垂直移动的最大距离只能是 0-外层容器的高度-mask的高度

右边大图

右边大图容器是一个绝对定位,溢出隐藏;
容器里面的图片尺寸是原图的 1.2倍,也是绝对定位,通过移动 left/top 改变图片的位置

鼠标移动

当移动鼠标时,mask移动的距离只能是e.clinetX - 外层容器.left - mask.width/2并且不能超过最大/最小范围

右侧的大图也要随之移动,移动的距离是mask移动的比例 * 可移动的距离, mask移动的比例也就是移动的距离 / 可移动的最大距离 = (e.clinetX - 外层容器.left - mask.width/2) / 外层容器.宽度 - mask.宽度

源码

vue
<template>
  <div class="container" @mouseover="boxShow = true" @mouseout="boxShow = false" @mousemove="onMouseMove" ref="container">

    <img :src="img" />
    <!-- 遮罩层 -->
    <div class="mask" ref="mask" v-show="boxShow" />

    <!-- 大图片 -->
    <div class="big-img_box" ref="bigImgBox" v-show="boxShow">
      <img class="big-img" ref="bigImg" :src="img" />
    </div>
  </div>
</template>
<script setup lang="ts">
import { assetsHTML } from "@/utils/elements";
import { ref } from "vue";
const img = ref("https://img.alicdn.com/imgextra/i3/1917047079/O1CN01lkG2pf22AEUi1owve_!!1917047079.png_430x430q90.jpg")
const container = ref<HTMLElement | null>(null);

const mask = ref<HTMLElement | null>(null);
const bigImg = ref<HTMLElement | null>(null)

const bigImgBox = ref<HTMLElement | null>(null);
const boxShow = ref(false);
// 最外层可以mouse 的盒子

const onMouseMove = (e: MouseEvent) => {
  assetsHTML(container.value)
  assetsHTML(mask.value)
  assetsHTML(bigImg.value)
  assetsHTML(bigImgBox.value)
  //NOTE: 在 vue.express 上这个不生效,在真实页面上生效
  // let x = e.clientX - container.value.offsetLeft;
  // let y = e.clientY - container.value.offsetTop;
  // let maskRefX = x - mask.value.offsetWidth / 2;
  // let maskRefY = y - mask.value.offsetHeight / 2;

  let x = e.clientX - container.value.getBoundingClientRect().left;
  let y = e.clientY - container.value.getBoundingClientRect().top;
  // 如果不减去 1/2,鼠标在右下方
  let maskRefX = x - mask.value.offsetWidth / 2;
  let maskRefY = y - mask.value.offsetHeight / 2;

  // maskRef的x最大移动距离
  let maskRefXMaxMove = container.value.offsetWidth - mask.value.offsetWidth;
  let maskRefYMaxMove = container.value.offsetHeight - mask.value.offsetHeight;

  // 大图片的最大移动距离
  let bigImgXMaxMove =
    bigImgBox.value.offsetWidth - bigImg.value.offsetWidth;

  let bigImgYMaxMove =
    bigImgBox.value.offsetHeight - bigImg.value.offsetHeight;


  // 限制移动距离  
  if (maskRefX <= 0) {
    maskRefX = 0;
  } else if (maskRefX >= maskRefXMaxMove) {
    maskRefX = maskRefXMaxMove;
  }

  if (maskRefY <= 0) {
    maskRefY = 0;
  } else if (maskRefY >= maskRefYMaxMove) {
    maskRefY = maskRefYMaxMove;
  }

  mask.value.style.left = maskRefX + "px";
  mask.value.style.top = maskRefY + "px";


  // 大盒子的移动x =  (小盒子水平移动的x / 可移动的宽度) * 最大移动距离
  let bixImgXMove = (maskRefX / maskRefXMaxMove) * bigImgXMaxMove;
  let bixImgYMove = (maskRefY / maskRefYMaxMove) * bigImgYMaxMove;


  bigImg.value.style.left = bixImgXMove + "px";
  bigImg.value.style.top = bixImgYMove + "px";
};
</script>
<style scoped>
.container {
  @apply w-[430px] aspect-square relative border border-gray-200
}

.mask {
  @apply absolute w-[200px] aspect-square bg-blue-200 opacity-50 cursor-move top-0 left-0;
}

.big-img_box {
  @apply absolute w-[500px] aspect-square top-0 left-[480px] bg-white overflow-hidden border border-gray-500;
}

.big-img {
  @apply absolute left-0 top-0;
  max-width: inherit;
  width: 120%;
}
</style>