bvaughn / react-resizable-panels

https://react-resizable-panels.vercel.app/
MIT License
3.58k stars 124 forks source link

Unstable panel widths with more than three panels #323

Closed functoria closed 3 months ago

functoria commented 3 months ago

Hi!

When a panel group has more than three panels, when resizing one of the "center" panels, I've noticed the sizes of the preceding panels are not changing smoothly, but seem to "jump around". I suppose this is caused by a rounding error...

Can this be remedied as the visual experience is not great, especially when the panel content is rich and depends on the panel size? Here's a video showing the issue with a horizontal layout:

https://github.com/bvaughn/react-resizable-panels/assets/24974959/7cb20065-3c94-4f11-988a-d534f7990e3d

Thank you for the great libraries!

bvaughn commented 3 months ago

Can you share a repro of this please? I suspect the reason for the “jumping” is that something in your app is slow to render on resize.

functoria commented 3 months ago

Sure, it's actually a simple modification of your example. Just changed direction, some style and added few more panels. Use zoom to notice the effect.

bvaughn commented 3 months ago

Looks fine for me, even with 6x CPU throttling enabled. 🤷🏼

functoria commented 3 months ago

This is strange, I'm using a MacBook Pro M2, without any throttling, and I can still reproduce the effect in the video below on all my browsers:

https://github.com/bvaughn/react-resizable-panels/assets/24974959/f2573964-26cb-433b-8f02-d0c4c14808a1

(I've updated the sandbox.)

bvaughn commented 3 months ago

Resizing seems smooth for me?

https://github.com/bvaughn/react-resizable-panels/assets/29597/2155fef6-5a8c-4218-b388-549b0e37267a

I think I initially misunderstood what you were reporting though. I assumed the "jumping" was because of slow renders and React not keeping up with the resize updates, but I think you're talking about some small changes in percentage sizes that cause the panels to shift a bit unexpectedly during resize.

I haven't noticed that either, personally.

functoria commented 3 months ago

It's definitely noticeable with the right panel content (just tested on other OS/machine as well).

And yes, I think is related to some non-determinism in how the sizes are computed here, probably due to the "fuzzy" number comparisons. When slowly resizing a panel, on my machine, I've noticed some of the panels, which shouldn't change size at all yet, still have their size "jumping" around the "right" value by couple of pixels.

I'm curious if anybody else is able to reproduce the issue; or maybe the effect is so negligible for most of the use cases that nobody really notices it...

Anyway, thank you for the quick response!

bvaughn commented 3 months ago

Maybe you could record a Replay of this happening?

Does seem like it could be related to the fuzzy number / precision stuff. :(

julianZeitler commented 3 months ago

It's definitely noticeable with the right panel content (just tested on other OS/machine as well).

And yes, I think is related to some non-determinism in how the sizes are computed here, probably due to the "fuzzy" number comparisons. When slowly resizing a panel, on my machine, I've noticed some of the panels, which shouldn't change size at all yet, still have their size "jumping" around the "right" value by couple of pixels.

I'm curious if anybody else is able to reproduce the issue; or maybe the effect is so negligible for most of the use cases that nobody really notices it...

Anyway, thank you for the quick response!

Seems like this is the same problem as in the issue I opened a few days ago. I also put up a sandbox where I was able to duplicate that behavior (You have to play around a bit to notice it, but in some positions of the right bar, it flickers if you resize with the left bar).

functoria commented 3 months ago

Hi, sorry for the late come-back.

Here's the updated sandbox and a recording of the unstable panel width behavior:

https://github.com/bvaughn/react-resizable-panels/assets/24974959/335dc3a0-1804-40a2-ae21-3900e9c28ea3

As you may see, when resizing Panel#1 and Panel#2, the PIXEL widths of Panel#3 and Panel#4 are changing, although they should not (none of the min sizes are reached yet). The percent sizes are indeed stable.

In my usecase I'm "observing" the panel PIXEL widths (with a resize observer) and rendering other (expensive) elements based on those widths. It would definitely help not having pixel widths of unaffected panels changing unnecesarily as in the example above.

I could help by looking at the code, if there is not an obvious immediate solution to fix this.

Thank you!

bvaughn commented 3 months ago

I think this is just a quirk in how flex box layout works. If you observe the CSS styles for each panel, you'll see that the flex-grow style values don't change for the "flickering" panels.

https://github.com/bvaughn/react-resizable-panels/assets/29597/955854fc-8353-407b-9891-0f69944149af

I don't think I can do anything to fix this, short of maybe a major overhaul in the way this library works, and I don't have the capacity for that. (This is just a pro bono side project.)

If you dig into this and have a proposal, please submit a PR. Otherwise I don't think this is something I can tackle.

bvaughn commented 3 months ago

If you update your test app to the following, it makes this a bit easier to see I think:

export default function App() {
  return (
    <div className={styles.PanelGroupWrapper}>
      <PanelGroup className={styles.PanelGroup} direction="horizontal">
        <PanelWithDebugInfo />
        <ResizeHandle className={styles.ResizeHandle} />
        <PanelWithDebugInfo />
        <ResizeHandle className={styles.ResizeHandle} />
        <PanelWithDebugInfo />
        <ResizeHandle className={styles.ResizeHandle} />
        <PanelWithDebugInfo />
      </PanelGroup>
    </div>
  );
}

function PanelWithDebugInfo() {
  const ref = useRef<HTMLDivElement>(null);

  useLayoutEffect(() => {
    const element = ref.current;
    if (element) {
      const update = () => {
        const { width } = element.getBoundingClientRect();
        const size = element.parentElement!.style.flexGrow;

        const percentage = element.querySelector(
          '[data-test-name="percentage"]'
        );
        if (percentage) {
          percentage.textContent = `${size}%`;
        }

        const pixels = element.querySelector('[data-test-name="pixels"]');
        if (pixels) {
          pixels.textContent = `${width}px`;
        }
      };

      update();

      const observer = new ResizeObserver(update);
      observer.observe(element);

      return () => {
        observer.disconnect();
      };
    }
  }, []);

  return (
    <Panel className={styles.PanelColumn}>
      <div className={styles.Centered} ref={ref}>
        <center>
          <div data-test-name="percentage">0%</div>
          <div data-test-name="pixels">0px</div>
        </center>
      </div>
    </Panel>
  );
}

https://github.com/bvaughn/react-resizable-panels/assets/29597/52c49b8e-3f72-43e8-915d-43d92e6d3286

bvaughn commented 3 months ago

I think I may have figured out a way to fix it. Stay tuned.

bvaughn commented 3 months ago

Okay check out react-resizable-panels@2.0.16. I think it should fix this for you!


❤️ → ☕ givebrian.coffee

functoria commented 3 months ago

Thank you for fixing this!