5 种瀑布流场景的实现原理解析

发表于 1年以前  | 总阅读数:245 次

一、背景

本文介绍 5 种瀑布流场景的实现,大家可以根据自身的需求场景进行选择

5 种场景分别是:

瀑布流 特点
纵向+高度排序 纯 CSS 多列实现,是最简单的瀑布流写法
纵向+高度排序+根据宽度自适应列数 通过 JS 根据屏幕宽度计算列数,在 web 端更加灵活的展示瀑布流
横向 纯 CSS 弹性布局实现,是最简单的横向瀑布流写法
横向+高度排序 横向+高度排序的瀑布流,需要通过 JS 计算每一列高度,损耗性能,但是可以避免某列特别长的情况,体验更好
横向+高度排序+根据宽度自适应列数 需要通过 JS 计算每一列高度,并根据屏幕宽度计算列数,损耗性能,但是可以避免某列特别长的情况,并且可以在 web 端更加灵活的展示瀑布流,体验更好,是 5 种瀑布流中用户体验最好的

我已经将这 5 种场景的实现封装成 npm 包,npm 包地址:https://www.npmjs.com/package/react-masonry-component2,可以直接在 React 项目中安装使用。

二、介绍

瀑布流,是比较流行的一种网站页面布局[1],视觉表现为参差不齐的多栏布局,随着页面滚动条[2]向下滚动,这种布局还会不断加载数据块[3]并附加至当前尾部。

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

三、纵向+高度排序

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

实现纵向+高度排序瀑布流的方法是 CSS 多列布局

1. 多列布局介绍

多列布局[4]指的是 CSS3 可以将文本内容设计成像报纸一样的多列布局,如下实例:

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[5] 的使用方法:

import { Masonry } from 'react-masonry-component2'

export const MyComponent = (args) => {
  return (
    <Masonry direction='column'>
      <div></div>
      <div></div>
      <div></div>
    </Masonry>
  )
}

在线预览[6]

四、纵向+高度排序+根据宽度自适应列数

在纵向+高度排序的基础上,按照宽度自适应列数。

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[7] 的使用方法:

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>
  )
}

在线预览[8]

五、横向

横向瀑布流指的是,每列按照横向排列,如下图所示。

实现横向瀑布流的方法是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[9] 的使用方法:

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>
  )
}

在线预览[10]

六、横向+高度排序

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

高度排序就需要用 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[11] 的使用方法:

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>
  )
}

在线预览[12]

七、横向+高度排序+根据宽度自适应列数

根据宽度自适应列数的做法和纵向场景一致,都是监听 resize 方法,根据屏幕宽度得到该宽度下应该展示的列数,这里不做赘述。

1. 直接使用 npm 包

npm - react-masonry-component2[13] 的使用方法:

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>
  )
}

在线预览[14]

小结

本文介绍了 5 种瀑布流场景的实现:

  • 纵向+高度排序
  • 纵向+高度排序+根据宽度自适应列数
  • 横向
  • 横向+高度排序
  • 横向+高度排序+根据宽度自适应列数

感兴趣的同学可以到项目源码[15]查看完整实现代码。

也可以下载[16]直接使用。

更多思考

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

相关链接

[1]页面布局: https://baike.baidu.com/item/%E9%A1%B5%E9%9D%A2%E5%B8%83%E5%B1%80?fromModule=lemma_inlink

[2]滚动条: https://baike.baidu.com/item/%E6%BB%9A%E5%8A%A8%E6%9D%A1/7166861?fromModule=lemma_inlink

[3]数据块: https://baike.baidu.com/item/%E6%95%B0%E6%8D%AE%E5%9D%97/107672?fromModule=lemma_inlink

[4]多列布局: https://www.runoob.com/css3/css3-multiple-columns.html

[5]npm - react-masonry-component2: https://www.npmjs.com/package/react-masonry-component2

[6]在线预览: https://632339a3ed0b247d36b0fa3c-njrsmzdcdj.chromatic.com/?path=/story/%E5%B8%83%E5%B1%80-masonry-%E7%80%91%E5%B8%83%E6%B5%81--%E7%BA%B5%E5%90%91%E5%B8%83%E5%B1%80

[7]npm - react-masonry-component2: https://www.npmjs.com/package/react-masonry-component2

[8]在线预览: https://632339a3ed0b247d36b0fa3c-njrsmzdcdj.chromatic.com/?path=/story/%E5%B8%83%E5%B1%80-masonry-%E7%80%91%E5%B8%83%E6%B5%81--%E7%BA%B5%E5%90%91%E5%B8%83%E5%B1%80

[9]npm - react-masonry-component2: https://www.npmjs.com/package/react-masonry-component2

