bugfix250107.1
全站通知:

Widget:Waterfall

来自恋与深空WIKI_BWIKI_哔哩哔哩
跳到导航 跳到搜索

Waterfall mini

基于 Waterfall.js https://raphamorim.io/waterfall.js/ 代码极小,不依赖JQ。

用法:

{{#Widget:Waterfall}}
<div class="waterfall-container">
    <div>内容块</div>
    <div>内容块</div>
    <div>......</div>
    <div>内容块</div>
</div>

当然也可以将其保存为JS文件,在指定页面动态加载。


功能更完善的另一个选择是 Masonry https://masonry.desandro.com/

源码

<script>
/* https://raphamorim.io/waterfall.js/ */
//function waterfall(a){function b(a,b){var c=window.getComputedStyle(b);return parseFloat(c["margin"+a])||0}function c(a){return a+"px"}function d(a){return parseFloat(a.style.top)}function e(a){return parseFloat(a.style.left)}function f(a){return a.clientWidth}function g(a){return a.clientHeight}function h(a){return d(a)+g(a)+b("Bottom",a)}function i(a){return e(a)+f(a)+b("Right",a)}function j(a){a=a.sort(function(a,b){return h(a)===h(b)?e(b)-e(a):h(b)-h(a)})}function k(b){f(a)!=t&&(b.target.removeEventListener(b.type,arguments.callee),waterfall(a))}"string"==typeof a&&(a=document.querySelector(a));var l=[].map.call(a.children,function(a){return a.style.position="absolute",a});a.style.position="relative";var m=[];l.length&&(l[0].style.top="0px",l[0].style.left=c(b("Left",l[0])),m.push(l[0]));for(var n=1;n<l.length;n++){var o=l[n-1],p=l[n],q=i(o)+f(p)<=f(a);if(!q)break;p.style.top=o.style.top,p.style.left=c(i(o)+b("Left",p)),m.push(p)}for(;n<l.length;n++){j(m);var p=l[n],r=m.pop();p.style.top=c(h(r)+b("Top",p)),p.style.left=c(e(r)),m.push(p)}j(m);var s=m[0];a.style.height=c(h(s)+b("Bottom",s));var t=f(a);window.addEventListener?window.addEventListener("resize",k):document.body.onresize=k}
function waterfall (container) {
  if (typeof (container) === 'string') {
    container = document.querySelector(container)
  }

  function style (el) {
    return window.getComputedStyle(el)
  }

  function margin (name, el) {
    return parseFloat(style(el)['margin' + name]) || 0
  }

  function px (n) { return parseFloat(n) + 'px' }
  function y (el) { return parseFloat(el.style.top) }
  function x (el) { return parseFloat(el.style.left) }
  function width (el) { return parseFloat(style(el).width) }
  function height (el) { return parseFloat(style(el).height) }
  function bottom (el) { return y(el) + height(el) + margin('Bottom', el) }
  function right (el) { return x(el) + width(el) + margin('Right', el) }

  function sort (l) {
    l = l.sort(function (a, b) {
      var bottomDiff = bottom(b) - bottom(a)
      return bottomDiff || x(b) - x(a)
    })
  }

  function Boundary (firstRow) {
    var els = firstRow
    sort(els)

    this.add = function (el) {
      els.push(el)
      sort(els)
      els.pop()
    }

    this.min = function () { return els[els.length - 1] }
    this.max = function () { return els[0] }
  }

  function placeEl (el, top, left) {
    el.style.position = 'absolute'
    el.style.top = px(top)
    el.style.left = px(left)
  }

  function placeFirstElement (el) {
    placeEl(el, 0, margin('Left', el))
  }

  function placeAtTheFirstLine (prev, el) {
    placeEl(el, prev.style.top, right(prev) + margin('Left', el))
  }

  function placeAtTheSmallestColumn (minEl, el) {
    placeEl(el, bottom(minEl) + margin('Top', el), x(minEl))
  }

  function adjustContainer (container, maxEl) {
    container.style.position = 'relative'
    container.style.height = px(bottom(maxEl) + margin('Bottom', maxEl))
  }

  function thereIsSpace (els, i) {
    return Math.ceil(right(els[i - 1]) + width(els[i])) <= Math.ceil(width(container))
  }

  var els = Array.from(container.children)
  for(let i = els.length - 1; i >= 0; i--){
    if(els[i].style.display === 'none'){
      els.splice(i, 1);
    }
  }

  if (els.length) {
    placeFirstElement(els[0])
  }

  for (var i = 1; i < els.length && thereIsSpace(els, i); i++) {
    placeAtTheFirstLine(els[i - 1], els[i])
  }

  var firstRow = [].slice.call(els, 0, i)
  var boundary = new Boundary(firstRow)

  for (; i < els.length; i++) {
    placeAtTheSmallestColumn(boundary.min(), els[i])
    boundary.add(els[i])
  }

  adjustContainer(container, boundary.max())
}

/* 处理所有的 .waterfall-container 元素 */
(function() {
    console.log("waterfall loading...")
    var name = '.waterfall-container';
    function main() {
        document.querySelectorAll(name).forEach((el) =>{waterfall(el);})
    }
    if (document.readyState === "loading") {
        document.addEventListener('DOMContentLoaded', main);
    } else {
        main();
    }
})();
</script>