瀑布流
一、認識瀑布流
瀑布流,又稱瀑布流式布局。最早采用此布局的網站是Pinterest,視覺效果表現為參差不齊的多欄布局,即多行等寬不等高的元素依次排列。瀑布流布局的具體效果如下圖:
二、為什么要使用瀑布流
目前大多數網站采用的布局都是有規律的多列布局,即每列等寬,每行等高。而參差不齊的瀑布流式布局能給用戶不一樣的視覺沖擊效果。另外瀑布流結合下拉刷新,上拉加載進行數據的懶加載等操作,對于用戶的體驗感也是非常好的。
瀑布流主要針對的是帶圖片網站的內容展示,對于大小不一的圖片按照一定的規律排列。即對大小不一的圖片,讓其等比縮放的顯示在頁面中,可保證圖片不失真,可以有效的提高用戶體驗。
三、瀑布流的圖例分析
先用圖片分析一下我們想要的瀑布流是什么樣的
如下圖所示,假設一排五列。第一排排滿后,顯示的是這樣的。那么我們要放第6張圖片的時候,應該放在什么位置呢?
按照正常邏輯,第六張應該是放在第一張圖片下面,然后依次水平排列過去,如下圖:
但這樣下去可能會導致每列之間的高度差距越來越大,并不符合實際瀑布流效果要求,在瀑布流中,從第六張圖片開始,接下去的每一張圖片都應該放在高度最低的那一列圖片下方。如下圖:
即從第六張圖片開始都需要計算哪一列的圖片累計的高度最小,然后下一張圖片就應該放在哪一列。
四、具體實現
HTML代碼
加載中...
CSS代碼
* {
margin: 0;
padding: 0;
}
.content {
width: 1240px;
margin: 0 auto;
position: relative;
}
.shop {
width: 240px;
border: 1px solid #f00;
box-sizing: border-box;
position: absolute;
}
.shop img {
width: 100%;
}
.shopName{
height: 60px;
}
.loadBox {
display: flex;
justify-content: center;
align-items: center;
padding: 10px;
color: gray;
font-size: 14px;
}
.loadBox img{
animation: load 0.8s infinite;
}
@keyframes load {
from{
transform: rotate(0);
}to{
transform: rotate(360deg);
}
}
JS代碼
let data = [{
id: 1,
shopname: '圖片1',
imgUrl:
"https://img1.baidu.com/it/u=709381787,3386403196&fm=253&fmt=auto&app=138&f=JPEG?w=599&h=500"
}, {
id: 2,
shopname: '圖片2',
imgUrl: "https://img0.baidu.com/it/u=1489670366,411382419&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=753"
}, {
id: 3,
shopname: '圖片3',
imgUrl: "https://img1.baidu.com/it/u=3255035444,3515309779&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=666"
}, {
id: 4,
shopname: '圖片4',
imgUrl: "https://img2.baidu.com/it/u=3173263148,4118504965&fm=253&fmt=auto&app=138&f=JPEG?w=620&h=500"
}, {
id: 5,
shopname: '圖片5',
imgUrl: "https://img0.baidu.com/it/u=2628882850,1202220801&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=498"
}, {
id: 6,
shopname: '圖片6',
imgUrl: "https://img1.baidu.com/it/u=740708162,4263244726&fm=253&fmt=auto&app=120&f=JPEG?w=1280&h=800"
}, {
id: 7,
shopname: '圖片7',
imgUrl: "https://img1.baidu.com/it/u=701043886,582191677&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500"
}, {
id: 8,
shopname: '圖片8',
imgUrl: "https://img0.baidu.com/it/u=3878848433,891453978&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=667"
}, {
id: 9,
shopname: '圖片9',
imgUrl: "https://img1.baidu.com/it/u=1621477098,3345707124&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=771"
}, {
id: 10,
shopname: '圖片10',
imgUrl: "https://img1.baidu.com/it/u=2062807354,75688956&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=500"
}, {
id: 11,
shopname: '圖片11',
imgUrl: "https://img2.baidu.com/it/u=1190852256,1138303662&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=375"
}, {
id: 12,
shopname: '圖片12',
imgUrl: "https://img0.baidu.com/it/u=2280903142,1037129928&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500"
}, {
id: 13,
shopname: '圖片13',
imgUrl: "https://img0.baidu.com/it/u=1911023561,1873370868&fm=253&fmt=auto&app=138&f=JPEG?w=282&h=500"
}, {
id: 14,
shopname: '圖片14',
imgUrl: "https://img0.baidu.com/it/u=1303056880,3446954677&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=667"
}, {
id: 15,
shopname: '圖片15',
imgUrl: "https://img0.baidu.com/it/u=2463246409,2913214612&fm=253&fmt=auto&app=138&f=JPEG?w=750&h=500"
}, {
id: 16,
shopname: '圖片16',
imgUrl: "https://img1.baidu.com/it/u=673572676,3486461689&fm=253&fmt=auto&app=138&f=JPEG?w=667&h=500"
}, {
id: 17,
shopname: '圖片17',
imgUrl: "https://img0.baidu.com/it/u=1118388590,847334752&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500"
}, {
id: 18,
shopname: '圖片18',
imgUrl: "https://img2.baidu.com/it/u=1392060445,3405288279&fm=253&fmt=auto&app=138&f=JPEG?w=300&h=500"
}];
let content = document.querySelector('.content');
function getData() {
return new Promise((resolve, reject) => {
setInterval(() => {
resolve(data);
}, 600);//假設發起網絡請求數據需要600ms
});
}
let colHeight = [];//用于存儲每一列的高度
let lastShop = null;//用于存儲最后放入content的div
async function showData(isScroll=false) {
let r = await getData();
let shopBox = document.createDocumentFragment();
r.forEach((item, index) => {
// 創建圖片
let img = new Image();
img.src = item.imgUrl;
let imgInfo = item.imgUrl.split("?")[1].split("&");
/*
imgHeight imgWidth:240
imgInfo[1].split("=")[1] imgInfo[0].split("=")[1]
*/
let imgHeight = imgInfo[1].split("=")[1]*240/imgInfo[0].split("=")[1];
img.style.height = imgHeight + 'px';
// 創建圖片div
let shopdiv = document.createElement('div');
shopdiv.classList.add('shop');
// 創建放圖片名字的div
let shopNameDiv = document.createElement('div');
shopNameDiv.classList.add('shopName');
shopNameDiv.innerHTML = item.shopname;
let left;
let top = 0;
if(index<=4&&!isScroll){
colHeight.push(imgHeight+60+2+10);//第一行:圖片高度+文字div高度60+上下邊框2+底部間距10
// 計算放每個圖片的div的left值
left = (240 + 12) * (index % 5);
}else{
//需要每次計算找到最低的一列
let minColH = Math.min(...colHeight);//最低一列的高度
let minCol = colHeight.indexOf(minColH);//找出最低的是第幾列
// console.log(minCol,minColH);
// 計算放每個圖片的div的left值
left = (240 + 12) * minCol;
// 計算放每個圖片的div的top值
top = minColH;
// 改變每一列的高度
colHeight[minCol] += imgHeight+60+2+10;//imgHeight+60+2+10: 圖片高度+文字div高度60+上下邊框2+底部間距10
}
shopdiv.style.top = top + 'px';
shopdiv.style.left = left + 'px';
shopdiv.append(img);
shopdiv.append(shopNameDiv);
shopBox.append(shopdiv);
if(index == r.length-1){//判斷是否是最后一條數據
lastShop = shopdiv;
}
});
content.append(shopBox);
}
showData();
let flag = false;//用于判斷觸底加載數據是否加載完成
window.onscroll = async function () {//監聽滾動條滾動
if (lastShop.getBoundingClientRect().y < window.innerHeight+500 && flag == false) {//觸底加載新數據
flag = true;//正在加載
await showData(true);
flag = false;//加載完成
}
}
最終效果圖如下: