From db87eae19fc2d827b1aac00261b4f1191726de14 Mon Sep 17 00:00:00 2001 From: Maciej Hirsz <1096222+maciejhirsz@users.noreply.github.com> Date: Sun, 14 Oct 2018 11:16:23 +0200 Subject: [PATCH] Only render visible rows in the List (#87) --- .../frontend/src/components/List/List.tsx | 82 ++++++++++++++++++- 1 file changed, 79 insertions(+), 3 deletions(-) diff --git a/packages/frontend/src/components/List/List.tsx b/packages/frontend/src/components/List/List.tsx index 5f89ccf..2a1f352 100644 --- a/packages/frontend/src/components/List/List.tsx +++ b/packages/frontend/src/components/List/List.tsx @@ -3,10 +3,12 @@ import { Types, Maybe } from '@dotstats/common'; import { State as AppState, Node } from '../../state'; import { Row } from './'; import { PersistentSet } from '../../persist'; +import { viewport } from '../../utils' -// const HEADER = 148; +const HEADER = 148; const TH_HEIGHT = 35; const TR_HEIGHT = 31; +const ROW_MARGIN = 5; import './List.css'; @@ -16,9 +18,36 @@ export namespace List { appState: Readonly; pins: PersistentSet; } + + export interface State { + viewportHeight: number; + listStart: number; + listEnd: number; + } } export class List extends React.Component { + public state = { + viewportHeight: viewport().height, + listStart: 0, + listEnd: 0, + }; + + private relativeTop = -1; + private scrolling = false; + + public componentDidMount() { + this.onScroll(); + + window.addEventListener('resize', this.onResize); + window.addEventListener('scroll', this.onScroll); + } + + public componentWillUnmount() { + window.removeEventListener('resize', this.onResize); + window.removeEventListener('scroll', this.onScroll); + } + public render() { const { settings } = this.props.appState; const { pins, filter } = this.props; @@ -36,13 +65,18 @@ export class List extends React.Component { } } - const height = TH_HEIGHT + nodes.length * TR_HEIGHT; + const { listStart, listEnd } = this.state; + + const height = (TH_HEIGHT + nodes.length * TR_HEIGHT); + const transform = `translateY(${listStart * TR_HEIGHT}px)`; + + nodes = nodes.slice(listStart, listEnd); return (
- + { nodes.map((node) => ) } @@ -51,4 +85,46 @@ export class List extends React.Component { ); } + + private onScroll = () => { + if (this.scrolling) { + return; + } + + const relativeTop = divisibleBy(window.scrollY - (HEADER + TR_HEIGHT), TR_HEIGHT * ROW_MARGIN); + + if (this.relativeTop === relativeTop) { + return; + } + + this.relativeTop = relativeTop; + this.scrolling = true; + + window.requestAnimationFrame(this.onScrollRAF); + } + + private onScrollRAF = () => { + const { relativeTop } = this; + const { viewportHeight } = this.state; + const top = Math.max(relativeTop, 0); + const height = relativeTop < 0 ? viewportHeight + relativeTop : viewportHeight; + const listStart = Math.max((top / TR_HEIGHT | 0) - ROW_MARGIN, 0); + const listEnd = listStart + ROW_MARGIN * 2 + Math.ceil(height / TR_HEIGHT); + + if (listStart !== this.state.listStart || listEnd !== this.state.listEnd) { + this.setState({ listStart, listEnd }); + } + + this.scrolling = false; + } + + private onResize = () => { + const viewportHeight = viewport().height; + + this.setState({ viewportHeight }); + } +} + +function divisibleBy(n: number, dividor: number): number { + return n - n % dividor; }