瀑布流布局 不到30行代码实现(JavaScript + absolute)支持懒加载
@
前言
瀑布流 网站页面布局
瀑布流,又称瀑布流式布局。是比较流行的一种网站页面布局,视觉表现为参差不齐的多栏布局,随着页面滚动条向下滚动,这种布局还会不断加载数据块并附加至当前尾部。最早采用此布局的网站是Pinterest,逐渐在国内流行开来。国内大多数清新站基本为这类风格。
简单来说: 瀑布流式布局其实式一种网页的样式效果,简单来说就是一种单个宽度固定,单个图片高度根据内容适应,多个块自动换行,但是紧凑拼在一起的效果。既可以使用纯css快速的达到这种样式效果,也可以通过JavaScript辅助,实现更丰富合理的效果
一、使用css实现瀑布流布局
1.flex
布局
使用flex布局,将容器设置为flex布局,设置主轴方向为列方向,允许换行,容器设定固定高度(这样才会自动换列),然后子元素设置宽度为对应比例,在某一列填满时会自动往下一列填充,并且每一列元素填充十分紧凑****
优化方向:使用order属性优化排序方向
2.column-count
多栏布局
类似的还有使用 多栏布局,使用column-count
属性,设置多栏布局,效果跟flex布局类似,排列方向也是从上到下,从左到右
3.grid
网格布局
二、结合JavaScript的瀑布流布局实现
1.推荐原因
瀑布流式布局经常被使用在图片的展示页面,大量的图片资源加载,如果只用css样式的话必须一次性加载所有图片,我们期待的是瀑布流和懒加载结合在一起,在滚动视图的同时,不断加载下面的内容
2.实现步骤
如果需要结果的话可以直接跳到文章最后
a.初步实现:结合JavaScript实现瀑布流
原理:
- 子元素
绝对定位
- 通过
javascript
设置偏移量
html代码:
<!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"> <title>Document</title> <link rel="stylesheet" href="./index.css"> </head> <body> <div id="app"></div> <!-- <script src="https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.min.js"></script> --> <!-- <script src="./src/vue.min.js"></script> --> <script src="./util.js"></script> <script src="./index.js"></script> </body> </html>
css 代码:
html { background-color: black; } #app { position: relative; width: 100%; margin: auto; } .item { position: absolute; border-radius: 20px; border: 10px solid black; box-sizing: border-box; text-align: center; color: white; }
utils.js 代码:
const randomImgSize = [ // 这里是模拟各种尺寸的图片 { width:1920, height:1080 },{ width:640, height:720 },{ width:720, height:680 },{ width:720, height:1920 },{ width:900, height:600 },{ width:400, height:300 },{ width:2048, height:1920 },{ width:700, height:900 },{ width:1600, height:900 },{ width:200, height:200 } ] function randomColor(){ let r = Math.floor(Math.random() * 255) let g = Math.floor(Math.random() * 255) let b = Math.floor(Math.random() * 255) return `rgb(${r},${g},${b})` }
index.js 代码
// 创建元素函数 function generateBlock(size,color){ let dom = document.createElement('div') dom.setAttribute('class','item') dom.style.width = '240px' // 这里默认写死宽度,后面会优化 dom.style.height = `${240/size.width * size.height}px` // 由于这里没有图片资源,高度模拟计算 // 图片资源的话不需要直接width:100% 高度会自适应 dom.style.backgroundColor = color // 颜色也是随机生成而已 document.getElementById('app').appendChild(dom) return dom } // 元素统计数组 let domArr = [] // 元素高度数组 let heightArr = new Array(4) // 创建对应列数,存储每一列高度,用来设置偏移量 heightArr.fill(0) // 常规不考虑高度不整齐逻辑 randomImgSize.forEach(ele=>{ let dom =generateBlock(ele,randomColor()) domArr.push(dom) // 通过transform 属性可以一次设置,不用设置left和top,不过也一样 dom.style.transform = `translate(${(domArr.length-1)%4 * 100}%, ${heightArr[domArr.length%4]}px)` heightArr[domArr.length%4] += dom.offsetHeight })
实现内容:
-
初步实现瀑布流布局,正确计算每一个块(图片)的正确位置
-
每个块只是死板的按照从左到右,从上到下的顺序,可以优化:下一个块生成位置为当前列最低高度位置,自动填补进去
-
没有实现懒加载
b.稍微优化:每次自动填补到高度最低
index.js
// 创建元素函数 function generateBlock(size,color){ let dom = document.createElement('div') dom.setAttribute('class','item') dom.style.width = '240px' dom.style.height = `${240/size.width * size.height}px` dom.style.backgroundColor = color document.getElementById('app').appendChild(dom) return dom } // 元素统计数组 // let domArr = [] // 元素高度数组 let heightArr = new Array(4) heightArr.fill(0) // 常规不考虑高度不整齐逻辑 // randomImgSize.forEach(ele=>{ // let dom =generateBlock(ele,randomColor()) // domArr.push(dom) // dom.style.transform = `translate(${(domArr.length-1)%4 * 100}%, ${heightArr[domArr.length%4]}px)` // heightArr[domArr.length%4] += dom.offsetHeight // }) // 考虑高度不整齐逻辑 randomImgSize.forEach(ele=>{ let dom =generateBlock(ele,randomColor()) let minHeight= Math.min(...heightArr) let minHeightIndex = heightArr.indexOf(minHeight) dom.style.transform = `translate(${minHeightIndex * 100}%, ${minHeight}px)` heightArr[minHeightIndex] += dom.offsetHeight })
实现效果:
- 可以看到效果对比之前还是要好很多,不会出现极高或者极低的情况,而且代码还简化了,不需要数组来缓存元素,直接通过长度为4 的高度数组,查询应该插入到哪个列
- 对应列的偏移量:
x
为 100% * 对应列下标,y
为对应列下标高度 - 但是宽度还是写死的,而且没有实现懒加载
b.最终结果 (不到三十行代码)支持懒加载
index.js
// 响应式 const columns = 3 let tempArr = new Array(columns) // 创建数组用来缓存高度 tempArr.fill(0) function create(){ let observer = new IntersectionObserver(callback) function callback(entries){ if(entries[0].isIntersecting){ // 如果已进入视图,停止监听,并且生成新的元素 observer.unobserve(dom) create() } } function generateBlock(size,color){ let dom = document.createElement('div') dom.setAttribute('class','item') dom.style.width = `${Math.floor(100/columns)}%` // 宽度为百分比 dom.style.height = `${300/size.width * size.height}px` // 300为默认高度 dom.style.backgroundColor = color document.getElementById('app').appendChild(dom) return dom } let donSizeIndex = Math.floor(Math.random() * randomImgSize.length) // 随机生成大小(模拟不同尺寸的图片) let dom =generateBlock(randomImgSize[donSizeIndex],randomColor()) // 生成随机块 let minHeight= Math.min(...tempArr) // 找到对应列最小高度的值 let minHeightIndex = tempArr.indexOf(minHeight) // 找到对应列最小高度的下标 dom.style.transform = `translate(${minHeightIndex*100}%, ${minHeight}px)` // 根据下标进行变换,变换宽度为偏移多少个下标,上下为该下标所有高度 tempArr[minHeightIndex] += dom.offsetHeight // 对应下标增加高度 observer.observe(dom) // 观察该元素用作懒加载 } create()
实现效果:
实现比较简单其实,主要是javascript
操作样式,设置对应transform
属性,通过全局设置一个高度数组,每次添加元素时获取每一列高度,选取最小高度列,将元素偏移量
设置正常,并且通过intersectionObserver
对象,监听与视口交叉事件
,每次触发时,生成新的元素
同理可以拓展到加载图片..等等
总结
以上就是今天要的结合JavaScript实现图片瀑布流式布局+懒加载的实现过程,本文仅仅简单介绍了瀑布流布局相关设计思路,如果需要增加其他功能可以在该demo的基础上进行修改
觉得有帮助的话,点赞关注支持一下,b站前端同名分享号:猿油站
· [翻译] 为什么 Tracebit 用 C# 开发
· 腾讯ima接入deepseek-r1,借用别人脑子用用成真了~
· Deepseek官网太卡,教你白嫖阿里云的Deepseek-R1满血版
· DeepSeek崛起:程序员“饭碗”被抢,还是职业进化新起点?
· RFID实践——.NET IoT程序读取高频RFID卡/标签