JavaScript图片放大镜的原理和具体实现,以vue演示代码,其他框架类似

一、分析原理        

        线上商城的商品详情页经常用到图片放大镜的设计,以京东为例,黄色图层部分为需要在右侧大框内放大展示的区域:

分析上图:

  • 有四个区域:图片区域(宽高记为imgX,imgY),一个遮罩(黄色图层部分)(宽高记为maskX,maskY),一个放大的图片区域(宽高记为bigImgX,bigImgY),大图(宽高记为bigX,bigY)。
  • 先确定遮罩层的移动:理论上,鼠标应该位于遮罩的中心点。但是当遮罩左上角已经和图片左上角重合时,移动鼠标,应该保持遮罩层位置固定,换言之,已经移动到边界了,不能再往外了。同理,右下角、右上角、左下角也是保持遮罩层位置固定。
  1. 遮罩层左侧距离left应为鼠标左侧距离减去遮罩宽度的一半:event.offsetX - maskX / 2。
  2. 遮罩层头部距离top应为鼠标顶部距离减去遮罩高度的一半:event.offsetY - maskY / 2。
  3. 当left < 0,left固定为0;当left > imgX - maskX,left固定为imgX - maskX。
  4. 当top < 0,top固定为0;当top > imgY - maskY,top固定为imgY - maskY。

  • 由于放大的图片区域实际是对遮罩区域的放大,且为了保证图片不被拉伸,故而放大的图片区域实际上也是对遮罩图层的放大,则maskX / bigImgX = maskY / bigImgY。当然了,大图(bigX,bigY)是对小图的放大。则imgX / bigX = imgY / bigY。要保证移动遮罩的过程中刚好完成展示区域细节,则maskX / bigImgX = maskY / bigImgY = imgX / bigX = imgY / bigY。简言之,就是遮罩层与放大图片区域的比例应该和大小图放大比例保持一致。
  • 假设放大比例是5倍,遮罩层是30*40,则放大图片区域就应该是150*200,小图宽高假设为50*50,则大图宽高就应该是250*250。大图在放大图片区域的偏移量应该是倍数乘遮罩的偏移量:
  1. 当left < 0,left固定为0;当left > imgX - maskX,left固定为imgX - maskX,大图左侧偏移 -5 * left。
  2. 当top < 0,top固定为0;当top > imgY - maskY,top固定为imgY - maskY,大图顶部偏移 -5 * top。

上述分析过程中,我们可以看出存在很多偏移量的计算,确定应该使用相对定位和绝对定位。

二、具体实现

        笔者为了方便,这里使用vue来演示,其他框架也是一样的原理。

四个区域:

        <div class="preview">
          <img :src="imgUrl" v-if="imgUrl" class="img" @mousemove="(e) => handlerMouseMove(e)">
          <el-image :src="defaultImg" alt="" class="img" v-else />

          <!-- 遮罩 -->
          <div class="mask" ref="mask"></div>

          <!-- 放大的图片显示区域 -->
          <div class="big">
            <img :src="bigImgUrl" ref="bigImg" />
          </div>
        </div>

设置样式: 

  • 设置图片展示区域宽高为50*50。
  • 遮罩和大图展示区域默认不展示,当鼠标移动到图片上才展示。
  • 遮罩层设置为25*20,绝对定位,初始化为左上角。
  • 设置大图展示区域,按5倍比例,设置为125*100。
  • 大图相对于大图展示区域定位,初始为左上角。
.preview {
  width: 50px;
  height: 50px;
  margin: 0 auto;
  position: relative;

  .img {
    width: 50px;
    height: 50px;
    position: relative;
    top: 0;
    left: 0;

    &:hover {
      cursor: crosshair;
    }
  }

  .img:hover~.mask,
  .img:hover~.big {
    display: block;
  }

  .mask {
    width: 25px;
    height: 20px;
    background: rgb(109, 109, 109);
    position: absolute;
    left: 0;
    top: 0;
    display: none;
    transform: translate3d(0px, 0px, 0px);
    pointer-events: none;
  }

  .big {
    width: 125px;
    height: 100px;
    position: absolute;
    top: 0;
    left: 110%;
    overflow: hidden;
    z-index: 998;
    display: none;
    background: white;
    border-radius: 2px;

    img {
      width: 250px;
      height: 250px;
      position: absolute;
      left: 0;
      top: 0;
    }
  }
}

鼠标位置:event.offsetX、event.offsetY,按上文算出left和top,分别给mask遮罩和big大图展示区域进行定位:

    handlerMouseMove(event) {
      let mask = this.$refs.mask;
      let big = this.$refs.big;

      let left = event.offsetX - mask.offsetWidth / 2
      let top = event.offsetY - mask.offsetHeight / 2
      //当left < 0,left固定为0;当left > imgX - maskX,left固定为imgX - maskX,50-25=25
      if (left < 0) {
        left = 0
      } else if (left > 25) {
        left = 25
      }
      //当top < 0,top固定为0;当top > imgY - maskY,top固定为imgY - maskY,50-20=30
      if (top < 0) {
        top = 0
      } else if (top > 30) {
        top = 30
      }
      mask.style.left = left + 'px'
      mask.style.top = top + 'px'
      //大图左侧偏移 -5 * left,125/25 === 100/20 === 250/50 === 250/50 === 5
      big.style.left = -5 * left + 'px'
      //大图顶部偏移 -5 * top
      big.style.top = -5 * top + 'px'
    },

猜你喜欢

转载自blog.csdn.net/sxww_zyt/article/details/130841275
今日推荐