基于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中的String对象详谈
Mar 03 Javascript
javascript中的事件代理初探
Mar 08 Javascript
纯js和css实现渐变色包括静态渐变和动态渐变
May 29 Javascript
手机端网页点击链接触发自动拨打或保存电话的示例代码
Aug 15 Javascript
Javascript前端UI框架Kit使用指南之kitjs事件管理
Nov 28 Javascript
js实现图片从左往右渐变切换效果的方法
Feb 06 Javascript
javascript图片预加载实例分析
Jul 16 Javascript
微信小程序技巧之show内容展示,上传文件编码问题
Jan 23 Javascript
Mui使用jquery并且使用点击跳转新窗口的实例
Aug 19 jQuery
JS获取input[file]的值并显示在页面的实现方法
Mar 09 Javascript
js将日期格式转换为YYYY-MM-DD HH:MM:SS
Sep 18 Javascript
vue+element ui实现锚点定位
Jun 29 Vue.js
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保存Base64图片base64_decode的问题整理
2019/11/04 PHP
网页自动刷新,不产生嗒嗒声的一个解决方法
2007/03/27 Javascript
用js实现的检测浏览器和系统的函数
2009/04/09 Javascript
基于Jquery的温度计动画效果
2010/06/18 Javascript
jQuery简单实现图片预加载
2015/04/20 Javascript
JS实现随机乱撞彩色圆球特效的方法
2015/05/05 Javascript
js+css实现文字散开重组动画特效代码分享
2015/08/21 Javascript
一个字符串中出现次数最多的字符 统计这个次数【实现代码】
2016/04/29 Javascript
JavaScript ES6的新特性使用新方法定义Class
2016/06/28 Javascript
prototype与__proto__区别详细介绍
2017/01/09 Javascript
jQuery.cookie.js使用方法及相关参数解释
2017/03/06 Javascript
JS实现数组按升序及降序排列的方法
2017/04/26 Javascript
使用Webpack提高Vue.js应用的方式汇总(四种)
2017/07/10 Javascript
浅谈vue单一组件下动态修改数据时的全部重渲染
2018/03/01 Javascript
JavaScript使用递归和循环实现阶乘的实例代码
2018/08/28 Javascript
angular6的响应式表单的实现
2018/10/10 Javascript
vue项目前端错误收集之sentry教程详解
2019/05/27 Javascript
微信小程序开发常见问题及解决方案
2019/07/11 Javascript
微信小程序实现多行文字超出部分省略号显示功能
2019/10/23 Javascript
解决vue watch数据的方法被调用了两次的问题
2020/11/07 Javascript
[01:26]DOTA2荣耀之路2:iG,China
2018/05/24 DOTA
详解python脚本自动生成需要文件实例代码
2017/02/04 Python
python实现12306火车票查询器
2017/04/20 Python
解决pandas.DataFrame.fillna 填充Nan失败的问题
2018/11/06 Python
Python 200行代码实现一个滑动验证码过程详解
2019/07/11 Python
python 利用jinja2模板生成html代码实例
2019/10/10 Python
python基于celery实现异步任务周期任务定时任务
2019/12/30 Python
用纯CSS3实现网页中常见的小箭头
2017/10/16 HTML / CSS
John Hardy官方网站:手工设计首饰的奢侈品牌
2017/07/05 全球购物
英国最大的化装舞会服装网站:Fancydress.com
2017/08/15 全球购物
科技之星事迹材料
2014/06/02 职场文书
入党转正申请报告
2015/05/15 职场文书
导游经典开场白——导游词
2019/04/17 职场文书
python神经网络ResNet50模型
2022/05/06 Python
Python使用pyecharts控件绘制图表
2022/06/05 Python
pandas时间序列之pd.to_datetime()的实现
2022/06/16 Python