vue组件之商品详情页放大镜🔍

375 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第3天,点击查看活动详情

近期早上看书📖多些,博客的话,更多的在抽一些零碎的时间在弄,现在的这篇也算是有点纪念意义啦,因为莎童鞋就在我身边玩手机,而我在写博客,哈哈哈哈哈...生活有时候就是这样吧,简单又美好~

开发过程中需要封装一个类似某宝或者某东商品详情页里面,鼠标经过图片,对图片上的区域进行一个放大展示组件,所以就分析了下整个封装的过程吧,也为了以后封装少走弯路。

需要考虑的信息:

  • 既然是组件,那么原图片的大小一定是可以通过props配置的(笔者这里只是将其提取到datadrawEnlargeStyle),用来设置组件展示区原图区域的大小

  • 鼠标经过图片,需要有一个跟随鼠标滑动的滑块,来指示要放大的区域

  • 需要一个等大图层用于测量图片移动的位置以及边界问题。

  • 在生命周期created阶段对组件容易的原图大小原图容器大小橘红色移动滑块大小右侧展示区域的图片大小的尺寸进行一个设置。(这里也是可以通过props)进行设置哒,笔者这里暂时放到data中设置,有需要的可以抽取 到props中去进行设置

  • 对和图片等大的蒙层(即 class="maskTop"的div)绑定三个事件进行监听:

    • enterHandler:展示右侧图片以及橘红色移动滑块
    • moveHandler:鼠标移动至原图区域的计算:
      • 层罩的左上角坐标位置,并对其进行限制:无法超出原图区域左上角

      • 对层罩位置再一次限制,保证层罩只能在原图区域空间内

这里需要注意的一个点,就是理解放大倍数和右侧展示图片放大的关系:

  • 右侧展示图片其实大小是左侧原图的enlargeTimes倍,之所以有放大效果,是以为他在一定的可视区域内,展示出来图片的冰山一角,给人呈现以放大的感觉。

  • 理解了上面👆🏻的点,也就可以理解,右侧图片移动的transform相应倍数的topX和topY位置

完成效果如下:

最后,组件代码如下:

<!--拍卖详情页-放大器效果-->
<template>
  <div class="draw-enlarge" :style="drawEnlargeStyle">
    <!-- 图片容器 -->
    <div class="left">
      <!-- 原图图片 -->
      <img class="leftImg" :src="require(`../../assets/images/index/${currImgName}.png`)" alt="" />
      <!-- 在原图上移动的罩层 -->
      <div v-show="topShow" class="top" :style="topStyle"></div>
      <!-- 跟原图等大的罩层 -->
      <div
        class="maskTop"
        @mouseenter="enterHandler"
        @mousemove="moveHandler"
        @mouseout="outHandler"
      ></div>
    </div>
    <!-- 右侧展示放大后的图片 -->
    <div v-show="rShow" class="right" :style="rightStyle">
      <img
        :style="r_img"
        class="rightImg"
        :src="require(`../../assets/images/index/${currImgName}.png`)"
        alt="放大的展示图片"
      />
    </div>
  </div>
</template>
<script>
export default {
  data() {
    return {
      topStyle: { transform: '' }, //图片上橘红色移动层的样式
      drawEnlargeStyle: {}, //组件的样式
      r_img: {}, //右侧容器内图片样式
      rightStyle: {}, // 右侧容易的样式
      topShow: false, // 是否显示图片上移动的的橘红色罩层
      rShow: false,  // 是否显示右侧图片容器
      imgSize: { //图片初始大小
        width: 400,
        height: 280,
      },
      shadeSize: { //图片上移动的橘红色移动图层大小
        width: 150,
        height: 100,
      }, 
      magnificationFactor: 3, // 放大倍数
      currImgName:'baner1', // 当前展示图片的名称
    };
  },
  props:{
    // 图片名称
    imgName:{
      default:'baner1',
      type:String
    },
    // 放大倍数
    enlargeTimes:{
      default:3,
      type:Number
    }
  },
  watch:{
    // 图片名称
    imgName:{
      handler(newVal,oldVal){
        if(newVal !== undefined){
          this.currImgName = newVal;
        }
      },
      immediate:true
    },
    // 放大倍数
    enlargeTimes:{
      handler(newVal,oldVal){
        if(newVal !== undefined){
          this.magnificationFactor = newVal;
        }
      },
      immediate:true
    }
  },
  created: function() {
    // 设置右侧图片大小
    this.$set(this, 'r_img', {
      width: this.imgSize.width * this.magnificationFactor + 'px',
      height: this.imgSize.height * this.magnificationFactor + 'px',
    });
    // 设置组件容器展示大小
    this.$set(this, 'drawEnlargeStyle', {
      width: this.imgSize.width + 'px',
      height: this.imgSize.height + 'px',
    });
    // 设置图片上移动的悬浮层的大小
    this.$set(this, 'topStyle', {
      width: this.shadeSize.width + 'px',
      height: this.shadeSize.height + 'px',
      transform: '',
    });
    // 右侧展示图片容器大小
    this.$set(this, 'rightStyle', {
      'margin-left': this.imgSize.width + 12 + 'px',
      width: this.shadeSize.width * this.magnificationFactor + 'px',
      height: this.shadeSize.height * this.magnificationFactor + 'px',
    });
  },
  methods: {
    /**
     * @function 鼠标移入处理
    */ 
    enterHandler() {
      this.topShow = true;
      this.rShow = true;
    },
    /**
     * @function 鼠标在原图中移动的处理
    */
    moveHandler(event) {
      let x = event.offsetX;
      let y = event.offsetY;
      // 层罩的左上角坐标位置,并对其进行限制:无法超出原图区域左上角
      let topX =
        x - this.shadeSize.width / 2 < 0 ? 0 : x - this.shadeSize.width / 2;
      let topY =
        y - this.shadeSize.height / 2 < 0 ? 0 : y - this.shadeSize.height / 2;
      // 对层罩位置再一次限制,保证层罩只能在原图区域空间内
      if (topX > this.imgSize.width - this.shadeSize.width) {
        topX = this.imgSize.width - this.shadeSize.width;
      }
      
      if (topY > this.imgSize.height - this.shadeSize.height) {
        topY = this.imgSize.height - this.shadeSize.height;
      }
      // 通过 transform 进行移动控制
      this.topStyle.transform = `translate(${topX}px,${topY}px)`;
      this.r_img.transform = `translate(-${this.magnificationFactor *
        topX}px,-${this.magnificationFactor * topY}px)`;
    },
    /**
     * @function 鼠标移除后的功能处理
    */
    outHandler() {
      // 控制层罩与放大空间的隐藏
      this.topShow = false;
      this.rShow = false;
    },
  },
};
</script>

下面为css部分的代码:

//css 部分
<style scoped>
.draw-enlarge {
  position: relative;
}
/* 放大的图片,通过定位将左上角定位到(0,0) */
.draw-enlarge .rightImg {
  display: inline-block;
  position: absolute;
  top: 0;
  left: 0;
}
/* 右边的区域图片放大空间 */
.draw-enlarge .right {
  width: 100%;
  height: 100%;
  position: absolute;
  overflow: hidden;
  z-index: 999;
}
/* 一个最高层层罩 */
.draw-enlarge .maskTop {
  width: 100%;
  height: 100%;
  position: absolute;
  z-index: 1;
  top: 0;
  left: 0;
}
/* 层罩,通过定位将左上角定位到(0,0) */
.draw-enlarge .top {
  background-color: lightcoral;
  opacity: 0.4;
  position: absolute;
  top: 0;
  left: 0;
}
/* 原图的显示 */
.draw-enlarge .leftImg {
  width: 100%;
  height: 100%;
  display: inline-block;
}
/* 原图的容器 */
.draw-enlarge .left {
  width: 100%;
  height: 100%;
  float: left;
  position: relative;
}
</style>