前言
在做电商类应用时,难免会遇到商品主图实现放大镜效果的场景,现有的基于 Vue
的第三方包不多并且无法直接复用,今天,我来分享一种高稳定性的基于 Vue
的图片放大镜方法。
实现原理
放大镜的原理用一句话概括,就是根据小图上的鼠标位置去定位大图。
图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="/UploadFiles/2021-04-02/`${src}">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 "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 "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 的移动等。
本文顺着这个思路,做了一个简单的实现,还有一些优化的空间,欢迎各位大佬在评论区讨论。虽然代码看起来不是非常优雅,但是足够明了,感兴趣的同学可以自己尝试一下。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
P70系列延期,华为新旗舰将在下月发布
3月20日消息,近期博主@数码闲聊站 透露,原定三月份发布的华为新旗舰P70系列延期发布,预计4月份上市。
而博主@定焦数码 爆料,华为的P70系列在定位上已经超过了Mate60,成为了重要的旗舰系列之一。它肩负着重返影像领域顶尖的使命。那么这次P70会带来哪些令人惊艳的创新呢?
根据目前爆料的消息来看,华为P70系列将推出三个版本,其中P70和P70 Pro采用了三角形的摄像头模组设计,而P70 Art则采用了与上一代P60 Art相似的不规则形状设计。这样的外观是否好看见仁见智,但辨识度绝对拉满。
更新日志
- 小骆驼-《草原狼2(蓝光CD)》[原抓WAV+CUE]
- 群星《欢迎来到我身边 电影原声专辑》[320K/MP3][105.02MB]
- 群星《欢迎来到我身边 电影原声专辑》[FLAC/分轨][480.9MB]
- 雷婷《梦里蓝天HQⅡ》 2023头版限量编号低速原抓[WAV+CUE][463M]
- 群星《2024好听新歌42》AI调整音效【WAV分轨】
- 王思雨-《思念陪着鸿雁飞》WAV
- 王思雨《喜马拉雅HQ》头版限量编号[WAV+CUE]
- 李健《无时无刻》[WAV+CUE][590M]
- 陈奕迅《酝酿》[WAV分轨][502M]
- 卓依婷《化蝶》2CD[WAV+CUE][1.1G]
- 群星《吉他王(黑胶CD)》[WAV+CUE]
- 齐秦《穿乐(穿越)》[WAV+CUE]
- 发烧珍品《数位CD音响测试-动向效果(九)》【WAV+CUE】
- 邝美云《邝美云精装歌集》[DSF][1.6G]
- 吕方《爱一回伤一回》[WAV+CUE][454M]