js+CSS简单实现瀑布流布局
作者:sherlockkid7
前言
瀑布流布局,是一种视觉表现为参差不齐的多栏布局。常用于内容以图片为主的页面展示,图片等宽不等高,每行从左到右排满后,后面的元素依次添加至短的一列其后。随着页面向下滚动,还会不断加载数据并添加到当前尾部,视觉上显得错落有致不拘一格。今天使用css和js两种方式来实现瀑布流布局
css实现瀑布流布局
该方式实现的瀑布流布局其中的内容数据是固定的,主要利用了 CSS 的多列属性 columns
页面结构:创建容器container,该容器包裹所有用于展示的元素item(图片和标题)
<div class="container"> <div class="item"> <img src="..."/> <h2>...</h2> </div> ... </div>
给容器container设置宽度,并居中显示;控制其中的内容分成3列columns: 3;
,列与列之间的距离为20pxcolumn-gap: 20px;
每个item元素,设置属性break-inside: avoid;
, 避免在多列布局中元素内容被分割(元素的内容被自动断开,图片在一列尾部,文字在下一列的头部)
.container { width: 1200px; margin: 20px auto; columns: 3; column-gap: 20px; } .container .item { width: 100%; break-inside: avoid; margin-bottom: 20px; } .container .item img{ width: 100%; }
小结:根据上图可以看出CSS的多列布局实现的瀑布流是从上到下,再从左到右的顺序排列元素,适用于对于元素顺序没有要求,且元素数据固定的情况
js实现瀑布流布局
思路:
每个元素使用绝对定位的方式,动态的设置相应的top,left值进行排列,先计算一行能够容纳几列元素,第一行元素直接设置top为0,left为索引乘以(元素宽度 + 间隙)进行排列,后面的元素排列,需要找出所有列中高度最小的那一列,将元素添加至高度最小的那一列后面,直至所有元素添加完毕。
创建一个容器container,用于存放展示的元素。并设置css为相对定位,其中展示的元素为绝对定位
<div id="container"></div>
计算页面一行能容纳几列元素
列数 = 页面的宽度 / (元素的宽度+元素之间的间隙 ) column = pageWidth / (itemWidth + gap);
注:页面的宽度这里设置的是body的宽度(使用css控制了最大宽度和最小宽度)
创建createElement函数,用来动态创建组成瀑布流的元素块(包含图片和标题),并在请求数据成功后调用添加到页面中,并进行瀑布流布局
function createElement(itemData) { return new Promise((resolve, reject) => { let div = document.createElement('div') div.innerHTML = `...` div.className = 'item' // 监听图片的load事件 div.querySelector('img').addEventListener('load', () => { resolve(div) }) }) } // 获取喵咪的数据 let page = 1 //请求数据的页码 let isReq = false //设置节流阀,处于请求过程中,则不再请求数据 function loadNewData() { if (isReq) return isReq = true getData() .then(data => { // 处理新的数据 let items = data let promises = items.map(item => createElement(item)) Promise.all(promises).then(nodes => { let frag = document.createDocumentFragment() isReq = false page++ nodes.forEach(node => { frag.appendChild(node) Node.push(node) }) document.getElementById('container').appendChild(frag) waterfall(Node) }) }) }
注:遇到获取元素块高度不正确的问题
因为图片可能还没有完全加载,获取的元素块高度就只是h4标签的高度。因此需要在图片加载完成后再获取元素的高度。
a.通过监听图片的 load
事件,当事件触发时,表示图片已经加载完成,返回创建的div
元素
b.在获取数据loadNewData()
函数中,对返回的数据执行创建元素方法,并使用Promise.all()
对由返回的div
元素(Promise
实例)组成的Promise
数组进行处理,当每个Promise
实例的状态变为 fulfilled
(所有图片都加载完成),就执行瀑布流布局
创建waterfall函数,实现瀑布流
接收元素数组,然后对每个元素进行布局。如果元素在第一行,那么它的顶部位置就是 0,左侧位置就是它的索引乘以(元素宽度 + 间隙)。如果元素不在第一行,那么我们首先找出高度最小的列,然后将元素放在这个列的下方,并更新这个列的高度。
function waterfall(items) { for (var i = loaded; i < items.length; i++) { let item = items[i] let height = item.offsetHeight if (i < columns) { //满足这个条件则说明在第一行 item.style.top = 0 item.style.left = (itemWidth + gap) * i + 'px' heightArr.push(height) } else { //其他行,先找出最小高度列的索引 const minIndex = getMinIndex(heightArr) //top值就是最小列的高度+gap const top = heightArr[minIndex] + gap item.style.top = top + 'px' item.style.left = minIndex * (itemWidth + gap) + 'px' //修改原本最小列的高度 heightArr[minIndex] = top + height } loaded = items.length } } //用于获取heightArr数组中值最小的列的索引。 const getMinIndex = (array) => { const min = Math.min.apply(null, array) return array.indexOf(min) }
监听页面滚动,当未显示的元素内容小于屏幕高度的一大半(这里设置的0.7,因为数据加载比较慢,一般设置成0.5就可以),就需要加载新数据进行展示
window.addEventListener('scroll', lazy) function lazy() { const scrollTop = document.documentElement.scrollTop || document.body.scrollTop const documentHeight = document.documentElement.scrollHeight // 未显示的内容 = 文档(内容)高度-滚动高度-屏幕高度 if (documentHeight - scrollTop - clientHeight < 0.7 * clientHeight) { loadNewData() } }
小结:
a. 简单封装的请求数据方法getData()没有展示出来,请求的接口是从网上找的(地址),一次只能加载10张,并且加载很慢,可以自行查找接口。
b. 需要定义的变量
根据以上基本可以实现瀑布流了,排列顺序和滚动页面加载数据也比较符合一般情况
以上就是js+CSS简单实现瀑布流布局的详细内容,更多关于js瀑布流布局的资料请关注脚本之家其它相关文章!