支持5中滚动类型

  • Table 基于Grid来实现,表格具有固定的头部,并且可以在垂直方向上滚动
  • Grid 构建任意网状的结构,传入一个二维的数组,渲染出类似棋盘的结构
  • List 列表,一个维的列表,横向滚动,X轴 & 竖向滚动,Y轴
  • Masonry 卡片效果
    • 可以在水平方向,也可以在垂直方向滚动
    • 不同于Grid的是可以自定义每个元素的大小,或者子元素的大小也可以是动态变化的
  • Collection 瀑布流
    • 类似于瀑布流的形式,同样可以水平和垂直方向滚动

https://github.com/bvaughn/react-virtualized
虚拟滚动demo https://codesandbox.io/examples/package/react-virtualized
https://css-tricks.com/rendering-lists-using-react-virtualized/
https://segmentfault.com/a/1190000017233625

  1. npm install react-virtualized
  • 缺点:不支持子元素的动态高度或者宽度,只支持固定的宽高

Table

Table案例 https://bvaughn.github.io/react-virtualized/#/components/Table
Table文档 https://github.com/bvaughn/react-virtualized/blob/master/docs/Table.md
image.png

  1. import React from 'react';
  2. import { Column, Table } from 'react-virtualized';
  3. import 'react-virtualized/styles.css'; // only needs to be imported once
  4. // Table data as an array of objects
  5. const list = [
  6. { name: 'Brian Vaughn', description: 'Software engineer' }
  7. ];
  8. function VirtualTable() {
  9. return (
  10. <Table
  11. width={300}
  12. height={300}
  13. headerHeight={20}
  14. rowHeight={30}
  15. rowCount={list.length}
  16. rowGetter={({ index }) => list[index]}
  17. >
  18. <Column
  19. label='Name'
  20. dataKey='name'
  21. width={100}
  22. />
  23. <Column
  24. width={200}
  25. label='Description'
  26. dataKey='description'
  27. />
  28. </Table>
  29. );
  30. };
  31. export default VirtualTable

Grid

https://bvaughn.github.io/react-virtualized/#/components/Grid
image.png

MultiGrid

多列固定虚拟滚动

  1. import React from 'react';
  2. import PropTypes from 'prop-types';
  3. import moment from 'moment';
  4. import { MultiGrid } from 'react-virtualized';
  5. import styles from "./style.less";
  6. const subChild = () => {
  7. const arr = ['成绩', '科目'];
  8. return arr.map(item => <div key={item} className='flex-1'>{item}</div>);
  9. };
  10. App.propTypes = {
  11. data: PropTypes.array.isrequired,
  12. }
  13. function App({ data }) {
  14. // 固定表头
  15. function getThead(columnIndex) {
  16. const { values } = data[0] || {};
  17. if (columnIndex < 2) {
  18. const arr = ['INFO', '机刷'];
  19. return arr[columnIndex];
  20. };
  21. if (values) {
  22. const thead = values.map((item, i) => {
  23. const [time] = item;
  24. return moment(time).format('HH:mm');
  25. });
  26. return (
  27. <>
  28. <div style={{ borderBottom: '1px solid #ddd' }}>{thead[columnIndex]}</div>
  29. <div className="flex">{subChild()}</div>
  30. </>
  31. );
  32. }
  33. }
  34. // 固定左侧的列
  35. function getColumns(columnIndex, rowIndex) {
  36. const { keys } = data[rowIndex - 1];
  37. if (columnIndex > 1) return;
  38. return keys[columnIndex];
  39. }
  40. // 表格的内容
  41. function getTbody(columnIndex, rowIndex) {
  42. const { values } = data[rowIndex - 1];
  43. const columns = values[columnIndex - 2];
  44. return columns.map((item, i) => {
  45. if (i < 3) return null;
  46. return <div className='flex-1'>{item ? item : '-'}</div>;
  47. });
  48. }
  49. function cellRenderer({ columnIndex, key, rowIndex, style }) {
  50. // 渲染表头
  51. if (rowIndex === 0) {
  52. return (
  53. <div className={styles.Cell} key={key} style={{ ...style, height: 80 }}>
  54. {getThead(columnIndex)}
  55. </div>
  56. );
  57. }
  58. // 固定左侧2列
  59. if (columnIndex < 2) {
  60. return (
  61. <div className={styles.Cell} key={key} style={style}>
  62. {getColumns(columnIndex, rowIndex)}, {rowIndex}
  63. </div>
  64. );
  65. }
  66. return (
  67. <div className={styles.Cell} key={key} style={style}>
  68. <div className="flex">{getTbody(columnIndex, rowIndex)}</div>
  69. </div>
  70. );
  71. }
  72. return (
  73. <MultiGrid
  74. cellRenderer={cellRenderer} // 渲染的表格内容
  75. columnWidth={240} // 每列的宽度
  76. columnCount={182} // 多少列,如果有固定列,需要加上固定列
  77. fixedRowCount={1} // 固定的表头行
  78. fixedColumnCount={2} // 左侧固定的的列
  79. width={1200} // Table的宽度
  80. height={254} // Table的高度 + 滚动条的高度 14px
  81. styleTopLeftGrid={{ height: 80 }}
  82. styleTopRightGrid={{ height: 80 }}
  83. rowHeight={40} // 一行的高度
  84. rowCount={5} // 多少行
  85. hideTopRightGridScrollbar={true}
  86. />
  87. );
  88. }
  89. export default App;

