圖片來(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