木桶布局 實(shí)現(xiàn)

百度圖片

圖片來(lái)自 百度圖片

像這樣高度一樣烁峭,而寬度不同的布局方式稱之為木桶布局。它有幾個(gè)鮮明的特點(diǎn): 每行的圖片高度一致秕铛;每行的圖片都是占滿的约郁。

如何實(shí)現(xiàn)木桶布局 之 整體思路

我們需要先擁有一些素材(圖片), 并且將這些圖片橫向擺放到頁(yè)面上但两。假如我現(xiàn)在有100張圖片鬓梅,并且編號(hào)1~100, 它們的寬高都不是固定的〗飨妫現(xiàn)在我們需要將圖片都擺放到目標(biāo)頁(yè)面上绽快。

首先,我們每行的高度是事先設(shè)定的紧阔,所以我們?cè)诎褕D片放入每一行(row)的時(shí)候坊罢,需要先調(diào)整圖片的大小(等比例放大或縮猩玫ⅰ)使得圖片的高度與行容器的高度一致 —— 即下圖中的藍(lán)色方框活孩。

其中: 橙色方框代表圖片(假設(shè)已和藍(lán)色方框等高), 黑色數(shù)字代表圖片編號(hào)乖仇。

依次往行容器中放置等比例大小調(diào)整過(guò)的圖片憾儒,①號(hào)圖片,②號(hào)圖片这敬,...航夺,一直到⑤號(hào)圖片,發(fā)現(xiàn)⑤號(hào)圖片它放不下了崔涂。這時(shí)候我們就需要把這個(gè)⑤號(hào)圖片放到下一行的開(kāi)頭阳掐,并且再次調(diào)整圖片大小,使得第一行中的①號(hào)-④號(hào)圖片能夠撐滿第一行冷蚂。該次調(diào)整主要是調(diào)整圖片的高度缭保,使得其寬度能夠自適應(yīng)撐滿該行。

如果測(cè)量一下百度圖片每行的高度就會(huì)發(fā)現(xiàn): 每行高度基本相等蝙茶,但有幾像素的差距艺骂。


木桶布局原理

然后重復(fù)上述步驟,直至圖片擺放完畢隆夯。

木桶布局代碼實(shí)現(xiàn) 之 具體步驟

從HTML钳恕、CSS别伏、JS部分依次實(shí)現(xiàn)。

大概有個(gè)結(jié)構(gòu)忧额,就是先有一個(gè)固定寬度的父容器厘肮,與若干個(gè)(數(shù)量未定的)行容器來(lái)盛放每行的圖片數(shù)量,然后每行有若干個(gè)圖片容器來(lái)盛放圖片睦番。

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

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

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

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

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

JS代碼

這里采用構(gòu)造函數(shù)創(chuàng)建對(duì)象的方式來(lái)寫這段代碼类茂,注意按照約定構(gòu)造函數(shù)的首字母要大寫。創(chuàng)建一個(gè)新對(duì)象托嚣,然后將構(gòu)造函數(shù)的作用域賦給新對(duì)象巩检,調(diào)用構(gòu)造函數(shù)中的方法。

函數(shù)名聲明為 Barrel 示启,意為木桶兢哭。然后就要確定有哪些屬性和方法。在理解了思路步驟的前提下丑搔,可以構(gòu)思需要哪些屬性厦瓢、方法以及它們的作用。

屬性:

  • 每行圖片的高度固定: rowHeight, 行高
  • 擁有一個(gè)固定的容器: DOM對(duì)象,一個(gè)容器 命名為 .ct烘绽。 還應(yīng)該有行容器和圖片容器蛀蜜,但是由于這兩個(gè)容器內(nèi)容數(shù)量不固定,所以在布局的時(shí)候再創(chuàng)建
  • 行容器的寬度: width舞吭, 獲取ct的寬度
  • 存放每行圖片的數(shù)組: imgArr[]。每次把加載的圖片壓入該數(shù)組,判斷該行是否超出寬度郑诺。

方法:

  • 擁有素材圖片 : 通過(guò)getImgUrls()方法來(lái)獲取圖片鏈接,(或從數(shù)據(jù)庫(kù)中獲取圖片)杉武。這里是通過(guò)訪問(wèn)https://placeholder.com/ 網(wǎng)站來(lái)獲取代碼辙诞,具體后述
  • 加載圖片信息: loadImg()方法來(lái)加載圖片,以便獲取圖片信息轻抱,
  • 渲染圖片隊(duì)列: render() 改變圖片的比例大小飞涂,計(jì)算一行可以放置多少個(gè)圖片
  • 放置圖片位置: layout() 將改變完大小的圖片放置到頁(yè)面上,append到對(duì)應(yīng)的DOM元素節(jié)點(diǎn)上祈搜。具體關(guān)系對(duì)應(yīng)前面的父子關(guān)系即可

初步的代碼結(jié)構(gòu)就如下所示:

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

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

方法實(shí)現(xiàn):

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

隨機(jī)生成圖片的大泄倏ā(寬高限定一個(gè)范圍)蝗茁,背景顏色與文本顏色。這里添加一個(gè)參數(shù)寻咒,imgNum:確定需要圖片的數(shù)量评甜。最后返回包含這些圖片鏈接的一個(gè)數(shù)組

getImgUrls: function(imgNum){
    let imgUrls = []; 
    let colorArr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "A", "B", "C", "D", "E", "F"]; // 顏色數(shù)組[0-9, A-F],