[10]在线预览: https://632339a3ed0b247d36b0fa3c-njrsmzdcdj.chromatic.com/?path=/story/%E5%B8%83%E5%B1%80-masonry-%E7%80%91%E5%B8%83%E6%B5%81--%E6%A8%AA%E5%90%91%E5%B8%83%E5%B1%80

[11]npm - react-masonry-component2: https://www.npmjs.com/package/react-masonry-component2

[12]在线预览: https://632339a3ed0b247d36b0fa3c-njrsmzdcdj.chromatic.com/?path=/story/%E5%B8%83%E5%B1%80-masonry-%E7%80%91%E5%B8%83%E6%B5%81--%E6%A8%AA%E5%90%91%E5%B8%83%E5%B1%80%E9%AB%98%E5%BA%A6%E6%8E%92%E5%BA%8F

[13]npm - react-masonry-component2: https://www.npmjs.com/package/react-masonry-component2

[14]在线预览: https://632339a3ed0b247d36b0fa3c-njrsmzdcdj.chromatic.com/?path=/story/%E5%B8%83%E5%B1%80-masonry-%E7%80%91%E5%B8%83%E6%B5%81--%E6%A8%AA%E5%90%91%E5%B8%83%E5%B1%80%E9%AB%98%E5%BA%A6%E6%8E%92%E5%BA%8F

[15]项目源码: https://github.com/jiaozitang/react-masonry-component2

[16]下载: https://www.npmjs.com/package/react-masonry-component2

本文由哈喽比特于1年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/sm6cG98pqnYPniVXqL01TQ

 相关推荐

刘强东夫妇:“移民美国”传言被驳斥

京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。

发布于:8月以前  |  808次阅读  |  详细内容 »

博主曝三大运营商,将集体采购百万台华为Mate60系列

日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。

发布于:8月以前  |  770次阅读  |  详细内容 »

ASML CEO警告:出口管制不是可行做法,不要“逼迫中国大陆创新”

据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。

发布于:8月以前  |  756次阅读  |  详细内容 »

抖音中长视频App青桃更名抖音精选,字节再发力对抗B站

今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。

发布于:8月以前  |  648次阅读  |  详细内容 »

威马CDO:中国每百户家庭仅17户有车

日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。

发布于:8月以前  |  589次阅读  |  详细内容 »

研究发现维生素 C 等抗氧化剂会刺激癌症生长和转移

近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。

发布于:8月以前  |  449次阅读  |  详细内容 »

苹果据称正引入3D打印技术,用以生产智能手表的钢质底盘

据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。

发布于:8月以前  |  446次阅读  |  详细内容 »

千万级抖音网红秀才账号被封禁

9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...

发布于:8月以前  |  445次阅读  |  详细内容 »

亚马逊股东起诉公司和贝索斯,称其在购买卫星发射服务时忽视了 SpaceX

9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。

发布于:8月以前  |  444次阅读  |  详细内容 »

苹果上线AppsbyApple网站,以推广自家应用程序

据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。

发布于:8月以前  |  442次阅读  |  详细内容 »

特斯拉美国降价引发投资者不满:“这是短期麻醉剂”

特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。

发布于:8月以前  |  441次阅读  |  详细内容 »

光刻机巨头阿斯麦:拿到许可,继续对华出口

据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。

发布于:8月以前  |  437次阅读  |  详细内容 »

马斯克与库克首次隔空合作:为苹果提供卫星服务

近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。

发布于:8月以前  |  430次阅读  |  详细内容 »

𝕏(推特)调整隐私政策,可拿用户发布的信息训练 AI 模型

据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。

发布于:8月以前  |  428次阅读  |  详细内容 »

荣耀CEO谈华为手机回归:替老同事们高兴,对行业也是好事

9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。

发布于:8月以前  |  423次阅读  |  详细内容 »

AI操控无人机能力超越人类冠军

《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。

发布于:8月以前  |  423次阅读  |  详细内容 »

AI生成的蘑菇科普书存在可致命错误

近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。

发布于:8月以前  |  420次阅读  |  详细内容 »

社交媒体平台𝕏计划收集用户生物识别数据与工作教育经历

社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”

发布于:8月以前  |  411次阅读  |  详细内容 »

国产扫地机器人热销欧洲,国产割草机器人抢占欧洲草坪

2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。

发布于:8月以前  |  406次阅读  |  详细内容 »

罗永浩吐槽iPhone15和14不会有区别,除了序列号变了

罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。

发布于:8月以前  |  398次阅读  |  详细内容 »
 相关文章
Android插件化方案 5年以前  |  236901次阅读
vscode超好用的代码书签插件Bookmarks 1年以前  |  7054次阅读
 目录