Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The performance of the grid layout is adversely affected when dealing with large SVG grid elements (size ≥ 1MB). #2008

Open
tommyspot opened this issue Jan 12, 2024 · 4 comments

Comments

@tommyspot
Copy link

tommyspot commented Jan 12, 2024

Describe the bug

When the size of an SVG grid element is approximately 1MB or more, dragging the element in the grid layout results in noticeable lag.
Besides that, the mouse cursor is offset from the dragging element (I guess the issue is the same #1975

Your Example Website or App

https://codesandbox.io/p/sandbox/cold-rain-q3829h

import { FunctionComponent } from "react";
import _ from "lodash";
import ReactGridLayout from "react-grid-layout";
import "react-grid-layout/css/styles.css";
import "react-resizable/css/styles.css";
import "./styles.css";
import { ReactComponent as SVG_Picture_1MB } from "./picture_1MB.svg";

interface Props {}

const DropDrag: FunctionComponent<Props> = (props) => {
  const layout = [
    {
      i: "chart1",
      x: 0,
      y: 6,
      w: 8,
      h: 8,
      minW: 4,
      minH: 2,
    },
    {
      i: "chart2",
      x: 8,
      y: 6,
      w: 4,
      h: 8,
      minW: 4,
      minH: 2,
    },
    {
      i: "card2",
      x: 4,
      y: 0,
      w: 4,
      h: 3,
      minW: 2,
    },
    {
      i: "card3",
      x: 8,
      y: 0,
      w: 4,
      h: 3,
      minW: 2,
    },
    {
      i: "card4",
      x: 0,
      y: 3,
      w: 4,
      h: 3,
      minW: 2,
    },
    {
      i: "card6",
      x: 8,
      y: 3,
      w: 4,
      h: 3,
      minW: 2,
    },
    {
      i: "table",
      x: 0,
      y: 14,
      w: 12,
      h: 9,
      minW: 4,
      minH: 3,
    },
    {
      i: "card1",
      x: 0,
      y: 0,
      w: 4,
      h: 3,
      minW: 2,
    },
    {
      i: "card5",
      x: 4,
      y: 3,
      w: 4,
      h: 3,
      minW: 2,
    },
  ];

  const onDrop = (layout: any, layoutItem: any, _ev: any) => {
    alert(
      `Element parameters:\n${JSON.stringify(
        layoutItem,
        ["x", "y", "w", "h"],
        2
      )}`
    );
  };

  const generateDOM = () => {
    return _.map(layout, function ({ i }) {
      return (
        <div
          key={i}
          style={{ background: "#ccc", height: "100%", width: "100%" }}
        >
          <SVG_Picture_1MB width="100%" height="100%" />
        </div>
      );
    });
  };

  return (
    <>
      <div
        className="droppable-element"
        draggable
        unselectable="on"
        onDragStart={(e) => e.dataTransfer.setData("text/plain", "")}
      >
        Droppable Element (Drag me!)
      </div>

      <ReactGridLayout
        width={1381.25}
        rowHeight={49.22}
        cols={12}
        layout={layout}
        isDroppable
        onDrop={onDrop}
        style={{ background: "#f0f0f0", width: 1381.25 }}
      >
        {generateDOM()}
      </ReactGridLayout>
    </>
  );
};

export default DropDrag;

Steps to Reproduce the Bug or Issue

  1. Config a heavy SVG (~1MB) for all grid elements.
  2. Drag any elements around.
  3. Result:
    • Dragging is quite lag.
    • Mouse cursor is offset from the dragging element.

Expected behavior

  • Drag performance should be better.
  • The mouse cursor should accurately adjust the position of the dragging element.

react-grid-layout library version

1.4.4

Operating System Version

MacOS

Browser

Chrome

Additional context

In the create-react-app environment, there is more lag compared to CodeSandbox.

Screenshots or Videos

react-grid-layout.bad.performance.mov
@hungnmNWS
Copy link

meet same problem with heavy data :(

@dmeehan1968
Copy link

This still seems to be a problem with resize. I have a dashboard with chart widgets, and the resize experiences lag once there is a more complex chart (large SVG of 1.4mb). The drag performance is fine.

I have memoized the grid items, and have verified that the only chart that is being rerendered is the one being resized (as I'd like the chart to be redrawn whilst resize is in progress so that I can judge whether the size is good).

In the video I start by refreshing the page. Each chart is drawn without data whilst the fetch is in progress. The data fetch is currently sequential, so load of the next chart isn't started until the previous is complete. The order of the charts is the same as the on screen representation (top left to bottom right order). You can see that the first chart resize is not laggy once the second chart is rendered, but as soon as the third chart (1.4mb SVG) is loaded, the resize experiences lag.

I can't really see how this can be optimised further, other than to perhaps swap out the charts for a placeholder once resize starts to remove the SVG from the DOM. i.e. this may be down to DOM performance rather than RGL.

I use @observablehq/plot for charting, and this does not support redraws on new data - it throws away the chart a render afresh, and therefore the SVG is also being regenerated based on detected container size. This probably causes large amounts of DOM element removal/insertion, and the larger chart is probably adding weight.

What might be acceptable is that the resize is currently occuring on all sizes, rather than on the grid size, and this could be optimised to only cause the redraw at the closest matching grid size. I suspect I could intercept this during the resize but I'd need to communicate the grid size down the tree, and wonder if this could be an enhancement to RGL to only cause the child dimensions to change based on grid size (whilst still showing an outline box indicating the actual current size).

e.g. RGL shows a red 'placeholder' to indicate where the grid item will be shown, but the actual item is resized according to the position of the resize handle. Perhaps another placeholder can be used that shows the resize progress, but leaves the actual item at the nearest grid size, so the item 'jumps' to grid dimensions rather than following the resize handle.

Screen.Recording.2024-04-21.at.11.13.34.720.mov

@dmeehan1968
Copy link

I did experimented with detaching the SVG elements from the DOM during resize (except for the element being resized) and this did seem to resolve the lag on the element being resized. This does seem to confirm that the problem is the overall size of the DOM, and therefore resize/drag performance may be contextual depending on the overall size of the page on which element appear.

In the screen cap, I provide an onResizeStart handler in order to note which grid item is being resized (newItem.i), and pass an attribute to the grid element to indicate whether the chart element (containing SVG) should be attached to the DOM or not. I also set a placeholder element that is toggled between display: none and display: block depending on whether the chart is currently attached or not. My placeholder uses DaisyUI's 'skeleton' component which includes a bit of animation. When onResizeStop is received, I put the chart elements back into the DOM.

Screen.Recording.2024-04-23.at.10.50.39.480.mov

@dmeehan1968
Copy link

I also experienced this same lag problem when dragging grid items. This definitely seems to be down to the complexity (size) of the DOM overall, nor does it seem to matter greatly if you are resizing/dragging a node that has a large sub-tree, or whether one exists elsewhere in the tree (a sibling). My workaround is to omit the grid item content (at some depth that makes sense) which the drag/resize is in progress (tracked with onDrag[Start/End] and onResize[Start/End]. This isn't ideal as sometimes its useful to have some context for the space the item content needs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants