基于Vue的商品主图放大镜方案详解


Posted in Javascript onSeptember 19, 2019

前言

在做电商类应用时,难免会遇到商品主图实现放大镜效果的场景,现有的基于 Vue 的第三方包不多并且无法直接复用,今天,我来分享一种高稳定性的基于 Vue 的图片放大镜方法。

实现原理

放大镜的原理用一句话概括,就是根据小图上的鼠标位置去定位大图。

图1 原理图(以2倍放大为例)

基于Vue的商品主图放大镜方案详解

相信原理图已经画的很明白了, 图中,左侧框是小图框,其蓝色区域为图片遮罩层(需放大区域),右侧框是整个大图目前所在区域,其蓝色区域是放大区域,设置超出隐藏,就实现了放大遮罩区域的效果。

显然,两块蓝色区域存在着某种对应关系,即遮罩的左上角位置(相对于小图,以下称 X 坐标)和放大区域(相对于大图)的左上角位置是成比例的,即放大倍数。计算出 X 坐标后,适当调整背景图的位置,使大图向反方向移动 scale 倍的 X 坐标即可。

X 坐标为(maskX,maskY),以计算 maskX 为例:

鼠标移动中会产生 e.clientX ,标识鼠标与浏览器左侧的距离,小图与浏览器左侧的距离是 left ,由于遮罩始终是一个以鼠标为中心的正方形,所以:

maskX = e.clientX - left - mask/2

同理,

maskY = e.clientY - top - mask/2

大图的对应样式设置为:

{
 left: - maskX * scale + 'px';
 top: - maskY * scale + 'px';
}

效果演示

图2 长图展示

基于Vue的商品主图放大镜方案详解

图3 宽图展示

基于Vue的商品主图放大镜方案详解

图4 两倍放大效果图

基于Vue的商品主图放大镜方案详解

图5 四倍放大效果图

基于Vue的商品主图放大镜方案详解 

核心代码

HTML

一般放大镜实现的是 1:1 等宽等高的正方形图片,这里兼容了其他比例的图片,设置图片为垂直居中对齐,包括小图,大图。如果小图不够充满整个小图框,余留下的空白部分也可以有放大效果,只不过放大结果依然是空白。 这样只需计算背景图的移动距离,不用过多的关注图片定位问题。

<template>
 <div class="magnifier">
 <!-- 小图 -->
 <div class="small-box" @mouseover="handOver" @mousemove="handMove" @mouseout="handOut">
  <img class="smallPic" :src="`${src}?x-oss-process=image/resize,l_836`" />
  <div class="magnifier-zoom" 
  v-show="showMask"
  :style="{
   background: configs.maskColor,
   height: configs.maskWidth + 'px',
   width: configs.maskHeight + 'px', 
   opacity: configs.maskOpacity, 
   transform: transformMask
  }"
  ></div>
 </div>
 <!-- 大图, 注意误差 -->
 <div class="magnifier-layer" 
  v-show="showMagnifier"
  :style="{ 
  width: configs.width + 'px', 
  height: configs.height + 'px', 
  left: configs.width + 20 + 'px' 
  }"
 >
  <div class="big-box"
  :style="{ 
   width: bigWidth + 'px',
   height: bigHeight + 'px',
   left: moveLeft,
   top: moveTop
  }"
  >
  <div class="big-box-img"
   :style="{ 
   width: bigWidth - 2 + 'px', 
   height: bigHeight - 2 + 'px' 
   }"
  >
   <img
   :src="bigSrc"
   :style="{ 
    maxWidth: bigWidth - 2 + 'px', 
    maxHeight: bigHeight -2 + 'px' 
   }"
   />
  </div>
  </div>
 </div>
 </div>
</template>

JS

这里主要有三个事件函数。

handOver:鼠标进入到小图框上的事件,此时显示遮罩和放大区域,并计算小图框的位置信息。

handOver() {
 // 计算小图框在浏览器中的位置
 this.imgObj = this.$el.getElementsByClassName('small-box')[0];
 this.imgRectNow = this.imgObj.getBoundingClientRect();
 this.showMagnifier = true;
 this.showMask = true;
}

handMove:鼠标在小图上的移动事件,此事件发生在 handOver 之后,计算数据,移动遮罩以及背景图;

