bokuweb / react-rnd

🖱 A resizable and draggable component for React.
https://bokuweb.github.io/react-rnd/stories
MIT License
3.91k stars 325 forks source link

Lag on drag / resize start #598

Open supertote opened 5 years ago

supertote commented 5 years ago

Overview of the problem

I'm using react-rnd version [9.1.1]

My browser is: Chrome 75

I am sure this issue is not a duplicate? Yes, I have checked all open issues and not found anything

Reproduced project

None, the HMI is too complex

Description

We have a fairly complex in which we are using draggable/resizable dialog boxes based on React-rnd. The problem is that whenever we start a drag or resize action, the all browser window freezes for approximately 5 to 7 seconds before the actual moving/resizing occurs. After this initial lag the dragging/resizing is smooth. After investigation in the Chrome debugger, I found out that whenever drag/resize starts, Chome spends a huge amount of time recomputing styles. The root cause seem to be the following code in re-resizable index.tsx (line 279):

  get size(): NumberSize {
    let width = 0;
    let height = 0;
    if (typeof window !== 'undefined' && this.resizable) {
      const orgWidth = this.resizable.offsetWidth;
      const orgHeight = this.resizable.offsetHeight;
      // HACK: Set position `relative` to get parent size.
      //       This is because when re-resizable set `absolute`, I can not get base width correctly.
      const orgPosition = this.resizable.style.position;
      if (orgPosition !== 'relative') {
        this.resizable.style.position = 'relative';
      }
      // INFO: Use original width or height if set auto.
      width = this.resizable.style.width !== 'auto' ? this.resizable.offsetWidth : orgWidth;
      height = this.resizable.style.height !== 'auto' ? this.resizable.offsetHeight : orgHeight;
      // Restore original position
      this.resizable.style.position = orgPosition;
    }
    return { width, height };
}

The problematic line is this.resizable.style.position = 'relative';. That is where Chome starts recalculating styles for more than 5 seconds. To "fix" the problem in our local copy of re-resizable I have replaced this portion of code by calling this.resizable.getBoundingClientRect() in order to get the actual position of the dialog but I am not sure what would be the consequence on different use cases.

Steps to Reproduce

See above

Expected behavior

A smooth drag / resize experience

Actual behavior

A 5 to 7 seconds lag at drag / resize start

eddyw commented 5 years ago

I use getComputedStyle and calculate the width/height regardless of position. Maybe this will help: https://codesandbox.io/s/recursing-paper-b3461 It's an answer I gave to a different question, I just added position: absolute to both parent and children. It gets width/height in getBounds from CSSStyleDeclaration object that you get with getComputedStyle (available even on IE9)

eddyw commented 5 years ago

Changing position (left/top) of absolute elements is already an expensive task for the browser, so the above idea is to use the already computed styles instead of ugly "hack" 🙈 . I hope that helps :)

supertote commented 5 years ago

Thanks for your answer but I am not sure I fully understand what you are suggesting. Do you suggest to replace the code in re-resizable library size getter by getComputedStyle/getBounds as in your code ? I did try in my local copy of re-resizable and it works, as does just doing getBoundingClientRect, but since it within re-resizable I was more reporting the problem so that it gets fixed in the lib. Maybe I should report it in re-resizable rather than her?