需求文档:
瀑布流:宽度相同的多列不规则排布。
这些列宽度相同,列中的每一块宽度相同,高度不同。但是最终这些列的高度不能相差太大;
当滚动条滚动到页面底部时去加载下一页。
页面中所有的图片要延时加载。
原理:首先页面中的数据不能是写死的,是动态获取来的(AJAX),接着把数据绑定成 HTML。在插入到列之前,先给这些列按照高度进行升序排序,在插入数据时先给高度最矮的插,再给第二矮的插,最后给最高的插。这样就能保证最后高度相差不会太大。
关键问题
页面滚动到底去加载下一页:什么时候计算?页面滚动时,即 onscroll 事件触发时;
怎么计算?页面的 scrollHeight - 页面的 scrollTop - 浏览器可视窗口的高度 <= 0 表示已经滚到到底了
所以就是在页面的 onscroll 事件中去计算剩余高度,当剩余高度为0就表示要到底了。
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>花瓣网瀑布流效果</title>
<!-- IMPORT CSS -->
<link rel="stylesheet" href="css/reset.min.css">
<link rel="stylesheet" href="css/index.css">
</head>
<body>
<div class="container clearfix">
<!-- column列 -->
<div class="column">
<!-- item每一项 -->
<!-- <a class="item" href="">
<div class="imgBox">
<img src="img/1.jpg" alt="">
</div>
<p>泰勒·斯威夫特(Taylor Swift),1989年12月13日出生于美国宾州,美国歌手、演员。2006年出道,同年发行专辑《泰勒·斯威夫特》,该专辑获得美国唱片业协会的白金唱片认证</p>
</a> -->
</div>
<div class="column"></div>
<div class="column"></div>
</div>
<!-- IMPORT JS -->
<script src="js/jquery-1.11.3.min.js"></script>
<script src="node_modules/underscore/underscore.js"></script>
<script src="js/index.js"></script>
</body>
</html>
css
html,
body {
background: #E4E4E4;
overflow-x: hidden;
}
.container {
box-sizing: border-box;
margin: 20px auto;
width: 1000px;
/* display: flex; 基于 FLEX 实现水平排列,里面的每一项都会保持相同的高度,其中某一项变高,其余所有的项也都跟着变高 */
}
.container .column {
box-sizing: border-box;
float: left;
margin-right: 20px;
width: 320px;
}
.container .column:nth-last-child(1) {
margin-right: 0;
}
.container .column .item {
display: block;
padding: 10px;
margin-bottom: 10px;
background: #FFF;
box-shadow: 3px 3px 10px #666;
}
.container .column .item .imgBox {
/* height: 300px; 想要实现图片延迟加载,这块显示默认占位图,事先需要知道图片的高度(从服务获取的数据中有图片高度) */
background: url("../img/default.gif") no-repeat center center #EEE;
overflow: hidden;
}
.container .column .item .imgBox img {
display: none;
width: 100%;
}
.container .column .item p {
margin-top: 10px;
font-size: 12px;
color: #555;
line-height: 20px;
}
css清除默认样式用
body,h1,h2,h3,h4,h5,h6,hr,p,blockquote,dl,dt,dd,ul,ol,li,button,input,textarea,th,td{margin:0;padding:0}body{font-size:12px;font-style:normal;font-family:"\5FAE\8F6F\96C5\9ED1",Helvetica,sans-serif}small{font-size:12px}h1{font-size:18px}h2{font-size:16px}h3{font-size:14px}h4,h5,h6{font-size:100%}ul,ol{list-style:none}a{text-decoration:none;background-color:transparent}a:hover,a:active{outline-width:0;text-decoration:none}table{border-collapse:collapse;border-spacing:0}hr{border:0;height:1px}img{border-style:none}img:not([src]){display:none}svg:not(:root){overflow:hidden}html{-webkit-touch-callout:none;-webkit-text-size-adjust:100%}input,textarea,button,a{-webkit-tap-highlight-color:rgba(0,0,0,0)}article,aside,details,figcaption,figure,footer,header,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block}audio:not([controls]),video:not([controls]){display:none;height:0}progress{vertical-align:baseline}mark{background-color:#ff0;color:#000}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}button,input,select,textarea{font-size:100%;outline:0}button,input{overflow:visible}button,select{text-transform:none}textarea{overflow:auto}button,html [type="button"],[type="reset"],[type="submit"]{-webkit-appearance:button}button::-moz-focus-inner,[type="button"]::-moz-focus-inner,[type="reset"]::-moz-focus-inner,[type="submit"]::-moz-focus-inner{border-style:none;padding:0}button:-moz-focusring,[type="button"]:-moz-focusring,[type="reset"]:-moz-focusring,[type="submit"]:-moz-focusring{outline:1px dotted ButtonText}[type="checkbox"],[type="radio"]{box-sizing:border-box;padding:0}[type="number"]::-webkit-inner-spin-button,[type="number"]::-webkit-outer-spin-button{height:auto}[type="search"]{-webkit-appearance:textfield;outline-offset:-2px}[type="search"]::-webkit-search-cancel-button,[type="search"]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-input-placeholder{color:inherit;opacity:.54}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}.clear:after{display:block;height:0;content:"";clear:both}
js
/*
* debounce:函数防抖
* @params
* func:要执行的函数
* wait:间隔等待时间
* immediate:在开始边界还是结束边界触发执行(TRUE=>在开始边界)
* @return
* 可被调用的函数
*/
function debounce(func, wait, immediate) {
let result = null,
timeout = null;
return function (...args) {
let context = this,
now = immediate && !timeout;
clearTimeout(timeout); // 重要:在设置新的定时器之前,我们要把之前设置的定时器都干掉,因为防抖的目的是等待时间内,只执行一次
timeout = setTimeout(() => {
timeout = null;
if (!immediate) result = func.call(context, ...args);
}, wait);
if (now) result = func.call(context, ...args);
return result;
}
}
let flowModule = (function () {
let $columns = $('.column'),
_DATA = null;
// queryData:基于 AJAX 从服务器获取数据
let queryData = function () {
$.ajax({
url: 'json/data.json',
method: 'GET',
async: false,
success: result => {
_DATA = result;
}
});
};
// bindHTML:实现页面中的数据绑定
let bindHTML = function () {
// 瀑布流实现的原理:每一次从 _DATA 中取出三条数据,按照三列由矮到高的顺序依次插入
for (let i = 0; i < _DATA.length; i += 3) {
// 把数据按照图片由高到低排序
let group = _DATA.slice(i, i + 3);
if (i !== 0) {
group.sort((A, B) => B.height - A.height);
}
// 先按照高度排序(升序)
$columns.sort((A, B) => {
// A / B原生 JS 元素对象,想要使用 JQ 的方法,需要转换为 JQ 对象
let $A = $(A),
$B = $(B);
return $A.outerHeight() - $B.outerHeight();
}).each((index, column) => {
// _DATA[i+index] 计算出要往每一列中插入的数据
// let dataItem = _DATA[i + index];
let dataItem = group[index];
// 没有数据,说明数据都已经处理完了,我们结束循环
if (!dataItem) return false;
let {pic, height, title, link} = dataItem;
$(column).append(`<a class="item" href="${link}">
<div class="imgBox" style="height:${height}px">
<img src="" alt="" data-img="${pic}">
</div>
<p>${title}</p>
</a>`);
});
}
// 实现图片延迟加载:数据绑定完,延迟 1000MS 加载真实的图片
setTimeout(lazyImgs, 1000);
};
// lazyImgs:图片延迟加载
let lazyImgs = function () {
let $imgBoxs = $('.container .imgBox[isLoad!="true"]'),
$window = $(window),
B = $window.outerHeight() + $window.scrollTop();
// 循环每一个图片(图片盒子),计算其底边距离 BODY 的偏移,从而验证出是否加载真实图片
$imgBoxs.each((index, imgBox) => {
let $imgBox = $(imgBox),
$img = $imgBox.children('img'),
A = $imgBox.outerHeight() + $imgBox.offset().top;
// if ($imgBox.attr('isLoad') === "true") return;
if (A <= B) {
// 加载真实图片
$imgBox.attr('isLoad', 'true');
$img.attr('src', $img.attr('data-img'));
$img.on('load', () => {
// $img.css('display', 'block'); // 直接显示
$img.stop().fadeIn(); // 基于 JQ 动画渐现显示
});
}
});
};
// loadMore:加载更多数据
let loadMore = function () {
// 滚动到底端(一屏幕高度+卷去的高度+500>=页面真实的高度),加载更多数据
let $window = $(window),
winH = $window.outerHeight(),
scrollT = $window.scrollTop(),
pageH = $(document).height();
if (winH + scrollT + 500 >= pageH) {
queryData();
bindHTML();
}
};
return {
init: function () {
queryData();
bindHTML();
// 滚动条滚动处理一些事情
window.onscroll = _.throttle(function () {
// 延迟加载图片
lazyImgs();
// 加载更多数据
loadMore();
}, 50);
}
}
})();
flowModule.init();