瀑布流
CSS 实现
使用 column 属性
column 可以指定容器下元素列的宽度和数量
css
columns: column-width column-count;HTML 结构:
html
<div class="masonry">
<div class="item">
<img src="https://picsum.photos/360/480?random=1" alt="">
</div>
// ...
<div class="item">
<img src="https://picsum.photos/360/420?random=2" alt="">
</div>
</div>使用 2 个 css 属性,column-count (指定列数)和column-gap (列之间的间距)
css
body {
margin: 4px;
font-family: Helvetica;
}
.masonry {
column-count: 4;
column-gap: 0;
}
.masonry .item {
position: relative;
break-inside: avoid;
}
.masonry .item::after {
counter-increment: count;
content: counter(count);
width: 2em;
height: 2em;
background-color: rgba(0,0,0,0.9);
color: #ffffff;
line-height: 2em;
text-align: center;
position: absolute;
font-size: 1em;
z-index: 2;
left: 0;
top: 0;
}图片的排列是从上往下排列的而不是从左往右。
Sum
- 排列规律都是先上下再左右,无法控制,动态加载会出现问题。
- 兼容性不太好。
使用 FlexBox 实现
css
// 内容由上至下排列,容器的高度少于内容的高度时就换行,也就是向右排列
.masonry {
display: flex;
flex-direction: column;
flex-wrap: wrap;
height: 1000px;
}
.item {
position: relative;
width: 25%;
padding: 2px;
box-sizing: border-box;
}
.item img {
display: block;
width: 100%;
height: auto;
}flexbox 也是从上至下排列的,但是 flexbox 里的元素可以套用一个叫 order 的设定值,让它可以不跟随 HTML 结构顺序来排列
css
.item:nth-child(4n+1){
order: 1;
}
.item:nth-child(4n+2){
order: 2;
}
.item:nth-child(4n+3){
order: 3;
}
// ...Sum
- 高度是固定的,很难做活。
- 顺序虽然可以改变,但是仍然不灵活,不尽人意。
JS 实现
使用 absolute 实现:
初始化,计算出列宽来,将H作为列高存储器,4列那么就是[0,0,0,0]。然后收集子元素后,清除父容器内容。
遍历其子元素,设置其都为绝对定位,设置其列宽。后监听其下的图片加载是否完毕。
如果加载成功,那么计算应该在的位置,瀑布流的常规原则是哪一列数值最小就在那一列上设置新图片。当然他的相对高度和间距也要计算出来,同时在H当前列上要把高度存起来。
每次图片加载完就更新虚拟节点到父容器中。
javascript
import Waterfall from "./Waterfall.js"
window.onload = new Waterfall({
$el: document.querySelector(".masonry"),
count: 4,
gap: 10
})Waterfall.js 类
javascript
export default class Waterfall {
constructor(options) {
this.$el = null; // 父容器
this.count = 4; // 列数
this.gap = 10; // 间距
Object.assign(this, options);
this.width = 0; // 列的宽度
this.items = []; // 子元素集合
this.H = []; // 存储每列的高度方便计算
this.flag = null; // 虚拟节点集合
this.init();
}
init() {
this.items = Array.from(this.$el.children);
this.reset();
this.render();
}
reset() {
this.flag = document.createDocumentFragment();
this.width = this.$el.clientWidth / this.count;
this.H = new Array(this.count).fill(0);
this.$el.innerHTML = "";
}
render() {
const { width, items,flag,H,gap } = this;
items.forEach(item => {
item.style.width = width + "px";
item.style.position = "absolute";
let img = item.querySelector("img");
if(img.complete){
let tag = H.indexOf(Math.min(...H));
item.style.left = tag * (width + gap) + "px";
item.style.top = H[tag] + "px";
H[tag] += img.height*width/ img.width + gap;
flag.appendChild(item);
}
else{
img.addEventListener("load", () => {
let tag = H.indexOf(Math.min(...H));
item.style.left = tag * (width + gap) + "px";
item.style.top = H[tag] + "px";
H[tag] += img.height*width/ img.width + gap;
flag.appendChild(item);
this.$el.append(flag);
})
}
})
this.$el.append(flag);
}
}大部分商用瀑布流方案如下 isotope、Masonry.js .
