木桶布局 實現

百度圖片

圖片來自 百度圖片

像這樣高度一樣,而寬度不同的布局方式稱之為木桶布局。它有幾個鮮明的特點: 每行的圖片高度一致;每行的圖片都是占滿的。

如何實現木桶布局 之 整體思路

我們需要先擁有一些素材(圖片), 并且將這些圖片橫向擺放到頁面上。假如我現在有100張圖片,并且編號1~100, 它們的寬高都不是固定的。現在我們需要將圖片都擺放到目標頁面上。

首先,我們每行的高度是事先設定的,所以我們在把圖片放入每一行(row)的時候,需要先調整圖片的大小(等比例放大或縮小)使得圖片的高度與行容器的高度一致 —— 即下圖中的藍色方框。

其中: 橙色方框代表圖片(假設已和藍色方框等高), 黑色數字代表圖片編號。

依次往行容器中放置等比例大小調整過的圖片,①號圖片,②號圖片,...,一直到⑤號圖片,發現⑤號圖片它放不下了。這時候我們就需要把這個⑤號圖片放到下一行的開頭,并且再次調整圖片大小,使得第一行中的①號-④號圖片能夠撐滿第一行。該次調整主要是調整圖片的高度,使得其寬度能夠自適應撐滿該行。

如果測量一下百度圖片每行的高度就會發現: 每行高度基本相等,但有幾像素的差距。


木桶布局原理

然后重復上述步驟,直至圖片擺放完畢。

木桶布局代碼實現 之 具體步驟

從HTML、CSS、JS部分依次實現。

大概有個結構,就是先有一個固定寬度的父容器,與若干個(數量未定的)行容器來盛放每行的圖片數量,然后每行有若干個圖片容器來盛放圖片。

父子關系(父 > 子): 固定寬度的父容器 (" .ct ") > 行容器(" .img-row ") > 圖片容器(" .img-box ")
對應上圖(木桶布局原理)為: 紅色方框 > 藍色方框 > 橙色方框

<!-- HTML -->
<div class="ct"></div>

<style type="text/css">
/* CSS */
  /* 頁面布局容器*/
  .ct {
    width: 1000px;
    margin: 0 auto;
   }

  /* 圖片容器 */
  .img-box {
    float: left;
  }

  /* 行容器 清楚子元素(圖片容器)的浮動*/
  .img-row::after {
    content: "";
    display: block;
    clear: both;
  }
</style>

JS代碼

這里采用構造函數創建對象的方式來寫這段代碼,注意按照約定構造函數的首字母要大寫。創建一個新對象,然后將構造函數的作用域賦給新對象,調用構造函數中的方法。

函數名聲明為 Barrel ,意為木桶。然后就要確定有哪些屬性和方法。在理解了思路步驟的前提下,可以構思需要哪些屬性、方法以及它們的作用。

屬性:

  • 每行圖片的高度固定: rowHeight, 行高
  • 擁有一個固定的容器: DOM對象,一個容器 命名為 .ct。 還應該有行容器和圖片容器,但是由于這兩個容器內容數量不固定,所以在布局的時候再創建
  • 行容器的寬度: width, 獲取ct的寬度
  • 存放每行圖片的數組: imgArr[]。每次把加載的圖片壓入該數組,判斷該行是否超出寬度。

方法:

  • 擁有素材圖片 : 通過getImgUrls()方法來獲取圖片鏈接,(或從數據庫中獲取圖片)。這里是通過訪問https://placeholder.com/ 網站來獲取代碼,具體后述
  • 加載圖片信息: loadImg()方法來加載圖片,以便獲取圖片信息,
  • 渲染圖片隊列: render() 改變圖片的比例大小,計算一行可以放置多少個圖片
  • 放置圖片位置: layout() 將改變完大小的圖片放置到頁面上,append到對應的DOM元素節點上。具體關系對應前面的父子關系即可

初步的代碼結構就如下所示:

function Barrel(ct, imgNum, height) {
    this.ct = ct;  // 木桶布局容器的DOM節點
    this.width = parseInt(window.getComputedStyle(ct, null).getPropertyValue("width")); // 行寬,由于獲取到的值是string: 1000px 所以轉化為數值 1000
    this.rowHeight = height;  // 行高
    this.imgArr = [];   // 存放每行圖片的數組

    this.loadImg(imgNum);
}
Barrel.prototype = {
    getImgUrls: function(){},
    loadImg: function(){},
    render: function(){},
    layout: function(){}
}

方法實現:

getImgUrls()方法:首先可以訪問該網站 ,可以獲取到占位圖片,可以看到獲取占位圖片的格式 : http://via.placeholder.com/width x height /ffffff/00000/ , width與height分別代表圖片的寬高,fffff是圖片的背景顏色,000000是圖片的文本顏色。

