需求文档:

瀑布流:宽度相同的多列不规则排布。

  1. 这些列宽度相同,列中的每一块宽度相同,高度不同。但是最终这些列的高度不能相差太大;

  2. 当滚动条滚动到页面底部时去加载下一页。

  3. 页面中所有的图片要延时加载。

原理:首先页面中的数据不能是写死的,是动态获取来的(AJAX),接着把数据绑定成 HTML。在插入到列之前,先给这些列按照高度进行升序排序,在插入数据时先给高度最矮的插,再给第二矮的插,最后给最高的插。这样就能保证最后高度相差不会太大。

关键问题

  1. 页面滚动到底去加载下一页:什么时候计算?页面滚动时,即 onscroll 事件触发时;

  2. 怎么计算?页面的 scrollHeight - 页面的 scrollTop - 浏览器可视窗口的高度 <= 0 表示已经滚到到底了

  3. 所以就是在页面的 onscroll 事件中去计算剩余高度,当剩余高度为0就表示要到底了。

html

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>花瓣网瀑布流效果</title>
  6. <!-- IMPORT CSS -->
  7. <link rel="stylesheet" href="css/reset.min.css">
  8. <link rel="stylesheet" href="css/index.css">
  9. </head>
  10. <body>
  11. <div class="container clearfix">
  12. <!-- column列 -->
  13. <div class="column">
  14. <!-- item每一项 -->
  15. <!-- <a class="item" href="">
  16. <div class="imgBox">
  17. <img src="img/1.jpg" alt="">
  18. </div>
  19. <p>泰勒·斯威夫特(Taylor Swift),1989年12月13日出生于美国宾州,美国歌手、演员。2006年出道,同年发行专辑《泰勒·斯威夫特》,该专辑获得美国唱片业协会的白金唱片认证</p>
  20. </a> -->
  21. </div>
  22. <div class="column"></div>
  23. <div class="column"></div>
  24. </div>
  25. <!-- IMPORT JS -->
  26. <script src="js/jquery-1.11.3.min.js"></script>
  27. <script src="node_modules/underscore/underscore.js"></script>
  28. <script src="js/index.js"></script>
  29. </body>
  30. </html>

css

  1. html,
  2. body {
  3. background: #E4E4E4;
  4. overflow-x: hidden;
  5. }
  6. .container {
  7. box-sizing: border-box;
  8. margin: 20px auto;
  9. width: 1000px;
  10. /* display: flex; 基于 FLEX 实现水平排列,里面的每一项都会保持相同的高度,其中某一项变高,其余所有的项也都跟着变高 */
  11. }
  12. .container .column {
  13. box-sizing: border-box;
  14. float: left;
  15. margin-right: 20px;
  16. width: 320px;
  17. }
  18. .container .column:nth-last-child(1) {
  19. margin-right: 0;
  20. }
  21. .container .column .item {
  22. display: block;
  23. padding: 10px;
  24. margin-bottom: 10px;
  25. background: #FFF;
  26. box-shadow: 3px 3px 10px #666;
  27. }
  28. .container .column .item .imgBox {
  29. /* height: 300px; 想要实现图片延迟加载,这块显示默认占位图,事先需要知道图片的高度(从服务获取的数据中有图片高度) */
  30. background: url("../img/default.gif") no-repeat center center #EEE;
  31. overflow: hidden;
  32. }
  33. .container .column .item .imgBox img {
  34. display: none;
  35. width: 100%;
  36. }
  37. .container .column .item p {
  38. margin-top: 10px;
  39. font-size: 12px;
  40. color: #555;
  41. line-height: 20px;
  42. }

css清除默认样式用

  1. 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}

瀑布流.png

