我报名参与金石计划 1 期挑战——瓜分 10 万奖池,这是我的第 4 篇文章,点击检查活动详情

一、布景

本文介绍 5 种瀑布流场景的完成,我们能够依据自身的需求场景进行挑选

5 种场景分别是:

瀑布流 特色
纵向+高度排序 纯 CSS 多列完成,是最简单的瀑布流写法
纵向+高度排序+依据宽度自习惯列数 经过 JS 依据屏幕宽度核算列数,在 web 端更加灵敏的展现瀑布流
横向 纯 CSS 弹性布局完成,是最简单的横向瀑布流写法
横向+高度排序 横向+高度排序的瀑布流,需求经过 JS 核算每一列高度,损耗功用,但是能够防止某列特别长的情况,体会更好
横向+高度排序+依据宽度自习惯列数 需求经过 JS 核算每一列高度,并依据屏幕宽度核算列数,损耗功用,但是能够防止某列特别长的情况,而且能够在 web 端更加灵敏的展现瀑布流,体会更好,是 5 种瀑布流中用户体会最好的

我已经将这 5 种场景的完成封装成 npm 包,npm 包地址:www.npmjs.com/package/rea…,能够直接在 React 项目中装置运用。

二、介绍

瀑布流,是比较盛行的一种网站页面布局,视觉表现为良莠不齐的多栏布局,跟着页面翻滚条向下翻滚,这种布局还会不断加载数据块并附加至当前尾部。

下图就是一个瀑布流布局的示意图:

5 种瀑布流场景的完成原理解析

三、纵向+高度排序

纵向+高度排序指的是,每列依照纵向摆放,往高度最小的列增加内容,如下图所示。

5 种瀑布流场景的完成原理解析

完成纵向+高度排序瀑布流的办法是 CSS 多列布局

1. 多列布局介绍

多列布局指的是 CSS3 能够将文本内容设计成像报纸相同的多列布局,如下实例:

5 种瀑布流场景的完成原理解析

CSS3 的多列特点:

  • column-count:指定了需求分割的列数;
  • column-gap:指定了列与列间的间隙;
  • column-rule-style:指定了列与列间的边框款式;
  • column-rule-width:指定了两列的边框厚度;
  • column-rule-color:指定了两列的边框颜色;
  • column-rule:是 column-rule-* 一切特点的简写;
  • column-span:指定元素跨越多少列;
  • column-width:指定了列的宽度。

2. 完成思路

瀑布流完成思路如下:

  • 经过 CSS column-count 分割内容为指定列;
  • 经过 CSS break-inside 确保每个子元素烘托完再换行;

3. 完成代码

.css-column {
  column-count: 4; //分为4列
}
.css-column div {
  break-inside: avoid; // 确保每个子元素烘托完在换行
}

4. 直接运用 npm 包

npm – react-masonry-component2 的运用办法:

import { Masonry } from 'react-masonry-component2'
export const MyComponent = (args) => {
  return (
    <Masonry direction='column'>
      <div></div>
      <div></div>
      <div></div>
    </Masonry>
  )
}

在线预览:632339a3ed0b247d36b0fa3c-njrsmzdcdj.chromatic.com/?path=/stor…

四、纵向+高度排序+依据宽度自习惯列数

在纵向+高度排序的基础上,依照宽度自习惯列数。

5 种瀑布流场景的完成原理解析

1. 完成思路

  • 监听 resize 办法,依据屏幕宽度得到该宽度下应该展现的列数

2. 完成代码