隨機生成圖片的大小(寬高限定一個范圍),背景顏色與文本顏色。這里添加一個參數,imgNum:確定需要圖片的數量。最后返回包含這些圖片鏈接的一個數組

getImgUrls: function(imgNum){
    let imgUrls = []; 
    let colorArr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "A", "B", "C", "D", "E", "F"]; // 顏色數組[0-9, A-F],
// 該顏色數組生成與源代碼稍有不同,在我的GitHub上是用for循環生成的 
                
    for(let i = 0; i < imgNum; i++) {
        let imgWidth = Math.floor(Math.random()*50+50); // 設定寬度50-100
        let imgHeight = Math.floor(Math.random()*30+50); // 設定高度為30-80
        let bgColor = textColor = "";  // 下面使用的是字符串拼接,每次使用都需要重新清空

        for(let j=0; j < 6; j++){
            bgColor += colorArr[Math.floor(Math.random()*16)];
            textColor += colorArr[Math.floor(Math.random()*16)];
        }

        let url = "http://via.placeholder.com/" + imgWidth + "x" + imgHeight + "/" + bgColor + "/" + textColor;
        imgUrls.push(url); 
    } 

    return imgUrls;
}

loadImg() 方法:用來加載圖片,圖片來源就從已有素材中尋找。即getImgUrls()
在這個方法中,需要做的就是獲取圖片信息(寬高,src等)并改變大小,高度與行高一致,寬度等比例改變。并且將信息存儲起來,等每張圖片加載完畢后就加入渲染排列圖片的隊列中。

loadImg: function(imgNum){
    let imgUrlsArr = this.getImgUrls(imgNum);
    let _this = this;  // 保存this指針的指向,方便調用屬性及方法

    for(let i = 0; i < imgNum; i++){
        let newImg = new Image(); // 新建圖片對象
        newImg.src = imgUrlsArr[i]; // 加載圖片內容

        newImg.onload = function(){
            // Image對象加載了src后擁有寬高屬性, imgInfo存儲圖片信息
            let ratio = this.width / this.height;
            let imgInfo = {
               target: this, // 用來存放當前目標newImg,方便后續調用
               height: _this.rowHeight,
               width: ratio * _this.rowHeight, // 等比例縮放
               ratio: ratio,
             }; 
            // 把加載完的圖片加入渲染隊列
             _this.render(imgInfo);
        }
    }
},

render()方法:渲染隊列的方法,主要是判斷圖片能否放在一行上(每次把圖片加入到imgArr隊列中就可判斷長度),并當圖片符合占滿一行的條件時,將最后一張圖片放到下一行,并記錄需要改變的圖片比例交由layout()方法更改。

render: function(imgInfo){
    // 定義該行圖片寬度之和
    let wholeWidth = 0;
    this.imgArr.push(imgInfo);

    for(let i = 0; i < this.imgArr.length; i++){
        wholeWidth += this.imgArr[i].width;
    }

    // 如果該行加入的圖片寬度大于了該行的寬度 
    // 就需要彈出最后一張圖片,并更改前面的圖片大小比例
    if(wholeWidth > this.width){
        let lastImg = this.imgArr.pop();
        wholeWidth -= lastImg.width;
        // 利用面積相等原則,來計算新的高度
        let newHeight = this.width * this.rowHeight / wholeWidth;  
        this.layout(newHeight);
        // 放置完畢之前的圖片之后,清空該圖片隊列
        // 并將上一行溢出的圖片 作為下一行的第一張
        this.imgArr = [];
        this.imgArr.push(lastImg);
    }
 }

layout()方法:獲得newHeight參數,是圖片的新高度,改變該行圖片的高度,使得這些圖片自適應改變寬度之后能占滿該行。也就是說這個木桶布局的高度會發生變化 與之前設定的this.rowHeight相近但不相等。

之后創建節點,并把修改好的圖片依次加入到創建好的節點上,然后添加到頁面中。

layout: function(newHeight){
    // 一次只放一行, 所以只生成一個imgRow
    let imgRow = document.createElement("div");
    imgRow.classList.add("img-row");
    // 一行包含若干個圖片,所以需要若干個imgBox,并將圖片加入其中
    for(let i = 0; i < this.imgArr.length; i++){
        let imgBox = document.createElement("div");
        imgBox.classList.add("img-box");

        let img = this.imgArr[i].target;
        // 改變了高度之后寬度自己會跟著改變
        img.style.height = newHeight + "px";  // 注意加"px"
        imgBox.appendChild(img); 
        imgRow.appendChild(imgBox); 
    }
    // 先把圖片加載到圖片盒子里,然后加到圖片列中,最后加到容器中
    this.ct.appendChild(imgRow);
} 

最后便是兩行代碼來運行這段程序

let ct = document.querySelector(".ct");
let barrel = new Barrel(ct, 100, 100); // 100張圖片數量, 指定每行的初始行高為100 

效果預覽及代碼地址

效果預覽
代碼地址

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容