handMove(e) {
 // 计算初始的遮罩左上角的坐标
 let objX = e.clientX - this.imgRectNow.left;
 let objY = e.clientY - this.imgRectNow.top;

 // 计算初始的遮罩左上角的坐标
 let maskX = objX - this.configs.maskWidth / 2;
 let maskY = objY - this.configs.maskHeight / 2;

 // 判断是否超出界限,并纠正
 maskY = maskY < 0 ? 0 : maskY; 
 maskX = maskX < 0 ? 0 : maskX; 
 if(maskY + this.configs.maskHeight >= this.imgRectNow.height) {
 maskY = this.imgRectNow.height - this.configs.maskHeight;
 }
 if(maskX + this.configs.maskWidth >= this.imgRectNow.width) {
 maskX = this.imgRectNow.width - this.configs.maskWidth;
 }

 // 遮罩移动
 this.transformMask = `translate(${maskX}px, ${maskY}px)`;

 // 背景图移动
 this.moveLeft = - maskX * this.configs.scale + "px";
 this.moveTop = - maskY * this.configs.scale + "px";
}

handOut:鼠标离开小图事件,此时无放大镜效果,隐藏遮罩和放大区域。

handOut() {
 this.showMagnifier = false;
 this.showMask = false;
}

以上三个事件基本上就实现了图片的放大镜功能。

但仔细看,你会发现每次移入小图框都会触发一次 handOver 事件,并且计算一次小图框 DOM (imgObj) 。

为了优化此问题,可以用 init 标识是否是页面加载后首次触发 handOver 事件,如果是初始化就计算imgObj 信息,否则不计算。

handOver() {
 if (!this.init) {
 this.init = true;
 // 原 handOver 事件
 ...
 } 
 this.showMagnifier = true;
 this.showMask = true;
},

在测试的过程中,发现页面滚动后,会出现遮罩定位错误的情况,原来是因为初始化时,我们固定死了小图框的位置信息(存放在 this.imgRectNow ),导致 handMove 事件中的移动数据计算错误。

解决这个问题有两种方案:

  • 监听 scroll 事件,更新 this.imgRectNow;
  • 在 handMove 事件中更新 this.imgRectNow。

这里选择了第二种。

handMove(e) {
 // 动态获取小图的位置(或者监听 scroll )
 let imgRectNow = this.imgObj.getBoundingClientRect();
 let objX = e.clientX - imgRectNow.left;
 let objY = e.clientY - imgRectNow.top;
 // 原 handMove 事件剩余内容
 ...
},

综合以上,我们已经实现了一个完美的图片放大镜功能。最终的 js 如下所示:

data() {
 return {
 imgObj: {},
 moveLeft: 0,
 moveTop: 0,
 transformMask:`translate(0px, 0px)`,
 showMagnifier:false,
 showMask:false,
 init: false,
 };
},
computed: {
 bigWidth(){
 return this.configs.scale * this.configs.width;
 },
 bigHeight(){
 return this.configs.scale * this.configs.height;
 }
},
methods: {
 handMove(e) {
 // 动态获取小图的位置(或者监听 scroll )
 let imgRectNow = this.imgObj.getBoundingClientRect();
 let objX = e.clientX - imgRectNow.left;
 let objY = e.clientY - imgRectNow.top;

 // 计算初始的遮罩左上角的坐标
 let maskX = objX - this.configs.maskWidth / 2;
 let maskY = objY - this.configs.maskHeight / 2;

 // 判断是否超出界限,并纠正
 maskY = maskY < 0 ? 0 : maskY; 
 maskX = maskX < 0 ? 0 : maskX; 
 if(maskY + this.configs.maskHeight >= imgRectNow.height) {
  maskY = imgRectNow.height - this.configs.maskHeight;
 }
 if(maskX + this.configs.maskWidth >= imgRectNow.width) {
  maskX = imgRectNow.width - this.configs.maskWidth;
 }

 // 遮罩移动
 this.transformMask = `translate(${maskX}px, ${maskY}px)`;

 // 背景图移动
 this.moveLeft = - maskX * this.configs.scale + "px";
 this.moveTop = - maskY * this.configs.scale + "px";
 },
 handOut() {
 this.showMagnifier = false;
 this.showMask = false;
 },
 handOver() {
 if (!this.init) {
  this.init = true;
  this.imgObj = this.$el.getElementsByClassName('small-box')[0];
 }
 this.showMagnifier = true;
 this.showMask = true;
 }
}

使用方法

本示例中的固定参数:小图框:420 * 420 。

程序可接受参数:

// 小图地址
src: {
 type: String,
},
// 大图地址
bigSrc: {
 type: String,
},
// 配置项
configs: {
 type: Object,
 default() {
 return {
  width:420,//放大区域
  height:420,//放大区域
  maskWidth:210,//遮罩
  maskHeight:210,//遮罩
  maskColor:'rgba(25,122,255,0.5)',//遮罩样式
  maskOpacity:0.6,
  scale:2,//放大比例
 };
 }
}

文中图 2 是一张长图,小图的最大边不超过 836px(二倍图) ,大图为了视觉效果,分辨率尽量高点,程序会根据配置项自动设置对应的 height , width ,长图与宽图的效果对比可参考图3。

