每日一题之实现瀑布流
-
思路:
- 可以根据父元素总宽度和子元素单个宽度拿到一行可以放下n个元素
- 可以根据一行n个元素生成n长度的数组用于存放每列的高度
- 遍历父元素内部的图片根据步骤二中最短的索引设置单个图片的left, top,最后重新设置最短索引所在列的高度
代码如下
class Waterfall {
constructor(containerSelector, itemSelector, columnWidth, gap) {
this.container = document.querySelector(containerSelector);
// 拿到需要排列瀑布流的所有元素并转成数组
this.items = Array.from(this.container.querySelectorAll(itemSelector));
// 拿到每列需要设置的宽度
this.columnWidth = columnWidth;
// 拿到图片之间的间隔
this.gap = gap;
// 根据父元素容器宽度和每张图片的宽度, 向下取整得到一行可以有多少列, 如果小于1, 默认等于1
this.columns =
Math.floor(this.container.offsetWidth / (this.columnWidth + this.gap)) ||
1;
// 根据上面一步多少列生成对应长度的数组应用存储每一列的高度
this.columnHeights = new Array(this.columns).fill(0);
this.init();
}
init() {
this.layoutItems();
window.addEventListener("resize", this.handleResize.bind(this));
}
// 主要的代码逻辑
layoutItems() {
// 遍历子元素
this.items.forEach((item, index) => {
// 拿到所有列的最小索引
const columnIndex = this.getShortestColumnIndex();
// 设置最小索引下图片的左上偏移量
const left = columnIndex * (this.columnWidth + this.gap);
const top = this.columnHeights[columnIndex] + this.gap;
item.style.position = "absolute";
item.style.left = `${left}px`;
item.style.top = `${top}px`;
// 设置最小索引下的列高度
this.columnHeights[columnIndex] = top + item.offsetHeight;
});
// 根绝每一列的高度取出最大值并赋值给父元素
const maxHeight = Math.max(...this.columnHeights);
this.container.style.height = `${maxHeight}px`;
}
getShortestColumnIndex() {
return this.columnHeights.indexOf(Math.min(...this.columnHeights));
}
handleResize() {
// 窗口大小改变触发, 重新计算父元素可以放下多少列
this.columns = Math.floor(
this.container.offsetWidth / (this.columnWidth + this.gap)
);
this.columns = this.columns || 1;
// 重新设置每列的高度, 这里我感觉是否可以只改变最后一列?
this.columnHeights = new Array(this.columns).fill(0);
this.layoutItems();
}
}
// 示例使用
const waterfall = new Waterfall(".container", ".item", 200, 10);