内容
多个等宽的图片进行穿插排序成六列,并且在目前显示的最后一张图片显示之后,再进行下拉时能够在进行加载其他图片,每次下拉显示20-30张图片。
实验原理
计算每个图片,并利用定位使下面的图片插入最小高度的图片下面
监听窗口与滚动条高度变化,利用懒加载使其不断地展示图片
html
<!DOCTYPE html>
<html lang="zh-CN">
<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>demo12</title>
<link rel="stylesheet" href="./css/demo12.css">
</head>
<body>
<div class="waterfall" id="container"></div>
<script src="./js/demo12.js"></script>
</body>
</html>
css
<style>
* {
margin: 0;
padding: 0;
}
.waterfall {
width: 100%;
display: flex;
flex-wrap: wrap;
position: relative;
}
.item {
width: 240px;
background-color: #ccc;
position: absolute;
vertical-align: top;
}
</style>
js
<script>
let imgs1=[
{path:'https://ts3.cn.mm.bing.net/th?id=OIP-C.Rq5h9rKa8uBoUxtGTCCVRQHaKd&w=210&h=297&c=8&rs=1&qlt=90&o=6&dpr=2&pid=3.1&rm=2', width:200, height:284},
{path:'https://ts3.cn.mm.bing.net/th?id=OIP-C.atTl-J5TF3GE5FWfRtTIygHaJ8&w=215&h=289&c=8&rs=1&qlt=90&o=6&dpr=2&pid=3.1&rm=2', width:200, height:270},
{path:'https://ts3.cn.mm.bing.net/th?id=OIP-C.QFdwl07_aviM1ch2KpyyFgHaEo&w=316&h=197&c=8&rs=1&qlt=90&o=6&dpr=2&pid=3.1&rm=2', width:200, height:125},
{path:'https://ts1.cn.mm.bing.net/th?id=OIP-C.nfC2tVNM9TgwQ5QuqECd6wHaFj&w=288&h=216&c=8&rs=1&qlt=90&o=6&dpr=2&pid=3.1&rm=2', width:200, height:150},
{path:'https://ts2.cn.mm.bing.net/th?id=OIP-C.pgBMw_lMOYSOCZIDQ896NAHaLF&w=204&h=305&c=8&rs=1&qlt=90&o=6&dpr=2&pid=3.1&rm=2', width:200, height:300},
{path:'https://ts1.cn.mm.bing.net/th?id=OIP-C.1-wFaNNsH_OCBFvGCfloWgHaJ4&w=216&h=288&c=8&rs=1&qlt=90&o=6&dpr=2&pid=3.1&rm=2', width:200, height:267},
{path:'https://ts1.cn.mm.bing.net/th?id=OIP-C.ebfLNO07a781ncjScj7u6wHaLs&w=198&h=314&c=8&rs=1&qlt=90&o=6&dpr=2&pid=3.1&rm=2', width:200, height:318},
{path:'https://tse4-mm.cn.bing.net/th/id/OIP-C.uNxOyVhFHzUweInZEahy-QHaFj?w=231&h=180&c=7&r=0&o=5&dpr=2&pid=1.7',width:200,height:156},
{path:'https://tse2-mm.cn.bing.net/th/id/OIP-C.XXrW8aTA10CkicQuGceNjgHaE7?w=263&h=180&c=7&r=0&o=5&dpr=2&pid=1.7',width:200,height:137},
{path:'https://tse2-mm.cn.bing.net/th/id/OIP-C.mQBvGOCQDvosB6SXXIljfwHaE0?w=304&h=197&c=7&r=0&o=5&dpr=2&pid=1.7',width:200,height:130},
{path:'https://tse3-mm.cn.bing.net/th/id/OIP-C.pVMgoYOCp9YQ3BnH1AhuggHaE7?w=296&h=197&c=7&r=0&o=5&dpr=2&pid=1.7',width:200,height:135},
{path:'https://tse1-mm.cn.bing.net/th/id/OIP-C.YTQX73HsbhdnueV0dzYPpAHaEW?w=314&h=184&c=7&r=0&o=5&dpr=2&pid=1.7',width:200,height:120},
{path:'https://tse4-mm.cn.bing.net/th/id/OIP-C.ivx7EPOK5Y3d4J_Z2OlydgHaE7?w=260&h=180&c=7&r=0&o=5&dpr=2&pid=1.7',width:200,height:140},
{path:'https://tse1-mm.cn.bing.net/th/id/OIP-C.h02sipGoXVxrZGX27e2hoQHaE3?w=234&h=180&c=7&r=0&o=5&dpr=2&pid=1.7',width:200,height:154},
{path:'https://tse4-mm.cn.bing.net/th/id/OIP-C.4eNrKDHGVHrdX45iP22r6AHaEK?w=283&h=180&c=7&r=0&o=5&dpr=2&pid=1.7',width:200,height:125},
{path:'https://tse1-mm.cn.bing.net/th/id/OIP-C.hNu5gDicpIJY8yCE-hM2EwHaNK?w=115&h=184&c=7&r=0&o=5&dpr=2&pid=1.7',width:200,height:320},
{path:'https://tse3-mm.cn.bing.net/th/id/OIP-C.MfAU3E769jbMcGoA-WrbHwHaEK?w=294&h=180&c=7&r=0&o=5&dpr=2&pid=1.7',width:200,height:123},
{path:'https://tse2-mm.cn.bing.net/th/id/OIP-C.lE3uaNuJUHCapn4EnEPFbgHaE8?w=267&h=180&c=7&r=0&o=5&dpr=2&pid=1.7',width:200,height:135},
{path:'https://tse4-mm.cn.bing.net/th/id/OIP-C.OjgEXcW8FmAnIoO0bwTldwHaEK?w=297&h=183&c=7&r=0&o=5&dpr=2&pid=1.7',width:200,height:124},
{path:'https://tse2-mm.cn.bing.net/th/id/OIP-C.y8bP8drHeoVXB53aMIQhIQAAAA?w=189&h=190&c=7&r=0&o=5&dpr=2&pid=1.7',width:200,height:202},
]
// 获取容器元素
let container = document.getElementById('container')
// 造图片
function createImg(imgItem) {
let img = new Image()
img.setAttribute('data-src',imgItem.path)
img.height = imgItem.height
img.className = 'item'
img.src = ''
img.alt = 'loading...'
return img
}
// 图片标签上树
function loadImg(imgArr) {
let nodeImgArr = []
for(let i = 0; i < imgArr.length; i++) {
container.appendChild(createImg(imgArr[i]))
}
// 上树完成之后 就布局
waterfall()
}
// 瀑布流布局
function waterfall() {
// 得到所有图片和定义的间距
let items = document.getElementsByClassName('item')
let padNum = 10
// 确定有多少列
let itemWidth =items[0].offsetWidth
let columns =6
// 定义一个映射高度数组 * 核心
let heightArr = []
for (var i = 0; i < items.length; i++) {
if (i < columns) {
// 表示是第一行
items[i].style.top = 0
items[i].style.left = i * (itemWidth + padNum) + 'px'
items[i].src = items[i].dataset['src']
heightArr.push(items[i].offsetHeight)
// 更新高度
}else{
// 表示第二行开始了
// 要得到最小高度 以及最小高度所在的索引
let minHeight = Math.min(...heightArr)
let heightIdx = heightArr.findIndex((item, index) => item === minHeight)
// items[i]是否在可视区域
items[i].style.top = minHeight + padNum + 'px'
items[i].style.left = heightIdx * (itemWidth + padNum) + 'px'
// 判断是否在视口内
if(isInViewPort(items[i])) {
items[i].src = items[i].dataset['src']
}
// 更新高度
heightArr[heightIdx] += (items[i].clientHeight + padNum)
}
}
}
window.onload=function(){
// 当这个图片数组已经渲染过了之后 就不需要再加载了
loadImg(imgs1)
}
// 监听尺寸变化
window.onresize = function() {
waterfall()
if(touchBottom()) {
loadImg(imgs1)
}
}
window.onscroll = function(){
waterfall()
if(touchBottom()) {
loadImg(imgs1)
}
}
// 获取视口宽度 方法 -- 兼容
function getClient() {
return {
width: window.innerWidth || document.documentElement.clientHeight || document.body.clientHeight,
height: window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight
}
}
// 判断元素是否在视窗内
function isInViewPort(element){
const viewWidth = window.innerWidth || document.documentElement.clientWidth;
const viewHeight = window.innerHeight || document.documentElement.clientHeight;
const {
top,
right,
bottom,
left,
}=element.getBoundingClientRect();
return(
// top >= 0 &&
// left >= 0 &&
// right <= viewWidth &&
// bottom <= viewHeight
top >= 0 && (top + 30) <= viewHeight
);
}
// 获取滚动值 方法 -- 兼容
function getScrollTop() {
return window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
}
// 判断页面是否触底 //页面卷去高度 + 浏览器可视窗口的高度 >= 整个页面的高度
function touchBottom() {
return getScrollTop() + getClient().height + 10 >= document.body.scrollHeight
}
</script>
样式
瀑布流布局