List

https://bvaughn.github.io/react-virtualized/#/components/List
列表滚动demo https://codesandbox.io/s/sckvn
image.png

Collection

https://bvaughn.github.io/react-virtualized/#/components/Collection
image.png

antd-table-virtual

https://github.com/ctq123/ant-virtual-table
https://judes.me/frontend/2019/09/17/infinite-table.html
https://www.daimajiaoliu.com/daima/4723be904900405

  1. import React, { PureComponent, Fragment } from "react";
  2. import { VirtualTable } from "ant-virtual-table";
  3. import { Pagination } from "antd";
  4. import "antd/dist/antd.css";
  5. const columns = [
  6. {
  7. title: "序号",
  8. dataIndex: "id",
  9. // fixed: 'left',
  10. width: 100
  11. },
  12. {
  13. title: "姓名",
  14. dataIndex: "name",
  15. width: 150
  16. },
  17. {
  18. title: "年龄",
  19. dataIndex: "age",
  20. width: 100,
  21. sorter: (a, b) => a.age - b.age,
  22. },
  23. {
  24. title: "性别",
  25. dataIndex: "sex",
  26. width: 100,
  27. render: text => {
  28. return text === "male" ? "男" : "女";
  29. }
  30. }
  31. ];
  32. function generateData(count) {
  33. const res = [];
  34. const names = ["Tom", "Marry", "Jack", "Lorry", "Tanken", "Salla"];
  35. const sexs = ["male", "female"];
  36. for (let i = 0; i < count; i++) {
  37. let obj = {
  38. id: i,
  39. name: names[i % names.length] + i,
  40. sex: sexs[i % sexs.length],
  41. age: 15 + Math.round(10 * Math.random())
  42. };
  43. res.push(obj);
  44. }
  45. return res;
  46. }
  47. const dataSource = generateData(10000);
  48. class App extends PureComponent {
  49. constructor(props) {
  50. super(props);
  51. this.state = {
  52. pageNumber: 1,
  53. objectsPerPage: 10,
  54. list: dataSource
  55. };
  56. }
  57. // 改变页面数字第几页发起的请求
  58. onPageChange(pageNumber) {
  59. this.setState({
  60. pageNumber
  61. });
  62. }
  63. // 改变页面显示条数发起的请求
  64. onShowSizeChange(current, objectsPerPage) {
  65. const list = dataSource.slice(
  66. (current - 1) * objectsPerPage,
  67. objectsPerPage
  68. );
  69. this.setState({
  70. list,
  71. pageNumber: current,
  72. objectsPerPage
  73. });
  74. }
  75. onChange = (selectedRowKeys, selectedRows) => {
  76. console.log(
  77. `onChange selectedRowKeys: ${selectedRowKeys}`,
  78. "selectedRows: ",
  79. selectedRows
  80. );
  81. };
  82. onSelect = (record, selected, selectedRows, nativeEvent) => {
  83. console.log(
  84. `onSelect record: ${record} selected: ${selected}`,
  85. "selectedRows: ",
  86. selectedRows
  87. );
  88. };
  89. onSelectAll = (selected, selectedRows) => {
  90. console.log(
  91. `onSelectAll selected: ${selected}`,
  92. "selectedRows: ",
  93. selectedRows
  94. );
  95. };
  96. render() {
  97. const { list = [] } = this.state;
  98. const rowSelection = {
  99. onChange: this.onChange,
  100. onSelect: this.onSelect,
  101. onSelectAll: this.onSelectAll
  102. };
  103. return (
  104. <Fragment>
  105. <div style={{ height: 30, width: "100%" }} />
  106. <VirtualTable
  107. columns={columns}
  108. dataSource={list}
  109. rowKey="id"
  110. pagination={false}
  111. scroll={{ x: 300, y: 400 }}
  112. rowSelection={rowSelection}
  113. bordered
  114. />
  115. <Pagination
  116. size="small"
  117. total={list.length}
  118. current={this.state.pageNumber}
  119. pageSize={this.state.objectsPerPage}
  120. showSizeChanger
  121. pageSizeOptions={["10", "20", "50", "1000"]}
  122. onShowSizeChange={this.onShowSizeChange.bind(this)}
  123. onChange={this.onPageChange.bind(this)}
  124. showTotal={() => `共 ${list.length} 条`}
  125. />
  126. </Fragment>
  127. );
  128. }
  129. }
  130. export default App;