// 該顏色數(shù)組生成與源代碼稍有不同仔涩,在我的GitHub上是用for循環(huán)生成的 
                
    for(let i = 0; i < imgNum; i++) {
        let imgWidth = Math.floor(Math.random()*50+50); // 設(shè)定寬度50-100
        let imgHeight = Math.floor(Math.random()*30+50); // 設(shè)定高度為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() 方法:用來(lái)加載圖片,圖片來(lái)源就從已有素材中尋找熔脂。即getImgUrls()
在這個(gè)方法中佩研,需要做的就是獲取圖片信息(寬高,src等)并改變大小霞揉,高度與行高一致旬薯,寬度等比例改變。并且將信息存儲(chǔ)起來(lái)适秩,等每張圖片加載完畢后就加入渲染排列圖片的隊(duì)列中绊序。

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

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

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

render()方法:渲染隊(duì)列的方法秽荞,主要是判斷圖片能否放在一行上(每次把圖片加入到imgArr隊(duì)列中就可判斷長(zhǎng)度)骤公,并當(dāng)圖片符合占滿一行的條件時(shí),將最后一張圖片放到下一行扬跋,并記錄需要改變的圖片比例交由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;
        // 利用面積相等原則钦听,來(lái)計(jì)算新的高度
        let newHeight = this.width * this.rowHeight / wholeWidth;  
        this.layout(newHeight);
        // 放置完畢之前的圖片之后洒试,清空該圖片隊(duì)列
        // 并將上一行溢出的圖片 作為下一行的第一張
        this.imgArr = [];
        this.imgArr.push(lastImg);
    }
 }

layout()方法:獲得newHeight參數(shù),是圖片的新高度朴上,改變?cè)撔袌D片的高度垒棋,使得這些圖片自適應(yīng)改變寬度之后能占滿該行。也就是說(shuō)這個(gè)木桶布局的高度會(huì)發(fā)生變化 與之前設(shè)定的this.rowHeight相近但不相等痪宰。

之后創(chuàng)建節(jié)點(diǎn)叼架,并把修改好的圖片依次加入到創(chuàng)建好的節(jié)點(diǎn)上,然后添加到頁(yè)面中酵镜。

layout: function(newHeight){
    // 一次只放一行碉碉, 所以只生成一個(gè)imgRow
    let imgRow = document.createElement("div");
    imgRow.classList.add("img-row");
    // 一行包含若干個(gè)圖片,所以需要若干個(gè)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;
        // 改變了高度之后寬度自己會(huì)跟著改變
        img.style.height = newHeight + "px";  // 注意加"px"
        imgBox.appendChild(img); 
        imgRow.appendChild(imgBox); 
    }
    // 先把圖片加載到圖片盒子里淮韭,然后加到圖片列中垢粮,最后加到容器中
    this.ct.appendChild(imgRow);
} 

最后便是兩行代碼來(lái)運(yùn)行這段程序

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

效果預(yù)覽及代碼地址

效果預(yù)覽
代碼地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末靠粪,一起剝皮案震驚了整個(gè)濱河市蜡吧,隨后出現(xiàn)的幾起案子毫蚓,更是在濱河造成了極大的恐慌,老刑警劉巖昔善,帶你破解...
    沈念sama閱讀 219,589評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件元潘,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡君仆,警方通過(guò)查閱死者的電腦和手機(jī)翩概,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)返咱,“玉大人钥庇,你說(shuō)我怎么就攤上這事】。” “怎么了评姨?”我有些...
    開(kāi)封第一講書人閱讀 165,933評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)萤晴。 經(jīng)常有香客問(wèn)我吐句,道長(zhǎng),這世上最難降的妖魔是什么店读? 我笑而不...
    開(kāi)封第一講書人閱讀 58,976評(píng)論 1 295
  • 正文 為了忘掉前任嗦枢,我火速辦了婚禮,結(jié)果婚禮上两入,老公的妹妹穿的比我還像新娘净宵。我一直安慰自己,他們只是感情好裹纳,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,999評(píng)論 6 393
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著紧武,像睡著了一般剃氧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上阻星,一...
    開(kāi)封第一講書人閱讀 51,775評(píng)論 1 307
  • 那天朋鞍,我揣著相機(jī)與錄音,去河邊找鬼妥箕。 笑死滥酥,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的畦幢。 我是一名探鬼主播坎吻,決...
    沈念sama閱讀 40,474評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼宇葱!你這毒婦竟也來(lái)了瘦真?” 一聲冷哼從身側(cè)響起刊头,我...
    開(kāi)封第一講書人閱讀 39,359評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎诸尽,沒(méi)想到半個(gè)月后原杂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,854評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡您机,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,007評(píng)論 3 338
  • 正文 我和宋清朗相戀三年穿肄,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片际看。...
    茶點(diǎn)故事閱讀 40,146評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡咸产,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出仿村,到底是詐尸還是另有隱情锐朴,我是刑警寧澤,帶...
    沈念sama閱讀 35,826評(píng)論 5 346
  • 正文 年R本政府宣布蔼囊,位于F島的核電站焚志,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏畏鼓。R本人自食惡果不足惜酱酬,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,484評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望云矫。 院中可真熱鬧膳沽,春花似錦、人聲如沸让禀。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,029評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)巡揍。三九已至痛阻,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間腮敌,已是汗流浹背阱当。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,153評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留糜工,地道東北人弊添。 一個(gè)月前我還...
    沈念sama閱讀 48,420評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像捌木,于是被迫代替她去往敵國(guó)和親油坝。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,107評(píng)論 2 356

推薦閱讀更多精彩內(nèi)容