Improve BackdropPanel performance by ignoring React

This commit is contained in:
Dariusz Niemczyk 2021-08-13 18:12:02 +02:00
parent 5f9b55eaa9
commit 7f58a21aac
No known key found for this signature in database
GPG key ID: 28DFE7164F497CB6
2 changed files with 47 additions and 33 deletions

View file

@ -23,26 +23,22 @@ interface IProps {
backgroundImage?: CanvasImageSource; backgroundImage?: CanvasImageSource;
} }
interface IState { export default class BackdropPanel extends React.PureComponent<IProps> {
spacePanelWidth: number;
roomListWidth: number;
viewportHeight: number;
}
export default class BackdropPanel extends React.PureComponent<IProps, IState> {
private spacesCanvasRef = createRef<HTMLCanvasElement>(); private spacesCanvasRef = createRef<HTMLCanvasElement>();
private roomListCanvasRef = createRef<HTMLCanvasElement>(); private roomListCanvasRef = createRef<HTMLCanvasElement>();
private spacesCtx: CanvasRenderingContext2D; private spacesCtx: CanvasRenderingContext2D;
private roomListCtx: CanvasRenderingContext2D; private roomListCtx: CanvasRenderingContext2D;
private sizes = {
spacePanelWidth: 0,
roomListWidth: 0,
height: 0,
};
private currentFrame?: number;
constructor(props: IProps) { constructor(props: IProps) {
super(props); super(props);
this.state = {
spacePanelWidth: 0,
roomListWidth: 0,
viewportHeight: UIStore.instance.windowHeight,
};
} }
public componentDidMount() { public componentDidMount() {
@ -52,30 +48,41 @@ export default class BackdropPanel extends React.PureComponent<IProps, IState> {
UIStore.instance.on("LeftPanel", this.onResize); UIStore.instance.on("LeftPanel", this.onResize);
} }
public componentDidUpdate() {
if (this.props.backgroundImage) {
this.refreshBackdropImage();
}
}
public componentWillUnmount() { public componentWillUnmount() {
UIStore.instance.off("SpacePanel", this.onResize); UIStore.instance.off("SpacePanel", this.onResize);
UIStore.instance.off("LeftPanel", this.onResize); UIStore.instance.off("LeftPanel", this.onResize);
} }
public componentDidUpdate() {
if (this.props.backgroundImage) {
requestAnimationFrame(this.refreshBackdropImage);
}
}
private onResize = () => { private onResize = () => {
const spacePanelDimensions = UIStore.instance.getElementDimensions("SpacePanel"); const spacePanelDimensions = UIStore.instance.getElementDimensions("SpacePanel");
const roomListDimensions = UIStore.instance.getElementDimensions("LeftPanel"); const roomListDimensions = UIStore.instance.getElementDimensions("LeftPanel");
this.setState({ this.sizes = {
spacePanelWidth: spacePanelDimensions ? spacePanelDimensions.width : 0, spacePanelWidth: spacePanelDimensions?.width ?? 0,
roomListWidth: roomListDimensions ? roomListDimensions.width : 0, roomListWidth: roomListDimensions?.width ?? 0,
viewportHeight: UIStore.instance.windowHeight, height: UIStore.instance.windowHeight,
}); };
if (this.props.backgroundImage) {
this.refreshBackdropImage();
}
};
private animate = () => {
if (this.currentFrame !== undefined) {
cancelAnimationFrame(this.currentFrame);
this.currentFrame = undefined;
}
this.currentFrame = requestAnimationFrame(this.refreshBackdropImage);
}; };
private refreshBackdropImage = (): void => { private refreshBackdropImage = (): void => {
const width = this.state.spacePanelWidth + this.state.roomListWidth; const { spacePanelWidth, roomListWidth, height } = this.sizes;
const height = this.state.viewportHeight; const width = spacePanelWidth + roomListWidth;
const { backgroundImage } = this.props; const { backgroundImage } = this.props;
const imageWidth = (backgroundImage as ImageBitmap).width const imageWidth = (backgroundImage as ImageBitmap).width
@ -98,10 +105,11 @@ export default class BackdropPanel extends React.PureComponent<IProps, IState> {
const x = (width - resultWidth) / 2; const x = (width - resultWidth) / 2;
const y = (height - resultHeight) / 2; const y = (height - resultHeight) / 2;
this.spacesCanvasRef.current.width = this.state.spacePanelWidth; this.spacesCanvasRef.current.width = spacePanelWidth;
this.spacesCanvasRef.current.height = this.state.viewportHeight; this.spacesCanvasRef.current.height = height;
this.roomListCanvasRef.current.width = this.state.roomListWidth; this.roomListCanvasRef.current.width = roomListWidth;
this.roomListCanvasRef.current.height = this.state.viewportHeight; this.roomListCanvasRef.current.height = height;
this.roomListCanvasRef.current.style.transform = `translateX(${spacePanelWidth}px)`;
this.spacesCtx.filter = `blur(30px)`; this.spacesCtx.filter = `blur(30px)`;
this.roomListCtx.filter = `blur(60px)`; this.roomListCtx.filter = `blur(60px)`;
@ -116,7 +124,7 @@ export default class BackdropPanel extends React.PureComponent<IProps, IState> {
backgroundImage, backgroundImage,
0, 0, 0, 0,
imageWidth, imageHeight, imageWidth, imageHeight,
x - this.state.spacePanelWidth, x - spacePanelWidth,
y, y,
resultWidth, resultWidth,
resultHeight, resultHeight,
@ -134,7 +142,7 @@ export default class BackdropPanel extends React.PureComponent<IProps, IState> {
/> />
<canvas <canvas
style={{ style={{
transform: `translateX(${this.state.spacePanelWidth}px)`, transform: `translateX(0)`,
opacity: .1, opacity: .1,
}} }}
ref={this.roomListCanvasRef} ref={this.roomListCanvasRef}

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React, { ComponentProps, Dispatch, ReactNode, SetStateAction, useEffect, useState } from "react"; import React, { ComponentProps, Dispatch, ReactNode, SetStateAction, useEffect, useLayoutEffect, useRef, useState } from "react";
import { DragDropContext, Draggable, Droppable } from "react-beautiful-dnd"; import { DragDropContext, Draggable, Droppable } from "react-beautiful-dnd";
import classNames from "classnames"; import classNames from "classnames";
import { Room } from "matrix-js-sdk/src/models/room"; import { Room } from "matrix-js-sdk/src/models/room";
@ -42,8 +42,8 @@ import IconizedContextMenu, {
IconizedContextMenuOptionList, IconizedContextMenuOptionList,
} from "../context_menus/IconizedContextMenu"; } from "../context_menus/IconizedContextMenu";
import SettingsStore from "../../../settings/SettingsStore"; import SettingsStore from "../../../settings/SettingsStore";
import UIStore from "../../../stores/UIStore";
import { SettingLevel } from "../../../settings/SettingLevel"; import { SettingLevel } from "../../../settings/SettingLevel";
import UIStore from "../../../stores/UIStore";
const useSpaces = (): [Room[], Room[], Room | null] => { const useSpaces = (): [Room[], Room[], Room | null] => {
const invites = useEventEmitterState<Room[]>(SpaceStore.instance, UPDATE_INVITED_SPACES, () => { const invites = useEventEmitterState<Room[]>(SpaceStore.instance, UPDATE_INVITED_SPACES, () => {
@ -207,6 +207,11 @@ const InnerSpacePanel = React.memo<IInnerSpacePanelProps>(({ children, isPanelCo
const SpacePanel = () => { const SpacePanel = () => {
const [isPanelCollapsed, setPanelCollapsed] = useState(true); const [isPanelCollapsed, setPanelCollapsed] = useState(true);
const ref = useRef<HTMLUListElement>();
useLayoutEffect(() => {
UIStore.instance.trackElementDimensions("SpacePanel", ref.current);
return () => UIStore.instance.stopTrackingElementDimensions("SpacePanel");
}, []);
const onKeyDown = (ev: React.KeyboardEvent) => { const onKeyDown = (ev: React.KeyboardEvent) => {
let handled = true; let handled = true;
@ -281,6 +286,7 @@ const SpacePanel = () => {
onKeyDown={onKeyDownHandler} onKeyDown={onKeyDownHandler}
role="tree" role="tree"
aria-label={_t("Spaces")} aria-label={_t("Spaces")}
ref={ref}
> >
<Droppable droppableId="top-level-spaces"> <Droppable droppableId="top-level-spaces">
{ (provided, snapshot) => ( { (provided, snapshot) => (