电商图片放大镜功能

<template>
  <div class=”magnifier-system”>
    <!– 小图区域 –>
    <el-popover
      placement=”right”
      trigger=”hover”
      v-model=”isActive”
      :popper-class=”‘magnifier-popover'”
    >
      <!– 大图区域内容 –>
      <div class=”enlarged-area” :style=”bigContainerStyle”>
        <img :src=”bigImg” alt=”Enlarged” :style=”enlargedImgStyle” />
      </div>
      <!– 小图区域触发器 –>
      <template v-slot:reference>
        <div
          class=”preview-area”
          ref=”previewArea”
          @mousemove=”handleMouseMove”
          @mouseenter=”activateMagnifier”
          @mouseleave=”deactivateMagnifier”
        >
          <div class=”img-container” :style=”smallContainerStyle”>
            <img :src=”smallImg” alt=”Preview” class=”preview-img” />
          </div>
        </div>
      </template>
    </el-popover>
  </div>
</template>
<script>
export default {
  name: ‘ResponsiveMagnifier’,
  props: {
    smallImg: String,
    bigImg: String,
    zoomLevel: { type: Number, default: 2 },
    imgSmall: {
      type: Object,
      default: () => ({ W: 100, H: 100 }),
    },
    imgBig: {
      type: Object,
      default: () => ({ W: 300, H: 300 }),
    },
    imgNumber: {
      type: Number,
      default: 1,
    },
    imgMoveWidth: {
      type: String,
      default: ’15’,
    },
  },
  data() {
    return {
      isActive: false,
      lensPosition: { x: 0, y: 0 },
      containerSize: { w: 0, h: 0 },
    };
  },
  computed: {
    smallContainerStyle() {
      return {
        width: `${this.imgSmall.W}px`,
        height: `${this.imgSmall.H}px`,
      };
    },
    bigContainerStyle() {
      return {
        width: `${this.imgBig.W}px`,
        height: `${this.imgBig.H}px`,
      };
    },
    imageOffset() {
      const { x, y } = this.lensPosition;
      const scaleX = this.imgBig.W / this.imgSmall.W;
      const scaleY = this.imgBig.H / this.imgSmall.H;
      // 计算镜头中心位置
      const lensCenterX = x + this.imgSmall.W / this.zoomLevel / 2;
      const lensCenterY = y + this.imgSmall.H / this.zoomLevel / 2;
      return {
        x: -(lensCenterX * scaleX – this.imgBig.W / 2),
        y: -(lensCenterY * scaleY – this.imgBig.H / 2),
      };
    },
    enlargedImgStyle() {
      return {
        transform: `translate(${this.imageOffset.x}px, ${this.imageOffset.y}px)`,
        width: `${this.imgBig.W}px`,
        height: `${this.imgBig.H}px`,
      };
    },
  },
  watch: {
    ‘imgSmall.W'() {
      this.updateContainerSize();
    },
    ‘imgSmall.H'() {
      this.updateContainerSize();
    },
    ‘imgBig.W'() {
      this.updateContainerSize();
    },
    ‘imgBig.H'() {
      this.updateContainerSize();
    },
  },
  mounted() {
    this.updateContainerSize();
  },
  methods: {
    updateContainerSize() {
      this.$nextTick(() => {
        this.containerSize = {
          w: this.$refs.previewArea.querySelector(‘.img-container’).offsetWidth,
          h: this.$refs.previewArea.querySelector(‘.img-container’).offsetHeight,
        };
      });
    },
    activateMagnifier() {
      this.isActive = true;
    },
    deactivateMagnifier() {
      this.isActive = false;
    },
    handleMouseMove(e) {
      if (!this.isActive) return;
      const container = this.$refs.previewArea.querySelector(‘.img-container’);
      const rect = container.getBoundingClientRect();
      let x = e.clientX – rect.left – window.pageXOffset;
      let y = e.clientY – rect.top – window.pageYOffset;
      x = Math.max(0, Math.min(x, rect.width));
      y = Math.max(0, Math.min(y, rect.height));
      const lensWidth = this.imgSmall.W / this.zoomLevel;
      const lensHeight = this.imgSmall.H / this.zoomLevel;
      this.lensPosition = {
        x: Math.min(Math.max(x – lensWidth / 2, 0), rect.width – lensWidth),
        y: Math.min(Math.max(y – lensHeight / 2, 0), rect.height – lensHeight),
      };
    },
  },
};
</script>
<style scoped>
.magnifier-system {
  display: flex;
  align-items: flex-start;
}
.preview-area {
  position: relative;
  border: 1px solid #e0e0e0;
  overflow: hidden;
  cursor: crosshair;
}
.img-container {
  position: relative;
  overflow: hidden;
}
.preview-img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}
.magnifier-lens {
  position: absolute;
  backdrop-filter: blur(2px);
  pointer-events: none;
  transition: 0.05s linear;
}
.enlarged-area {
  border: 1px solid #e0e0e0;
  overflow: hidden;
  background: #fff;
}
.enlarged-img {
  position: absolute;
  width: auto;
  height: auto;
  min-width: 100%;
  min-height: 100%;
  transition: transform 0.05s linear;
}
</style>
<style>
/* 覆盖el-popover的样式 */
.magnifier-popover {
  padding: 0 !important;
  border: none !important;
  /* box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); */
}
.magnifier-popover .popper__arrow {
  display: none !important;
}
</style>

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注