import { useCallback, useEffect, useMemo, useState } from 'react'
import { DEFAULT_COLUMNS_COUNT } from '../const'
export const useHasMounted = () => {
  const [hasMounted, setHasMounted] = useState(false)
  useEffect(() => {
    setHasMounted(true)
  }, [])
  return hasMounted
}
export const useWindowWidth = () => {
  const hasMounted = useHasMounted()
  const [width, setWidth] = useState(0)
  const handleResize = useCallback(() => {
    if (!hasMounted) return
    setWidth(window.innerWidth)
  }, [hasMounted])
  useEffect(() => {
    if (hasMounted) {
      window.addEventListener('resize', handleResize)
      handleResize()
      return () => window.removeEventListener('resize', handleResize)
    }
  }, [hasMounted, handleResize])
  return width
}
export const useColumnCount = (columnsCountBreakPoints: {
  [props: number]: number
}) => {
  const windowWidth = useWindowWidth()
  const columnCount = useMemo(() => {
    const breakPoints = (
      Object.keys(columnsCountBreakPoints as any) as unknown as number[]
    ).sort((a: number, b: number) => a - b)
    let count =
      breakPoints.length > 0
        ? columnsCountBreakPoints![breakPoints[0]]
        : DEFAULT_COLUMNS_COUNT
    breakPoints.forEach((breakPoint) => {
      if (breakPoint < windowWidth) {
        count = columnsCountBreakPoints![breakPoint]
      }
    })
    return count
  }, [windowWidth, columnsCountBreakPoints])
  return columnCount
}

动态界说 style columnCount,完成依据屏幕宽度自习惯列数:

const { columnsCountBreakPoints } = props
const columnCount = useColumnCount(columnsCountBreakPoints)
return (
  <div className={classNames(['masonry-column-wrap'])} style={{ columnCount }}>
    {children}
  </div>
)

3. 直接运用 npm 包

npm – react-masonry-component2 的运用办法:

import { Masonry } from 'react-masonry-component2'
export const MyComponent = (args) => {
  return (
    <Masonry
      direction='column'
      columnsCountBreakPoints={{
        1400: 5,
        1000: 4,
        700: 3,
      }}
    >
      <div></div>
      <div></div>
      <div></div>
    </Masonry>
  )
}

在线预览:632339a3ed0b247d36b0fa3c-njrsmzdcdj.chromatic.com/?path=/stor…

五、横向

横向瀑布流指的是,每列依照横向摆放,如下图所示。

5 种瀑布流场景的完成原理解析

完成横向瀑布流的办法是CSS 弹性布局

1. 弹性布局介绍

弹性布局,是一种当页面需求习惯不同的屏幕大小以及设备类型时确保元素具有恰当的行为的布局方法。

引进弹性盒布局模型的意图是提供一种更加有效的方法来对一个容器中的子元素进行摆放、对齐和分配空白空间。

CSS3 的弹性布局特点:

  • flex-dicreation:指定了弹性质元素的摆放方法;
  • justify-content:指定了弹性布局的主轴对齐方法;
  • align-items:指定了弹性布局的侧轴对齐方法;
  • flex-wrap:指定了弹性质元素的换行方法;
  • align-content:指定弹性布局各行的对齐方法;
  • order:指定弹性质元素的摆放顺序;
  • align-self:指定弹性质元素的纵向对齐方法;
  • flex 特点用于指定弹性质元素如何分配空间;
    • auto: 核算值为 1 1 auto
    • initial: 核算值为 0 1 auto
    • none:核算值为 0 0 auto
    • inherit:从父元素继承
    • [ flex-grow ]:界说弹性盒子元素的扩展比率。
    • [ flex-shrink ]:界说弹性盒子元素的缩短比率。
    • [ flex-basis ]:界说弹性盒子元素的默认基准值。

2. 完成思路

瀑布流完成思路如下:

  • CSS 弹性布局对 4 列按横向摆放,对每一列内部按纵向摆放。

3. 完成代码

瀑布流完成代码如下:

<div className={classNames(['masonry-flex-wrap'])}>
  <div className='masonry-flex-wrap-column'>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
  </div>
  <div className='masonry-flex-wrap-column'>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
  </div>
</div>
.masonry-flex-wrap {
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-content: stretch;
  &-column {
    display: 'flex';
    flex-direction: 'column';
    justify-content: 'flex-start';
    align-content: 'stretch';
    flex: 1;
  }
}

