前言
随手打开一个电商网站(如淘宝),查看商品的时候, 把鼠标放到图片上都可以看到图片的更多细节,像把图片局部放大了一样(效果如下GIF)。这篇文章就是讲述这个放大器的实现。
原理
在左边的盒子中放入图片的 缩略图 ,右边的盒子只有在鼠标放上去的时候显示,并且放置的是对应图片的大图,这个大图只在右边盒子中显示出局部。
鼠标放在左边缩略图的时候,会显示出一个遮罩层(mask), 这个 mask 覆盖缩略图的位置,会在右边的盒子中“局部放大”显示。更确切的说是只显示右边图片的局部。
图中 蓝色区域 代表遮罩层, 绿色方框 代表放大图的原始大小,而 红色方框 就是我们看到的局部放大部分。 注意,这里有个比例显示要求:遮罩层大小 / 缩略图大小 = 放大部分 / 图片原始大小
当鼠标在左边缩略图中移动时,会带着这个遮罩层移动。获取遮罩层(mask)在小图中的位置,然后改变对应大图在右边盒子中局部显示的位置
当遮罩层向下移动的时候,这个大图向上等比例移动,就会显示对应的局部放大区域。只要把溢出的部分隐藏(overflow:hidden)即可达到放大效果。
图中黑色箭头是一个比例示例: 比如遮罩层mask 100px, 放大显示区域=缩略图显示区域=200px, 原图大小400px (宽高同理)
我前言的时候有说到这个放大器的时候,提到了一个“像”字,为何那么说?
因为这个左边的缩略图片,与右边的图片并非同一张图片。同一张图片放大之后查看局部是会变模糊的,而淘宝等电商网站放大图仍是清晰的,这是因为右边的放大图是左边这个缩略图的高清放大版。你看请求或者在网络状态不佳的情况下加载都可以知道加载了两个不同大小的图。
这样做的目的应该是为了加载网页的时候更快展现商品,而要查看细节的话需要另行加载。
实现
我用JS与jquery都实现了一遍, jquery代码约60行,整体实现还是比较简单的。这里就讲解一下主要代码及思路,具体细节欢迎访问我的GitHub上查看。
html与css部分比较简单,就是有两个小点需要注意,等下说。
(img标签貌似在简书的code中打不出,自行意会正确写法吧)
<div class="box">
<div class="small">
<!-- ![](img/123-mini.jpg) -->
<div class="mask hide"></div>
</div>
<div class="big hide">
![](img/123-large.jpg)
</div>
</div>
缩略图的部分 可以使用<img />
标签, 也可用background
,或者直接引入背景图片。不过要注意这个盒子的大小要由CSS指定,并且要让图片显示完整。我使用background: url("...")
来放小图,是因为background-size
控制背景图大小比较方便。
而大图部分,最好使用标签展示,这样方便控制 显示出来的位置。使用背景图就要控制background-position
,实现起来相对麻烦些(具体实现我还没试过)。
定义类: class="hide"
即默认该元素是隐藏的。用类来体现显示 / 隐藏,方便JS控制样式。
我这里没有写下面的列表部分,因为这个比较简单,而且与放大器功能也无关。
css
这部分也比较简单, 简单叙述一下定位和hover效果的问题:
- 遮罩层和
big
盒子部分应该设置为 绝对定位 且起始是 隐藏 的,父元素 相对定位。这样子可以让遮罩层在父元素中移动。 - 其中
.small
与.big
两个对应的元素,要注意图片大小和之前提过的比例显示相符。
遮罩层的显示,我这里参考了 《css secrets》 里面的点阵代码,与淘宝的实现有点不同,先贴代码再说:
.mask {
width: 205px;
height: 205px;
/* background-image: url('maskHover.png'); */
background-image: radial-gradient(rgba(124,165,236, 0.4) 30%, transparent 0),
radial-gradient(rgba(124,165,236, 0.4) 30%, transparent 0);
background-size: 3px 3px;
background-position: 0 0, 3px 3px;
position: absolute;
top: 0;
left: 0;
cursor: move;
}
注释的那行是淘宝的实现方式,比较简单粗暴:引用一张背景图,把我从 background-image
到 background-position
部分的代码都省略了。
授人以鱼不如授人以渔,如何获取这个图片: 随手打开淘宝网站的一个商品页面,然后鼠标放到缩略图上的时候审查元素,会看到一个 span
标签及其css样式,然后就会看到 background: url(...)
,这个url里面 //
代表该url协议与当前网站协议相同,即背景图片网址为:
https://gtms01.alicdn.com/tps/i4/T12pdtXaldXXXXXXXX-2-2.png
打开这个网址的时候是一片黑的,不要紧,下载过来引用进去就是一样的效果了。
好了,鱼和渔都有了, 下面可以开始JS了。
JS
这里省略 显示 / 隐藏 盒子&mask 的JS内容,直接到渲染遮罩层区域及移动的部分:
先获取遮罩层的左上角的位置,这是为了能够用JS来控制这个盒子的位置然后加到页面中去,这是因为遮罩层的大小已经固定了,只要定好一个点的位置,那么它在缩略图中的区域就是固定下来的。
获取遮罩层左上角坐标的步骤:
- 获取鼠标在缩略图区域中的坐标(鼠标在浏览器中的位置 - 盒子距离浏览器顶部的位置):
localX
&localY
- 再用
localX
&localY
, 减去遮罩层的宽高的一半。
因为我们的遮罩层是跟随鼠标移动的,所以要先获取鼠标的坐标再去计算遮罩层的位置。
let maskX = localX - $(".mask").outerWidth(true) / 2;
let maskY = localY - $(".mask").outerHeight(true) / 2;
然后把遮罩层加到页面上,但在这之前要做一个是否溢出的判断。
即判断我们获取的这个 maskX
的坐标是否在这个盒子里面:如果溢出,就改变左上角的坐标使得盒子与边界部分重叠。这时候就实现:到达边界的时候,鼠标可以在遮罩层的靠近边界区域随意移动而遮罩层不动。 (完成了 遮罩层在缩略图区域中大小不会改变 的需求)
边界处理:
// 判断mask是否越界, 1\2个if判断是否左右溢出
// 3\4 if判断 是否上下溢出
if(maskX < 0) {
maskX = 0;
}
if(maskX > $(".small").outerWidth(true) - $(".mask").outerWidth(true)){
maskX = $(".small").outerWidth(true) - $(".mask").outerWidth(true);
}
if(maskY < 0) {
maskY = 0;
}
if(maskY > $(".small").outerHeight(true) - $(".mask").outerHeight(true)){
maskY = $(".small").outerHeight(true) - $(".mask").outerHeight(true);
}
// 改变mask的位置, 把遮罩层加到页面上
$('.mask').css({
left: maskX + "px",
top: maskY + "px"
})
最后也就是最关键的一部分: 等比例移动大图
要先获得这个比例(ratio):大图的宽度 / 缩略图显示的宽度 = 图片移动的距离 / 遮罩层mask移动的距离
let ratio = parseInt($(".big img").css("width")) / $(".small").outerWidth(true);
// 确定图片移动的距离
let imgX = ratio * maskX;
let imgY = ratio * maskY;
// 改变图片显示的左上角的位置 来移动图片
$(".big img").css({
marginTop: -imgY + "px",
marginLeft: -imgX + "px"
})
获取完整代码请点击文章末尾处链接
顺带一提一个在线工具来调整图片尺寸,这样展示的时候比较方便:美图秀秀网页版