配置项可根据应用场景自行设置,本文示例的配置项是 2 倍放大,效果可参考图 4,四倍放大效果可参考图 5。

总结

其实图片放大镜的实现思路没有那么复杂,核心点有两点:

  • 小图、大图的定位,遮罩和放大区域的创建方法
  • 放大镜的原理理解,并用代码实现 DOM 的移动等。

本文顺着这个思路,做了一个简单的实现,还有一些优化的空间,欢迎各位大佬在评论区讨论。虽然代码看起来不是非常优雅,但是足够明了,感兴趣的同学可以自己尝试一下。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
理解JavaScript中的事件
Sep 23 Javascript
Javascript 类与静态类的实现
Apr 01 Javascript
bgsound 背景音乐 的一些常用方法及特殊用法小结
May 11 Javascript
取得元素的左和上偏移量的方法
Sep 17 Javascript
jquery html动态添加的元素绑定事件详解
May 24 Javascript
用js实现博客打赏功能
Oct 24 Javascript
js数组与字符串常用方法总结
Jan 13 Javascript
详解Vue的computed(计算属性)使用实例之TodoList
Aug 07 Javascript
详解Angular5路由传值方式及其相关问题
Apr 28 Javascript
vue自动化表单实例分析
May 06 Javascript
react-native android状态栏的实现
Jun 15 Javascript
vue.js实现三级菜单效果
Oct 19 Javascript
vue 实现模糊检索并根据其他字符的首字母顺序排列
Sep 19 #Javascript
微信小程序错误this.setData报错及解决过程
Sep 18 #Javascript
解决layui富文本编辑器图片上传无法回显的问题
Sep 18 #Javascript
layui 富文本赋值,取值,取纯文本值的实例
Sep 18 #Javascript
layui 富文本编辑器和textarea值的相互传递方法
Sep 18 #Javascript
微信小程序获取地理位置及经纬度授权代码实例
Sep 18 #Javascript
layui复选框限制选择个数的方法
Sep 18 #Javascript
You might like
php Smarty 字符比较代码
2011/02/27 PHP
destoon复制新模块的方法
2014/06/21 PHP
PHP开发注意事项总结
2015/02/04 PHP
PHP实现的简单异常处理类示例
2017/05/04 PHP
js中的escape及unescape函数的php实现代码
2007/09/04 Javascript
Jquery下:nth-child(an+b)的使用注意
2011/05/28 Javascript
javascript图片相似度算法实现 js实现直方图和向量算法
2014/01/14 Javascript
JS实现闪动的title消息提醒效果
2014/06/20 Javascript
jquery让返回的内容显示在特定div里(代码少而精悍)
2014/06/23 Javascript
JavaScript中用字面量创建对象介绍
2014/12/31 Javascript
浅析javascript中的Event事件
2016/12/09 Javascript
JS中常用的消息框总结
2018/02/24 Javascript
vue中$set的使用(结合在实际应用中遇到的坑)
2018/07/10 Javascript
angularjs的单选框+ng-repeat的实现方法
2018/09/12 Javascript
vue中el-upload上传图片到七牛的示例代码
2018/10/19 Javascript
详解JavaScript中typeof与instanceof用法
2018/10/24 Javascript
如何测量vue应用运行时的性能
2019/06/21 Javascript
微信小程序 简易计算器实现代码实例
2019/09/02 Javascript
layui table去掉右侧滑动条的实现方法
2019/09/05 Javascript
Python调用SQLPlus来操作和解析Oracle数据库的方法
2016/04/09 Python
Python中字符串的处理技巧分享
2016/09/17 Python
python中的break、continue、exit()、pass全面解析
2017/08/05 Python
Python Json序列化与反序列化的示例
2018/01/31 Python
Python针对给定列表中元素进行翻转操作的方法分析
2018/04/27 Python
Python实现的tcp端口检测操作示例
2018/07/24 Python
详解关于Django中ORM数据库迁移的配置
2018/10/08 Python
Python 实现两个服务器之间文件的上传方法
2019/02/13 Python
django使用django-apscheduler 实现定时任务的例子
2019/07/20 Python
pytorch查看通道数 维数 尺寸大小方式
2020/05/26 Python
Python+OpenCV检测灯光亮点的实现方法
2020/11/02 Python
详解html5 canvas 微信海报分享(个人爬坑)
2018/01/12 HTML / CSS
Timberland俄罗斯官方网上商店:全球领先的户外品牌
2020/03/15 全球购物
治超工作实施方案
2014/05/04 职场文书
室内设计专业自荐信
2014/05/31 职场文书
学生保证书格式
2015/02/27 职场文书
幼儿园春季开学通知
2015/07/16 职场文书