<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>瀑布流</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #f8f9fa;
font-family: 'Segoe UI', sans-serif;
}
.container {
position: relative;
margin: 20px auto;
max-width: 1200px;
}
.card {
position: absolute;
width: 280px;
background: white;
border-radius: 15px;
box-shadow: 0 5px 15px rgba(0,0,0,0.06);
transition: transform 0.3s ease, opacity 0.3s ease;
opacity: 0;
}
.card.visible {
opacity: 1;
}
.card-content {
padding: 25px;
}
h3 {
color: rgba(0,0,0,0.8);
margin-bottom: 12px;
font-size: 1.3em;
}
p {
color: rgba(0,0,0,0.6);
line-height: 1.6;
font-size: 0.95em;
}
.bg1 { background: #f0f8ff }
.bg2 { background: #f5f0ff }
.bg3 { background: #f0fff4 }
.bg4 { background: #fff8f0 }
.bg5 { background: #fff0f5 }
</style>
</head>
<body>
<div class="container" id="container"></div>
<script>
const cardsData = [
{ title: '晨曦微光', content: '黎明前的天空泛起鱼肚白,晨露在草叶上闪烁' },
{ title: '云海幻境', content: '群山之巅的云浪翻滚,如入蓬莱仙境。云层如轻纱般流动,朝阳为云海镶上金边' },
{ title: '林间小径', content: '斑驳树影下的碎石路通向未知的秘境,青苔悄悄爬上古老石阶' },
{ title: '琉璃湖畔', content: '风过无痕的水面倒映着天空的蔚蓝,几只白鹭掠过泛起涟漪' },
{ title: '青瓷记忆', content: '釉色流淌间凝固了千年的时光,冰裂纹中藏着岁月的密码' },
{ title: '花间絮语', content: '花瓣随风起舞诉说春日的私语,花香在暖阳中缓缓流淌' },
{ title: '茶韵悠长', content: '氤氲香气中品味时光的沉淀,茶汤在杯中画出金色漩涡' },
{ title: '纸上流年', content: '墨迹晕染开泛黄的书卷记忆,毛笔尖悬停着未尽的思绪' },
{ title: '雨巷琴声', content: '潮湿石板路上飘散的音符回响,油纸伞划过雨幕的弧度' },
{ title: '远山含黛', content: '水墨画般的轮廓在天际若隐若现,雾霭为群山蒙上薄纱' }
];
class Waterfall {
constructor(container, colWidth = 280, gap = 20) {
this.container = container;
this.colWidth = colWidth;
this.gap = gap;
this.cols = [];
this.init();
window.addEventListener('resize', () => this.debouncedLayout());
}
init() {
this.createCols();
this.debouncedLayout = this.debounce(() => this.layout());
}
createCols() {
const containerWidth = this.container.offsetWidth;
const colCount = Math.floor((containerWidth + this.gap) / (this.colWidth + this.gap));
this.cols = Array(colCount).fill(0);
}
layout() {
this.createCols();
const cards = Array.from(this.container.children);
const positions = [];
cards.forEach(card => {
const minHeight = Math.min(...this.cols);
const colIndex = this.cols.indexOf(minHeight);
const x = colIndex * (this.colWidth + this.gap);
const y = minHeight + (minHeight > 0 ? this.gap : 0);
positions.push({x, y, height: card.offsetHeight});
this.cols[colIndex] = y + card.offsetHeight;
});
cards.forEach((card, i) => {
card.style.transform = `translate(${positions[i].x}px, ${positions[i].y}px)`;
card.classList.add('visible');
});
this.container.style.height = Math.max(...this.cols) + 'px';
}
debounce(fn, delay = 100) {
let timer;
return () => {
clearTimeout(timer);
timer = setTimeout(fn, delay);
};
}
}
const container = document.getElementById('container');
const waterfall = new Waterfall(container);
cardsData.forEach((item, index) => {
const card = document.createElement('div');
card.className = `card bg${Math.floor(Math.random() * 5) + 1}`;
const randomHeight = 150 + Math.random() * 150;
card.style.height = `${randomHeight}px`;
const content = `
<div class="card-content">
<h3>${item.title}</h3>
<p>${item.content}</p>
</div>
`;
card.innerHTML = content;
container.appendChild(card);
});
setTimeout(() => waterfall.layout(), 50);
window.addEventListener('scroll', () => {
if (window.innerHeight + window.scrollY >= document.body.offsetHeight - 100) {
setTimeout(() => waterfall.layout(), 50);
}
});
</script>
</body>
</html>