图片瀑布流是一种常见的页面特效,通过动态计算每一张图片的位置,在有限的空间内展示最多的图片
本案例实现的图片瀑布流是在图片宽度相等,高度不一致的情况下
实现思路
通过容器宽度与单张图片宽度计算出容器内可放置图片最大列数,根据最大列数生成对应的列高数组集合。监听所有图片onload事件,当图片加载成功时,找到列高集合中最小数及其索引(当前图片列高最小位置,即图片插入位置),图片插入后更新列高数组对应列数值;当视口发生变化时,即window.onresize事件触发时,重新计算最大列数、生成对应列高数组集合、计算每张图片具体位置。
具体实现代码
基本布局
<!DOCTYPE html>
<html>
<head>
<meta chartset="utf-8">
<style>
// 基础样式设置
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
#container {
position: relative;
font-size: 0;
}
#container img {
position: absolute;
width: 100;
transition: all .5s;
}
</style>
<title>图片瀑布流</title>
</head>
<body>
<div id="container">
<!--放置图片 -->
<img src="http://via.placeholder.com/100x100" alt="图片">
<img src="http://via.placeholder.com/100x200" alt="图片">
<img src="http://via.placeholder.com/100x50" alt="图片">
<img src="http://via.placeholder.com/100x60" alt="图片">
<!-- 更多图片 -->
</div>
</body>
</html>
复制代码
JS代码
// 核心代码
class Falls {
constructor (options = {}) {
// 容器、容器宽度
this.el = this.isElement(options.el) ? options.el : document.querySelector(options.el)
this.elWidth = this.getElWidth(this.el)
// 图片top right bottom left 偏移量
this.offset = options.offset || 10
// window.onresize方法防抖延迟
this.delay = options.delay || 100
// 图片集合、图片宽度
this.imgs = Array.from(this.el.querySelectorAll('img'))
this.imgWidth = this.getElWidth(this.imgs[0]) + this.offset
// 容器内展示图片列数、列高度集合
this.colCount = ~~(this.elWidth / this.imgWidth)
this.colHeightList = []
// 生成列高数组集合、监听图片onload、监听window.resize
this.initColHeightList()
this.bindImgOnload()
this.onResize()
}
// 辅助方法
isElement (el) {
return el.nodeType === 1
}
getElWidth (el) {
let width = el.style.width || el.clientWidth || el.offsetWidth
return width
}
getElHeight (el) {
let height = el.style.height || el.clientHeight || el.offsetHeight
return height
}
bounce (callback) {
let timer = null
let that = this
return function () {
let arg = arguments
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
callback.apply(this, arg)
}, that.delay)
}
}
// 核心方法
initColHeightList () {
this.colHeightList = []
for (let i = 0; i < this.colCount; i++) {
this.colHeightList[i] = 0
}
}
bindImgOnload () {
this.imgs.forEach((img, i) => {
img.addEventListener('load', (e) => {
this.countPosition(e.target)
})
})
}
countPosition (el) {
let minHeight = Math.min(...this.colHeightList)
let minHeightIndex = this.colHeightList.indexOf(minHeight)
el.style.top = minHeight + this.offset + 'px'
el.style.left = minHeightIndex * this.imgWidth + 'px'
this.colHeightList[minHeightIndex] += this.getElHeight(el) + this.offset
}
onResize () {
let that = this
window.addEventListener('resize', this.bounce(() => {
// 重新计算
that.elWidth = that.getElWidth(that.el)
that.colCount = ~~(that.elWidth / that.imgWidth)
that.initColHeightList()
that.imgs.forEach(item => {
that.countPosition(item)
})
}))
}
}
复制代码
调用方式
const falls = new Falls({
el: '#container', // 指定容器
offset: 10, // 指定图片偏移量
delay: 100 // 指定resize事件防抖延迟
})
复制代码