写点什么

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

  • 2021-01-26
  • 本文字数:3771 字

    阅读完需:约 12 分钟

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

前言


在做电商类应用时,难免会遇到商品主图实现放大镜效果的场景,现有的基于Vue的第三方包不多并且无法直接复用,今天,我来分享一种高稳定性的基于 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';}
复制代码


效果演示






核心代码

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,//放大比例    };  }}
复制代码


总结


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


  • 小图、大图的定位,遮罩和放大区域的创建方法

  • 放大镜的原理理解,并用代码实现 dom 的移动等。


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



头图:Unsplash

作者:一玲

原文:https://mp.weixin.qq.com/s/_4TEiywnmre1wdqHxQaPLg

原文:基于 Vue 的商品主图放大镜方案

来源:政采云前端团队 - 微信公众号 [ID:Zoo-Team]

转载:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


2021-01-26 23:472231
用户头像

发布了 78 篇内容, 共 32.0 次阅读, 收获喜欢 330 次。

关注

评论

发布
暂无评论
发现更多内容

校园共享电动单车的积极作用!如何投?

共享电单车厂家

共享电动车厂家 校园共享电动车 共享电动单车投放

架构误区系列16:不可靠的幂等

agnostic

幂等设计

创建本地yum仓库

天翼云开发者社区

常用内核架构

天翼云开发者社区

【等保测评】电力行业等保测评多久做一次?

行云管家

电力 过等保 等级测评

引领文旅新体验!3DCAT实时云渲染助力打造“永不落幕”的湾区文采会元宇宙

3DCAT实时渲染

元宇宙 元宇宙线上虚拟展厅 VR虚拟现实

阿里最新 23版 Java 面试系列手册,竟堪称 GitHub 面试杀手锏

程序知音

Java java面试 后端技术 Java面试题 Java面试八股文

MobTech MobPush|A/B测试提升运营决策

MobTech袤博科技

2023 最新版 Java 面试八股文大全 PDF 版限时分享,含 700 道高频面试题

三十而立

共铸国云智领未来| 智慧医疗乘“云”而上!

天翼云开发者社区

led显示屏视频播放器的8大功能

Dylan

企业 视频 LED显示屏

爆肝了!阿里最新版的这份Spring Security源码手册,狂揽GitHub榜首

做梦都在改BUG

Java spring spring security

JVM调优-Nacos GC引发的服务批量下线问题

程序员小毕

程序员 微服务 后端 nacos jvm调优

ARB链挖矿dapp系统开发模式定制

开发v-hkkf5566

基于容器平台 ACK 快速搭建 Stable Diffusion

阿里巴巴云原生

阿里云 云原生 容器服务

Qz学算法-数据结构篇(表达式、递归)

浅辄

数据结构 三周年连更

直播预告 | 字节跳动云原生大数据分析引擎 ByConity 与 ClickHouse 有何差异?

墨天轮

大数据 字节跳动 Clickhouse 数仓

面试突击:MVCC 和间隙锁有什么区别?

做梦都在改BUG

Java 面试 MVCC

亮点预告!金蝶云·苍穹技术开放日第五期AI专场邀你围观!

金蝶云·苍穹

AI RPA 直播 企业云服务 ChatGPT

袋鼠云春季生长大会圆满落幕,带来数实融合下的新产品、新方案、新实践!

袋鼠云数栈

数字化转型

面试官:什么是双亲委派模型?

做梦都在改BUG

拥抱Serverless释放生产力,探索华为云Serverless车联网最佳实践

华为云开发者联盟

Serverless 车联网 华为云 华为云开发者联盟 企业号 4 月 PK 榜

【转载】三十而已,信智依然 | 田溯宁:写在亚信科技30华诞

亚信AntDB数据库

AntDB AntDB数据库 企业号 4 月 PK 榜

精髓!不愧为京东内部 Spring Boot 全解笔记

程序知音

Java 微服务 后端 springboot Java进阶

基于深度学习框架设计的货运管家(功能总结)

DS小龙哥

三周年连更

DevEco Studio 3.1 Beta新特性知多少

HarmonyOS开发者

HarmonyOS

国外顶级架构师编写2580页DDD领域驱动设计笔记,看到内容后破防了

做梦都在改BUG

Java 架构 领域驱动设计 DDD

一图读懂|ONES X 中国信通院《中国企业软件研发管理白皮书》

万事ONES

基于 Vue 的商品主图放大镜方案_编程语言_政采云前端团队_InfoQ精选文章