共计 3240 个字符,预计需要花费 9 分钟才能阅读完成。
仿照淘宝实现瀑布流
- 实现的效果图如下(以 iphone6/7/ 8 以及 ipadpro 为例):
实现思路:
- 利用相对定位进行布局
- 定义 gap(上下左右的间距)
- 先计算出列数 (columnNum) 即网页的可视区域的宽度(clientWidth)/ 列宽(itemWidth), 所以公式如下:
columnNum=clientWidth/itemWidth
- 将第一行每列的高度装进一个数组(
columnHeightArr
),用于高度定位 - 通过循环,
index<columnNum
时,阐明是第一行,同时将元素的高度装入columnHeightArr
数组其余状况为非第一行 - 找出
columnHeightArr
外面的最小值(minHeight
),以及最小值对应的index
, 该列的上面就是下一个元素的地位,而后计算出间隔右边,间隔左边的间隔,而后重置最小值的高度 - 间隔右边为:
index*(item.offsetWidth+gap)
- 间隔上边边为:
(元素对应的 index)*(minHeight+gap)
-
重置
columnHeightArr
数组对应的最小值为columnHeightArr[index]=item.offsetHeigth+gap+columnHeightArr[index]
js 示例代码如下:
/* 瀑布流 parentEl: 父级元素 gap:上下左右的间隔 columFixNum: 每行有多少列 */ class WaterFallFlow {constructor(parentEl, gap = 10, columFixNum = 2) { this.columFixNum = columFixNum this.parentEl = parentEl;// 瀑布流布局对应的父节点 this.childWidth = 0;// 瀑布流布局对应的每个子节点的宽度 this.gap = gap;// 上下左右的间距 /* 网页的可是区域 */ this.clientWidth = 0 this.coulmnNum = 0;// 列数(可视区域的宽度 / 要瀑布流布局元素的宽度)this.columnHeightArr = [];// 用于装列的高度} startWaterFall() {this.columnHeightArr = []// 初始化以及窗口扭转的时候重置第一列的高度 this.clientWidth = document.documentElement.clientWidth || document.body.clientWidth || window.clientWidth this.childWidth = (this.clientWidth / this.columFixNum) - (this.gap / this.columFixNum) const parentEl = this.parentEl;// 瀑布流布局对应的父节点 const childWidth = this.childWidth;// 瀑布流布局对应的每个子节点的宽度 document.querySelector(parentEl).style.position = 'relative'// 将父元素设置为绝对定位 const children = [...document.querySelector(parentEl).children]; if (children.length == 0) throw new Error(`${parentEl}节点下未找到子节点 `) children.forEach((item) => { item.style.position = 'absolute'// 每一个元素设置为相对定位 item.style.width = `${childWidth}px` }) const itemWidth = children[0].offsetWidth; this.coulmnNum = parseInt(this.clientWidth / itemWidth)// 向下取整 /* 第一行的布局 */ children.forEach((item, index) => { /* 第一行 */ if (index < this.coulmnNum) { item.style.top = 0; item.style.left = `${index * (item.offsetWidth + this.gap)}px`; this.columnHeightArr.push(item.offsetHeight + item.offsetTop) } else { // 不是第一行 const minHeight = Math.min(...this.columnHeightArr)// 找到列中高度最小的哪一个元素,顺次排在最小元素的前面 const minIndex = this.columnHeightArr.indexOf(minHeight)// 找到最小高度的下标是哪一个 item.style.top = `${minHeight + this.gap}px` item.style.left = `${minIndex * (item.offsetWidth + this.gap)}px` this.columnHeightArr[minIndex] = minHeight + item.offsetHeight + this.gap// 重置最小高度 } }); } init() {this.startWaterFall(); window.addEventListener('resize', () => {setTimeout(() => {this.startWaterFall() }, 300) }) } } window.onload = function () {const waterfallflow = new WaterFallFlow('ul'); waterfallflow.init()}
htnl 代码如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0"> <title>Document</title> <style> * { padding: 0; margin: 0; list-style: none; } img {width: 100%;} body {background-color: #ccc;} </style> </head> <body> <div class="box"> <ul> <li> <img src="./img/1.png" alt=""> </li> <li> <img src="./img/2.png" alt=""> </li> <li> <img src="./img/3.png" alt=""> </li> <li> <img src="./img/4.png" alt=""> </li> <li> <img src="./img/5.png" alt=""> </li> <li> <img src="./img/6.png" alt=""> </li> <li> <img src="./img/7.png" alt=""> </li> <li> <img src="./img/8.png" alt=""> </li> <li> <img src="./img/9.png" alt=""> </li> <li> <img src="./img/10.png" alt=""> </li> <li> <img src="./img/11.png" alt=""> </li> <li> <img src="./img/12.png" alt=""> </li> </ul> </div> </body> <!-- 瀑布流布局 --> <script src="./js/waterfallFlow.js"></script> </html>
正文完