前言
隨手打開一個電商網站(如淘寶),查看商品的時候, 把鼠標放到圖片上都可以看到圖片的更多細節(jié),像把圖片局部放大了一樣(效果如下GIF)。這篇文章就是講述這個放大器的實現(xiàn)。
原理
在左邊的盒子中放入圖片的 縮略圖 ,右邊的盒子只有在鼠標放上去的時候顯示,并且放置的是對應圖片的大圖,這個大圖只在右邊盒子中顯示出局部。
鼠標放在左邊縮略圖的時候,會顯示出一個遮罩層(mask), 這個 mask 覆蓋縮略圖的位置,會在右邊的盒子中“局部放大”顯示。更確切的說是只顯示右邊圖片的局部。
圖中 藍色區(qū)域 代表遮罩層, 綠色方框 代表放大圖的原始大小,而 紅色方框 就是我們看到的局部放大部分。 注意,這里有個比例顯示要求:遮罩層大小 / 縮略圖大小 = 放大部分 / 圖片原始大小
當鼠標在左邊縮略圖中移動時,會帶著這個遮罩層移動。獲取遮罩層(mask)在小圖中的位置,然后改變對應大圖在右邊盒子中局部顯示的位置
當遮罩層向下移動的時候,這個大圖向上等比例移動,就會顯示對應的局部放大區(qū)域。只要把溢出的部分隱藏(overflow:hidden)即可達到放大效果。
圖中黑色箭頭是一個比例示例: 比如遮罩層mask 100px, 放大顯示區(qū)域=縮略圖顯示區(qū)域=200px, 原圖大小400px (寬高同理)
我前言的時候有說到這個放大器的時候,提到了一個“像”字,為何那么說?
因為這個左邊的縮略圖片,與右邊的圖片并非同一張圖片。同一張圖片放大之后查看局部是會變模糊的,而淘寶等電商網站放大圖仍是清晰的,這是因為右邊的放大圖是左邊這個縮略圖的高清放大版。你看請求或者在網絡狀態(tài)不佳的情況下加載都可以知道加載了兩個不同大小的圖。
這樣做的目的應該是為了加載網頁的時候更快展現(xiàn)商品,而要查看細節(jié)的話需要另行加載。
實現(xiàn)
我用JS與jquery都實現(xiàn)了一遍, jquery代碼約60行,整體實現(xiàn)還是比較簡單的。這里就講解一下主要代碼及思路,具體細節(jié)歡迎訪問我的GitHub上查看。
html與css部分比較簡單,就是有兩個小點需要注意,等下說。
(img標簽貌似在簡書的code中打不出,自行意會正確寫法吧)
<div class="box">
<div class="small">
<!--  -->
<div class="mask hide"></div>
</div>
<div class="big hide">

</div>
</div>
縮略圖的部分 可以使用<img />
標簽, 也可用background
,或者直接引入背景圖片。不過要注意這個盒子的大小要由CSS指定,并且要讓圖片顯示完整。我使用background: url("...")
來放小圖,是因為background-size
控制背景圖大小比較方便。
而大圖部分,最好使用標簽展示,這樣方便控制 顯示出來的位置。使用背景圖就要控制background-position
,實現(xiàn)起來相對麻煩些(具體實現(xiàn)我還沒試過)。
定義類: class="hide"
即默認該元素是隱藏的。用類來體現(xiàn)顯示 / 隱藏,方便JS控制樣式。
我這里沒有寫下面的列表部分,因為這個比較簡單,而且與放大器功能也無關。
css
這部分也比較簡單, 簡單敘述一下定位和hover效果的問題:
- 遮罩層和
big
盒子部分應該設置為 絕對定位 且起始是 隱藏 的,父元素 相對定位。這樣子可以讓遮罩層在父元素中移動。 - 其中
.small
與.big
兩個對應的元素,要注意圖片大小和之前提過的比例顯示相符。
遮罩層的顯示,我這里參考了 《css secrets》 里面的點陣代碼,與淘寶的實現(xiàn)有點不同,先貼代碼再說:
.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;
}
注釋的那行是淘寶的實現(xiàn)方式,比較簡單粗暴:引用一張背景圖,把我從 background-image
到 background-position
部分的代碼都省略了。
授人以魚不如授人以漁,如何獲取這個圖片: 隨手打開淘寶網站的一個商品頁面,然后鼠標放到縮略圖上的時候審查元素,會看到一個 span
標簽及其css樣式,然后就會看到 background: url(...)
,這個url里面 //
代表該url協(xié)議與當前網站協(xié)議相同,即背景圖片網址為:
https://gtms01.alicdn.com/tps/i4/T12pdtXaldXXXXXXXX-2-2.png
打開這個網址的時候是一片黑的,不要緊,下載過來引用進去就是一樣的效果了。
好了,魚和漁都有了, 下面可以開始JS了。
JS
這里省略 顯示 / 隱藏 盒子&mask 的JS內容,直接到渲染遮罩層區(qū)域及移動的部分:
先獲取遮罩層的左上角的位置,這是為了能夠用JS來控制這個盒子的位置然后加到頁面中去,這是因為遮罩層的大小已經固定了,只要定好一個點的位置,那么它在縮略圖中的區(qū)域就是固定下來的。
獲取遮罩層左上角坐標的步驟:
- 獲取鼠標在縮略圖區(qū)域中的坐標(鼠標在瀏覽器中的位置 - 盒子距離瀏覽器頂部的位置):
localX
&localY
- 再用
localX
&localY
, 減去遮罩層的寬高的一半。
因為我們的遮罩層是跟隨鼠標移動的,所以要先獲取鼠標的坐標再去計算遮罩層的位置。
獲取mask坐標
let maskX = localX - $(".mask").outerWidth(true) / 2;
let maskY = localY - $(".mask").outerHeight(true) / 2;
然后把遮罩層加到頁面上,但在這之前要做一個是否溢出的判斷。
即判斷我們獲取的這個 maskX
的坐標是否在這個盒子里面:如果溢出,就改變左上角的坐標使得盒子與邊界部分重疊。這時候就實現(xiàn):到達邊界的時候,鼠標可以在遮罩層的靠近邊界區(qū)域隨意移動而遮罩層不動。 (完成了 遮罩層在縮略圖區(qū)域中大小不會改變 的需求)
邊界處理:
// 判斷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"
})
獲取完整代碼請點擊文章末尾處鏈接
順帶一提一個在線工具來調整圖片尺寸,這樣展示的時候比較方便:美圖秀秀網頁版