实现原理

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

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

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

显然,两块蓝色区域存在着某种对应关系,即遮罩的左上角位置(相对于小图,以下称 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 长图展示

图3 宽图展示

图4 两倍放大效果图

图5 四倍放大效果图

核心代码

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 的移动等。

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

作者:政采云前端团队
链接:https://juejin.im/post/5d8235565188256bbe57dc84
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

基于 Vue 的商品主图放大镜方案相关推荐

  1. 【每日一练】原生js仿淘宝主图放大镜功能,附学习源码

    在我们的项目中,经常会遇到各种功能效果的实现,对于每一项功能的实现方式,都有很多种,这些实现方式没有好坏之分,只有适合与否,但是我个人建议,如果项目急就选择自己擅长的方式实现,比较完成工作更加重要嘛. ...

  2. 第37课 thinkphp5添加商品基本信息及通过前置钩子上传商品主图 模型事件(勾子函数)...

    目录 手册地址: before_insert(新增之前的操作) 要实现的功能 思路 触发条件: 1. 控制器里必须要调用模型的save()方式保存数据,用insert()触发不了勾子函数的 2. 模型 ...

  3. php上传商品信息并显示,第37课 thinkphp5添加商品基本信息及通过前置钩子上传商品主图 模型事件(勾子函数)...

    [TOC] 手册地址: before_insert(新增之前的操作) 要实现的功能 上传原图片,在新增数据之前生成三张缩略图片,然后再插入数据 添加商品基本信息及通过后置钩子上传商品主图 思路 控制器 ...

  4. 教你一键采集天猫商品主图视频的方法及步骤

    多个天猫商品的主图视频要怎么一键采集保存到电脑上呢?看看大家是如何用"视频下载高手"软件来省时间采集的,现在跟小编一起看下操作步骤吧. 已下载的商品主图视频展示 准备所需要的软件 ...

  5. 教您如何批量采集1688商城多个商品主图和详情页并统一保存

    下面先让大伙浏览一组,就是运用载图助手电商图片及视频采集专用工具,所下载好的一些真实商品主图原图,和详情图以及视频等.详情操作一会儿给一一给大家介绍哈. 一.软件的智能分类保存功能 1.所下载好的文件 ...

  6. 京东商品主图怎么保存?如何正确的保存到原图?

    各位京东商家们,如果您在京东商城看到了好看的京东商品图片,是不是有想把它保存起来的冲动呢?告诉您是可以把图片保存起来的哦,更可以用到自己的京东店铺上去的,可您们知道京东商品主图怎么保存到原图的吗?如果 ...

  7. 如何整店导出天猫店铺商品主图及详情图

    怎样一键导出天猫的整店商品图片包含主图.详情图.属性图.及主图视频,今天小编选用一个好方法并能快速导出并分类保存,一起来看看. 1.双击打开载图助手,还没有安装的朋友可在百度上搜索并安装,如图: 2. ...

  8. 淘宝商品详情页API接口|tb获取商品主图接口

    用到淘宝商品详情页API接口的用户,大部分是做电商软件,电商平台,商家等,使用到的比较常用的淘宝详情页API接口包括商品价格,商品主图,商品标题,SKU,店铺名称等等,还有以下几种: 淘宝商品详情页A ...

  9. 批量保存拼多多批发商城商品主图及视频

    怎样可以一键采集出拼多多批发商城上多个商品主图及主图视频,并可以自动分类保存?其实现在在电商领域和一些影集图片的爱好收藏者,都在广泛使用一款叫载图助手的软件,其操作简单,支持平台广,均可批量采集,分类 ...

最新文章

  1. java清空栈_java - 如何使用Intent.FLAG_ACTIVITY_CLEAR_TOP清除活动堆栈?
  2. java中文乱码解决之道(二)—–字符编码详解:基础知识 + ASCII + GB**
  3. Linux打过cat没有编码,linux系统 终端下 cat中文乱码/vim不乱码 或者 cat不乱码/vim中文乱码...
  4. I2C 简介(备忘)
  5. 大学python笔记_Introduction to Python课程笔记
  6. python中解决中文乱码
  7. 边缘和智能,是谁在借谁上位?
  8. tp5 iis7 404 解决方案
  9. java中比较两个日期的大小
  10. 用计算机求锐角A,B,计算机操作题
  11. java如何引入qq登陆,Java Swing仿QQ登录界面 学习之用
  12. 术语html的含义是,术语html指的是什么
  13. 采集新浪微博数据建设网络舆情监测系统
  14. 关于VLAN的几种接口模式
  15. 【Java教程】Java 抽象工厂模式
  16. 华为服务器通过ilo虚拟光驱,如何通过ilo开启服务器远程桌面
  17. 冰点还原精灵图标不见了怎么办?
  18. python lncrna_使用CPAT分析lncRNA
  19. VUE中fetch结合支付宝API验证银行卡号
  20. echarts 双Y轴,双X轴, 折线图折点,折点与直方对应

热门文章

  1. Perfectly Clear WorkBench v4.0.0.2182 图像后期处理调色工具
  2. 数字货币区块链服务器交易平台面临着哪些安全威胁?
  3. 华为一日游 - 坂田基地家庭日见闻
  4. C# 或C++ 控制系统光驱弹出或关闭
  5. iOS 14:如何在iPhone和iPad上增强语音备忘录录音
  6. android-adb 打开手机端口进行无线模式调试
  7. 国内外著名IT公司技术岗位要求
  8. react初探-e.target为null
  9. 魔域私服怎么老服务器中断,教你如何在服务器重启后快速杀BOSS
  10. 校招行测笔试-数量关系