图片放大镜vue组件

1,006 阅读1分钟

最近看到放大镜相关的帖子,所以也想自己实现下。 查看原帖

解析

  1. 外层定位为相对定位,放大镜和遮罩为绝对定位,鼠标移入遮罩层放大镜显示移出则不显示
  2. 可以根据需要设置外层宽高,原图和外层同宽高,只需传入图片源就行
  3. 通过配置scale自动设置放大镜中图片宽高(原图*scale)

实际项目中应用

image.png

image.png

源码

<template>
  <div class="image-magnifier">
    <!-- 原图片 -->
    <el-image ref="origin" class="img-src-org" :src="src" :fit="fit" />

    <!-- 放大镜 -->
    <div v-show="isShow" class="magnifier" :style="magnifierWrapStyle">
      <el-image
        class="img-src-magnifier"
        :src="src"
        :style="magnifierImgStyle"
        :fit="fit"
      />
    </div>

    <!-- 遮罩 -->
    <div
      class="mask"
      @mouseenter="handleEnter"
      @mouseleave="handleLeave"
      @mousemove="moveDebounce"
    />
  </div>
</template>

<script>
function debounce(fn, delay) {
  let timer
  return function() {
    const context = this
    const args = arguments
    clearTimeout(timer)
    timer = setTimeout(function() {
      fn.apply(context, args)
    }, delay)
  }
}

// 图片放大镜
export default {
  name: 'ImageMagnifier',

  props: {
    // 图片源
    src: {
      type: String,
      default: ''
    },
    // 放大镜尺寸
    magnifierSize: {
      type: [String, Number],
      default: 160
    },
    // 放大倍数
    scale: {
      type: [String, Number],
      default: 1.8
    },
    // 确定图片如何适应容器框,同原生 object-fit
    fit: {
      type: String,
      default: ''
    }
  },

  data() {
    return {
      isShow: false,
      magnifierLeft: 0,
      magnifierTop: 0,
      magnifierImgLeft: 0,
      magnifierImgTop: 0,
      moveDebounce: () => {},
      orgW: this.magnifierSize,
      orgH: this.magnifierSize
    }
  },

  computed: {
    magnifierWrapStyle() {
      const { magnifierSize, magnifierLeft, magnifierTop } = this
      return {
        width: magnifierSize + 'px',
        height: magnifierSize + 'px',
        left: magnifierLeft - magnifierSize / 2 + 'px',
        top: magnifierTop - magnifierSize / 2 + 'px'
      }
    },

    magnifierImgStyle() {
      const { orgW, orgH, magnifierImgLeft, magnifierImgTop, scale } = this
      return {
        width: orgW * scale + 'px',
        height: orgH * scale + 'px',
        left: magnifierImgLeft + 'px',
        top: magnifierImgTop + 'px'
      }
    }
  },

  mounted() {
    // 防抖
    this.moveDebounce = debounce(this.handleMove, 3)

    const $org = this.$refs.origin
    // 原图宽
    this.orgW = $org.$el.clientWidth
    // 原图高
    this.orgH = $org.$el.clientHeight
  },

  methods: {
    handleMove(e) {
      const { offsetX, offsetY } = e
      const { orgW, orgH, scale, magnifierSize } = this
      
      // 放大镜定位
      this.magnifierLeft = offsetX
      this.magnifierTop = offsetY
      // 鼠标在遮罩层中所处位置相对遮罩层宽/高的比例(遮罩层和外层及原图宽高一致)
      const percW = offsetX / orgW
      const percH = offsetY / orgH
       
      // 放大镜中图片定位
      this.magnifierImgLeft = -percW * orgW * scale + magnifierSize / 2
      this.magnifierImgTop = -percH * orgH * scale + magnifierSize / 2
    },

    handleEnter() {
      this.isShow = true
    },

    handleLeave() {
      this.isShow = false
    }
  }
}
</script>

<style lang="scss" scoped>
.image-magnifier {
  position: relative;
}

.img-src-org {
  width: 100%;
  height: 100%;
}

.img-src-magnifier {
  position: absolute;
}

.magnifier {
  position: absolute;
  overflow: hidden;
  border-radius: 50%;
  box-shadow: 0 0 20px 4px #000;
  left: 0;
  top: 0;
  background-color: #fff;
  z-index: 2;
}

.mask {
  position: absolute;
  width: 100%;
  height: 100%;
  left: 0;
  top: 0;
  z-index: 3;
}
</style>