4. 直接运用 npm 包

npm – react-masonry-component2 的运用办法:

import { Masonry } from 'react-masonry-component2'
export const MyComponent = (args) => {
  return (
    <Masonry
      columnsCountBreakPoints={{
        1400: 5,
        1000: 4,
        700: 3,
      }}
    >
      <div></div>
      <div></div>
      <div></div>
    </Masonry>
  )
}

在线预览:632339a3ed0b247d36b0fa3c-njrsmzdcdj.chromatic.com/?path=/stor…

六、横向+高度排序

横向+高度排序指的是,每列依照横向摆放,往高度最小的列增加内容,如下图所示。

5 种瀑布流场景的完成原理解析

高度排序就需求用 JS 逻辑来做了。

1. 完成思路

  • JS 将瀑布流的列表按高度均为分为指定列数,比如瀑布流为 4 列,那么就要把瀑布流列表分红 4 个列表

2. 完成代码

export const getColumnsSortWithHeight = (
  children: React.ReactNode,
  columnCount: number
) => {
  const columns: {
    height: number
    children: React.ReactNode[]
  }[] = Array.from({ length: columnCount }, () => ({
    height: 0,
    children: [],
  }))
  React.Children.forEach(children, (child: React.ReactNode, index) => {
    if (child && React.isValidElement(child)) {
      if (index < columns.length) {
        columns[index % columnCount].children.push(child)
        columns[index % columnCount].height += child.props.height
        return
      }
      const minHeightColumn = minBy(columns, (a) => a.height) as {
        height: number
        children: React.ReactNode[]
      }
      minHeightColumn.children.push(child)
      minHeightColumn.height += child.props.height
    }
  })
  return columns
}

3. 直接运用 npm 包

npm – react-masonry-component2 的运用办法:

import { Masonry, MasonryItem } from 'react-masonry-component2'
export const MyComponent = (args) => {
  return (
    <Masonry
      sortWithHeight
      columnsCountBreakPoints={{
        1400: 5,
        1000: 4,
        700: 3,
      }}
    >
      <MasonryItem height={200}>
        <div></div>
      </MasonryItem>
      <MasonryItem height={300}>
        <div></div>
      </MasonryItem>
      <MasonryItem height={400}>
        <div></div>
      </MasonryItem>
    </Masonry>
  )
}

在线预览:632339a3ed0b247d36b0fa3c-njrsmzdcdj.chromatic.com/?path=/stor…

七、横向+高度排序+依据宽度自习惯列数

依据宽度自习惯列数的做法和纵向场景共同,都是监听 resize 办法,依据屏幕宽度得到该宽度下应该展现的列数,这里不做赘述。

5 种瀑布流场景的完成原理解析

1. 直接运用 npm 包

npm – react-masonry-component2 的运用办法:

import { Masonry } from 'react-masonry-component2'
export const MyComponent = (args) => {
  return (
    <Masonry
      sortWithHeight
      direction='column'
      columnsCountBreakPoints={{
        1400: 5,
        1000: 4,
        700: 3,
      }}
    >
      <div></div>
      <div></div>
      <div></div>
    </Masonry>
  )
}

在线预览:632339a3ed0b247d36b0fa3c-njrsmzdcdj.chromatic.com/?path=/stor…

小结

本文介绍了 5 种瀑布流场景的完成:

  • 纵向+高度排序
  • 纵向+高度排序+依据宽度自习惯列数
  • 横向
  • 横向+高度排序
  • 横向+高度排序+依据宽度自习惯列数

感兴趣的同学能够到项目源码检查完好完成代码。

也能够下载 www.npmjs.com/package/rea… 直接运用。

更多思考

当瀑布流数据特别多时,dom 节点过多,会影响到页面功用,那么就需求为瀑布流增加翻滚预加载和节点收回功用来进行优化了,在下个版本中将更新翻滚预加载和节点收回功用的完成原理。