js

  1. /*
  2. * debounce:函数防抖
  3. * @params
  4. * func:要执行的函数
  5. * wait:间隔等待时间
  6. * immediate:在开始边界还是结束边界触发执行(TRUE=>在开始边界)
  7. * @return
  8. * 可被调用的函数
  9. */
  10. function debounce(func, wait, immediate) {
  11. let result = null,
  12. timeout = null;
  13. return function (...args) {
  14. let context = this,
  15. now = immediate && !timeout;
  16. clearTimeout(timeout); // 重要:在设置新的定时器之前,我们要把之前设置的定时器都干掉,因为防抖的目的是等待时间内,只执行一次
  17. timeout = setTimeout(() => {
  18. timeout = null;
  19. if (!immediate) result = func.call(context, ...args);
  20. }, wait);
  21. if (now) result = func.call(context, ...args);
  22. return result;
  23. }
  24. }
  25. let flowModule = (function () {
  26. let $columns = $('.column'),
  27. _DATA = null;
  28. // queryData:基于 AJAX 从服务器获取数据
  29. let queryData = function () {
  30. $.ajax({
  31. url: 'json/data.json',
  32. method: 'GET',
  33. async: false,
  34. success: result => {
  35. _DATA = result;
  36. }
  37. });
  38. };
  39. // bindHTML:实现页面中的数据绑定
  40. let bindHTML = function () {
  41. // 瀑布流实现的原理:每一次从 _DATA 中取出三条数据,按照三列由矮到高的顺序依次插入
  42. for (let i = 0; i < _DATA.length; i += 3) {
  43. // 把数据按照图片由高到低排序
  44. let group = _DATA.slice(i, i + 3);
  45. if (i !== 0) {
  46. group.sort((A, B) => B.height - A.height);
  47. }
  48. // 先按照高度排序(升序)
  49. $columns.sort((A, B) => {
  50. // A / B原生 JS 元素对象,想要使用 JQ 的方法,需要转换为 JQ 对象
  51. let $A = $(A),
  52. $B = $(B);
  53. return $A.outerHeight() - $B.outerHeight();
  54. }).each((index, column) => {
  55. // _DATA[i+index] 计算出要往每一列中插入的数据
  56. // let dataItem = _DATA[i + index];
  57. let dataItem = group[index];
  58. // 没有数据,说明数据都已经处理完了,我们结束循环
  59. if (!dataItem) return false;
  60. let {pic, height, title, link} = dataItem;
  61. $(column).append(`<a class="item" href="${link}">
  62. <div class="imgBox" style="height:${height}px">
  63. <img src="" alt="" data-img="${pic}">
  64. </div>
  65. <p>${title}</p>
  66. </a>`);
  67. });
  68. }
  69. // 实现图片延迟加载:数据绑定完,延迟 1000MS 加载真实的图片
  70. setTimeout(lazyImgs, 1000);
  71. };
  72. // lazyImgs:图片延迟加载
  73. let lazyImgs = function () {
  74. let $imgBoxs = $('.container .imgBox[isLoad!="true"]'),
  75. $window = $(window),
  76. B = $window.outerHeight() + $window.scrollTop();
  77. // 循环每一个图片(图片盒子),计算其底边距离 BODY 的偏移,从而验证出是否加载真实图片
  78. $imgBoxs.each((index, imgBox) => {
  79. let $imgBox = $(imgBox),
  80. $img = $imgBox.children('img'),
  81. A = $imgBox.outerHeight() + $imgBox.offset().top;
  82. // if ($imgBox.attr('isLoad') === "true") return;
  83. if (A <= B) {
  84. // 加载真实图片
  85. $imgBox.attr('isLoad', 'true');
  86. $img.attr('src', $img.attr('data-img'));
  87. $img.on('load', () => {
  88. // $img.css('display', 'block'); // 直接显示
  89. $img.stop().fadeIn(); // 基于 JQ 动画渐现显示
  90. });
  91. }
  92. });
  93. };
  94. // loadMore:加载更多数据
  95. let loadMore = function () {
  96. // 滚动到底端(一屏幕高度+卷去的高度+500>=页面真实的高度),加载更多数据
  97. let $window = $(window),
  98. winH = $window.outerHeight(),
  99. scrollT = $window.scrollTop(),
  100. pageH = $(document).height();
  101. if (winH + scrollT + 500 >= pageH) {
  102. queryData();
  103. bindHTML();
  104. }
  105. };
  106. return {
  107. init: function () {
  108. queryData();
  109. bindHTML();
  110. // 滚动条滚动处理一些事情
  111. window.onscroll = _.throttle(function () {
  112. // 延迟加载图片
  113. lazyImgs();
  114. // 加载更多数据
  115. loadMore();
  116. }, 50);
  117. }
  118. }
  119. })();
  120. flowModule.init();

瀑布流-图片加载问题.png