放大器组件

前言

随手打开一个电商网站(如淘宝),查看商品的时候, 把鼠标放到图片上都可以看到图片的更多细节,像把图片局部放大了一样(效果如下GIF)。这篇文章就是讲述这个放大器的实现。


放大器

原理

在左边的盒子中放入图片的 缩略图 ,右边的盒子只有在鼠标放上去的时候显示,并且放置的是对应图片的大图,这个大图只在右边盒子中显示出局部。

鼠标放在左边缩略图的时候,会显示出一个遮罩层(mask), 这个 mask 覆盖缩略图的位置,会在右边的盒子中“局部放大”显示。更确切的说是只显示右边图片的局部。

初始原理图

图中 蓝色区域 代表遮罩层, 绿色方框 代表放大图的原始大小,而 红色方框 就是我们看到的局部放大部分。 注意,这里有个比例显示要求:遮罩层大小 / 缩略图大小 = 放大部分 / 图片原始大小

当鼠标在左边缩略图中移动时,会带着这个遮罩层移动。获取遮罩层(mask)在小图中的位置,然后改变对应大图在右边盒子中局部显示的位置

当遮罩层向下移动的时候,这个大图向上等比例移动,就会显示对应的局部放大区域。只要把溢出的部分隐藏(overflow:hidden)即可达到放大效果。


移动mask & 比例显示

图中黑色箭头是一个比例示例: 比如遮罩层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-imagebackground-position 部分的代码都省略了。

授人以鱼不如授人以渔,如何获取这个图片: 随手打开淘宝网站的一个商品页面,然后鼠标放到缩略图上的时候审查元素,会看到一个 span 标签及其css样式,然后就会看到 background: url(...) ,这个url里面 // 代表该url协议与当前网站协议相同,即背景图片网址为:
https://gtms01.alicdn.com/tps/i4/T12pdtXaldXXXXXXXX-2-2.png

打开这个网址的时候是一片黑的,不要紧,下载过来引用进去就是一样的效果了。


获取mask背景图

好了,鱼和渔都有了, 下面可以开始JS了。

JS

这里省略 显示 / 隐藏 盒子&mask 的JS内容,直接到渲染遮罩层区域及移动的部分:

先获取遮罩层的左上角的位置,这是为了能够用JS来控制这个盒子的位置然后加到页面中去,这是因为遮罩层的大小已经固定了,只要定好一个点的位置,那么它在缩略图中的区域就是固定下来的。

获取遮罩层左上角坐标的步骤:

  • 获取鼠标在缩略图区域中的坐标(鼠标在浏览器中的位置 - 盒子距离浏览器顶部的位置): localX & localY
  • 再用 localX & localY , 减去遮罩层的宽高的一半。
    因为我们的遮罩层是跟随鼠标移动的,所以要先获取鼠标的坐标再去计算遮罩层的位置。
    获取mask坐标
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"
    })    

获取完整代码请点击文章末尾处链接

顺带一提一个在线工具来调整图片尺寸,这样展示的时候比较方便:美图秀秀网页版

调整图片尺寸使用

效果展示及源码

预览地址
源码地址

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容