瀑布流布局详谈

瀑布流3种布局方式

  1. column-count (纯css)
  2. flex-box (纯css)
  3. js 计算每个元素绝对定位

布局优劣

  • css
    兼容性差,只能纵向排列,无法应用于动态加载
    好处是容易写
  • js
    写起来麻烦,性能差
    横向排布,能更好地利用空间,可支持动态加载

JS 布局代码


  // 瀑布流布局
  const waterfall = useCallback(() => {
    // 外层
    const layer = document.getElementById('approval-desk')
    // 容器
    const container: HTMLElement = _.get(document.getElementsByClassName('tabs-content'), '[0]')
    // 容器内需要瀑布流布局的元素
    const arr = document.getElementsByClassName('tabs-container') as HTMLCollectionOf<HTMLElement>

    if (!layer || !container || !arr.length) return

    // 移动端布局
    if (document.documentElement.clientWidth <= 1020) {
      // 清除样式
      container.style.cssText = ''
      _.forEach(arr, item => { item.style.cssText = '' })
    }

    const layerWidth = layer.clientWidth
    const clientRects = _.map(arr, item => item.getBoundingClientRect())

    // 列宽:由于每列宽度一样,取第一列即可
    const columnWidth = clientRects[0].width
    // 列数
    let columnCount = parseInt(`${layerWidth / columnWidth}`)
    // 不能超过总元素个数
    if (columnCount > clientRects.length) columnCount = clientRects.length

    if (!columnCount) return

    // 布局中每个元素的定位数据
    const cells = Array(columnCount).fill(1).map(() => []) as {
      top: number,
      height: number,
    }[][]

    // 容器样式
    container.style.cssText
      = `position: relative; width: ${columnCount * columnWidth}px; margin: auto;`

    // 当前循环的元素需要放入的列下标
    let columnIndex = 0
    for (let i = 0; i < clientRects.length; i++) {
      // 当前循环的元素
      const rect = clientRects[i]
      // 需要放入的列
      const column = cells[columnIndex]

      // 该列的最后一个元素的定位数据,还未放入过元素则取默认值
      const lastColumnRect = column.length
        ? column[column.length - 1]
        : { top: 0, height: 0 }

      // 当前元素的定位数据
      const newCell = {
        top: lastColumnRect.top + lastColumnRect.height,
        height: rect.height,
      }
      column.push(newCell)

      arr[i].style.cssText
        = `position: absolute; top: ${newCell.top}px; left: ${columnWidth * columnIndex}px`

      // 每一列的高度
      const columnHeights = cells.map(column => _.sum(column.map(cell => cell.height)))
      // 最低高度
      const minHeight = Math.min(...columnHeights)
      // 下一个元素需要放入的列下标
      columnIndex = columnHeights.findIndex(height => height === minHeight)
    }
  }, [])

  // 拉取到分类数据后,调用瀑布流布局函数
  useEffect(() => waterfall, [categories])
  // 窗口大小改变时,调用瀑布流布局函数
  useEffect(() => {
    window.addEventListener('resize', waterfall)
    return () => window.removeEventListener('resize', waterfall)
  }, [])
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值