在討論之前,必須先認識等高響應佈局是種怎樣的佈局:
等高響應式局不同於瀑布流,瀑布流是指 ”內容寬度相同,高度不同且依高度排列排列下一行的項目”。
而等高響應佈局則是 ”每一行的內容同高、每一行切齊頭尾且內容與內容之間的寬度固定”,
可以參考 PEXELS 的呈現方式
等高響應佈局通常用於圖片呈現,訴求能讓各種不同比例、大小的圖片,簡單乾淨的呈現在佈局上,在動手寫程式之前,先把需求整理條列如下:
- 每一行的高度相當
- 自動分配圖片屬於哪一行
- 自動拉伸項目切齊頭尾
<div class="foo">
<ul>
<li><a href="#"><img src="#"></a></li>
<li><a href="#"><img src="#"></a></li>
...
</ul>
<div>
為了避免空白與斷行字元的干擾,因此 a、img 的 display 都設為 block,並且讓 li 置左對齊 float: left,樣式設定如下。
.foo {
margin: 40px;
background-color: #ddd;
}
ul {
padding: 0; /*清除預設樣式*/
list-style: none;
}
ul::after { /*清除浮動*/
content: '\200B'; /*空字元*/
display: block;
height: 0;
visibility: hidden;
clear: both;
}
li {
float: left;
}
a {
<data></data>isplay: block;
}
img {
display: block;
width: auto;
height: auto;
}
首先,必須先定義一個基礎高度,匹配給所有圖片,辨別該圖片的歸屬行數:
第三步,計算該行所有圖片相加的總寬度,並求出總寬度與佈局寬度的比例:
第四步,依序放大圖片:
知道概念之後,我們開始寫程式吧!
在範例中,必須等到圖片加載完成,js 才能抓圖片高度:
window.load = function(){
...
}
先宣告想要的高度:
var _height = 200;
接著要抓取 .foo 節點,算出他的寬度。當然也要抓取所有的 img:
var $env = document.querySelector('.foo'), //抓 .foo
$env_w = $env.getBoundingClientRect().width, //取得 .foo 的寬
$imgs = $env.querySelectorAll('img'); //抓所有的 img
此時建立一個叫 Item 的類別,它的項目實例會記錄 img 的節點與寬度資訊。
我們可以預先把計算過的資訊放進去:
var Item = function(imgNode){
this.node = imgNode;
this.nodeInfo = this.node.getBoundingClientRect();
this.width = this.nodeInfo.width * ( _height / this.nodeInfo.height ); //存放預先計算過的寬度資訊
this.height = this.nodeInfo.height;
}
當然,我們也需要一個方法調整項目中的寬/高比例,一個方法設定真正的 node 寬/高,最好是寫在類別原型中:
Item.prototype.reSize = function(prop){ //調整實例寬高的方法,參數是一個比例
this.width *= prop;
this.height *= prop;
}
Item.prototype.setSize = function(){ //設定圖片寬高的方法
this.node.style.width = this.width + 'px';
this.node.style.height = this.height + 'px';
}
有了方法之後,我們可以開始讓所有 img 生產項目實例了,別忘了用一個陣列把它們通通存起來:
var $items = [];
for( var i = 0; i < $imgs.length; i++ ){
var $obj = new Item($imgs[i]);
$items.push($obj);
}
我們得到了一個塞滿項目的陣列 $items,接下來該來分配行數歸屬了。
規則是圖片寬度一一相加,一但大於 .foo 的寬度時,我們會做一些事情,最後讓最後一個相加,且超出 .foo 寬度的項目等於下一行的第一個 item。
var _lineWidth = 0, //存放這一行圖片寬度相加的值
_firstItem = 0; //第一個項目
for( var i = 0; i < $items.length; i++ ){
var $obj = $items[i],
$obj_w = $obj.width;
_lineWidth += $obj_w;
if( _lineWidth > $env_w ) { //檢測寬度是否在一行內,若大於一行寬度,則安排至下一行
//做一些事情...
_firstItem = i;
_lineWidth = $obj_w;
}
}
一但 _lineWidth 大於 .foo 的寬度,減去最後一個項目,就可以得知這一行圖片目前加總的寬度,也就可以用 ( .foo 寬度 / 這一行的寬度 ) 計算出這一行應該被放大的比例,再把這個比例一一存入實例。
var _lineWidth = 0, //存放這一行圖片寬度相加的值
_firstItem = 0; //第一個項目
for( var i = 0; i < $items.length; i++ ){
var $obj = $items[i],
$obj_w = $obj.width;
_lineWidth += $obj_w;
if( _lineWidth > $env_w ) { //檢測寬度是否在一行內,若大於一行寬度,則安排至下一行
var _prop = $env_w / ( _lineWidth - $obj_w ); //這一行應該被放大的比例
for( var j = _firstItem; j < i; j++ ) { //這一行的項目一一計算比例並存入實例
var $obj = $items[j];
$obj.reSize(_prop);
}
_firstItem = i;
_lineWidth = $obj_w;
}
}
最後,所有項目都得知自己的寬度,再一一調整 node 比例就好:
for( var i = 0; i < $items.length; i++ ){ //分配行
$items[i].setSize();
}
到此完成整個效果,當然你可以用各種方法優或性能,或讓它支援更多情境(例如 RWD)。
我在 github 有一個關於 等高響應佈局 的庫,你也可以協助我改進它,感謝大家囉!
沒有留言:
張貼留言