不知各位遇到特別長的圖片時是怎么處理的?
是 截取符合長寬的部分做臨時展示?
還是 硬要長寬100%模糊(啥也看不清)展示?
還是 先拿一個壓縮的圖片做占位,在滑鼠移入或點擊時放大預覽?
今天偶然打開PC端QQ空間時,我發現了一種似乎更好的方式 —— 滑鼠移入時在范圍內上下滾動圖片預覽,移出時停止滾動,直到用戶點擊圖片跳轉到詳情展示:

分析
這種方式著實讓我“眼前一亮”,一定程度上帶給了用戶新奇的體驗感,順著思路,一鍵 f12 打開原始碼,我看到了這樣的代碼:


顯而易見,QQ應該是采用了js監聽滑鼠位置的做法,動態改變 img 標簽中自定義屬性的值,并根據此去改變圖片的 margin-top 值,用 transition 屬性去制造影片效果,
模擬實作
為了方便些,這里筆者采用兩個空標簽分割“包裹圖片的元素”,然后分別在上面監聽滑鼠事件的做法,實作效果如下:

首先,“科普”幾個API,它們將會是你的助力:
image.naturalHeight:看到前面的image沒?這是用來獲取圖片原始高度的(同系的還有image.naturalWidth,你可以用它來確定包裹元素的最大/最小寬度);dom.offsetTop:offset系的API,用來獲取dom元素和離它最近的父元素頂部的距離(同系的還有offsetLeft、offsetWidth/offsetHeight(回傳元素的像素寬高,包含該元素的內邊距和邊框,是一個整數且不包含:before或:after等偽類元素的寬高)、offsetParent(獲取父元素));dom.getBoundingClientRect():它有四個常用值:left、top、right、bottom,分別是相對于當前視口(即此tab網頁視窗左側、頂部、右側、底部)的位置;dom.scrollHeight:scroll系的API,用來獲取元素的真實高度(同系的還有scrollWidth/scrollLeft/scrollTop),一般不會用它來作用于圖片上,因為它必須等元素加載出來才能確定;window.innerHeight:inner系的API,它們只作用在window物件上,回傳視窗的檔案顯示區的高度(同系的還有一個window.innerWidth) <-> 相對的兩個outerWidth和outerHeight,用于獲取加上工具條與滾動條視窗的寬度與高度;
順便說一句,像
img.getBoundingClientRect().top、img.offsetTop這些都是 只讀 值,所以不要妄想用它們來改變元素位置!
布局如下:
<div class="box">
<i class="before"></i>
<img src="img/nan.png" class="img" />
<i class="after"></i>
</div>
這里class為before和after的兩個標簽就是前面所說的“占位”元素(至于QQ是怎么實作的,等筆者稍作研究后再回來更新),它們負責判斷“圖片是應該向上滑還是向下滑”!
本來這里筆者想采用偽元素的方式:用
::before和::after占位并觸發事件,但是在查遍資料以后我突然想到一件事:不是經常說偽元素的優勢是脫離檔案流嗎?那還如何能夠獲取到?
唉,大意了,,,
html,body{
margin: 0;
padding: 0;
}
.box{
width: 400px;
height: 200px;
overflow: hidden;
position: relative;
}
.lang::after{
content: "長圖";
position: absolute;
right: 0;
bottom: 0;
padding: 2px 3px;
background-color: rgba(0,0,0,.36);
color: white;
}
i{
position: absolute;
width: 100%;
height: 50%;
left: 0;
}
i.before{
top: 0;
}
i.after{
bottom: 0;
}
.img{
margin-top: 0;
transition: all 2s linear;
}
對img元素設定一個初始的margin-top,就是為了配合下面的transition使得在js中改變top值時能夠有影片效果!
有了上面的布局方式和API解讀,其實js實作就非常簡單了 —— 根據上面分析的按部就班來就行:
let box=document.querySelector('.box');
let img=document.querySelector('.img');
let i_before=document.querySelector('i.before');
let i_after=document.querySelector('i.after');
let box_height=box.offsetHeight;
let img_height=img.naturalHeight;
// 只有圖片高度大于盒子高度時才有下面的事件
if(img_height>box_height){
console.log(1)
box.classList.add('lang');
let img_top=0;
// 滑鼠移入下半部分時圖片向下滑動
i_after.addEventListener('mouseenter',(e)=>{
console.log(img.offsetTop)
img.style.marginTop=-(img_height-box_height)+'px';
},false)
i_after.addEventListener('mouseout',(e)=>{
console.log(img.offsetTop)
img_top=img.offsetTop;
img.style.marginTop=img.offsetTop+'px';
},false)
// 滑鼠移入上半部分時圖片向上滑動
i_before.addEventListener('mouseenter',(e)=>{
if(img_top){
img.style.marginTop=0;
}
},false)
i_before.addEventListener('mouseout',(e)=>{
if(img_top){
img.style.marginTop=img.offsetTop+'px';
}
},false)
}
至此,效果就全部實作了,
但是如果你仔細看,你會發現由于transition影片效果的時間是固定的,在向上/下滑動過短的情況下再向下/上滑動那么滑動的會特別慢!
!當然,我們可以改變策略,讓圖片的 margin-top 不斷--或++ 直到臨界值,但這樣勢必會帶來巨大的性能開銷,
再回到PC端QQ空間 —— 我們發現,它的transition時間竟然是動態變化的:

這…我猜測可能是設定了一個從上到下固定的時間,然后在JS中按斬訓出部分高度(已經滑動的距離)占總高度的比例動態調節時間,,,相關代碼筆者正在嘗試ing
當然,本文對QQ前端團隊對圖片的處理來說也許只是滄海一粟,,,更多的還有比如:根據圖片整體平均色差調整說明文字的顏色黑/白(canvas-getImageData API)、圖片內容的延遲展示、多圖上傳性能調優(promise API)等等,
那咱就 再見了?
CSDN認證博客專家
ECMAScript 6
Node.js
CSS
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/262943.html
標籤:其他
