瀑布流布局 不到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站前端同名分享号:猿油站