Alexander Knyazev
Alexander Knyazev

Reputation: 2892

How to move fixed elements more smoothly?

I have some elements with {position: absolute}. When I'm scrolling page I want to move this elements.

I set window.addEventListener(scroll, this.handleScroll).

The simplified code:

componentDidMount = () => {
  window.addEventListener('scroll', this.handleScroll);
}

handleScroll {
  var scrollLeft = window.pageXOffset;
  this.setState({scrollLeft: scrollLeft})
}
...
<div style={{position: "absolute", left: this.state.scrollLeft + "px" 
}}>
   content 
</div>
<div style={{position: "absolute", left: this.state.scrollLeft + "px" 
}}>
   content 
</div>
....

On my PC this works excellent, but on the phone I see some hangs when elements are moving. How can I move elements more smoothly?

Upvotes: 0

Views: 1140

Answers (2)

Arash Motamedi
Arash Motamedi

Reputation: 10682

Looking at the snippet of code you've provided, it seems that you're hooking the window scroll event to a handler that changes the state of your component, which in turn will update the position of your component. This arrangement will unfortunately be slow/jittery on not only phone browsers, but also on slightly older desktop browsers. React, while generally fast, has an internal timer which may occasionally dictate it to batch state updates (when too many state changes happen in a short period of time, which would be the case with window scroll events). So, currently you're in an uphill battle against:

  • React's internal state transition engine (which times and batches updates), and

  • The layout redrawing process which, due to use of absolute positioning choice, will have to do a lot of recalculation every time you request it to position your component in the right place.

You can improve the situation and get smoother results by:

  • Using fixed position if at all possible. This will be natively handled by the browser, providing excellent refresh rate. If, as you've elaborated, you need the component fixed only in one axis, still see if you can start with the fixed position and adjust the floating axis using the window scroll callback.

  • Handle the repositioning of the element outside of React's state transitioning flow. While creating your component, create refs for them, and handle their positioning the plain-old Javascript way. Decoupling from the React state machine will likely improve things drastically. Here's how to get a reference to your DOM element: https://reactjs.org/docs/refs-and-the-dom.html Once you have that ref, you can manipulate it directly on the window scroll callback and avoid waiting for React state updates.

  • Use translate-x and translate-y instead of left and top to reposition your component. translate properties belong to the transform CSS directive group, and are fundamentally different from the left and top properties in that they don't require layout re-render which consume a lot of the browser's rendering time. You will likely gain a lot of performance improvement just by substituting your left and top properties with translate-x and translate-y. Additionally, if you couple this approach with the suggestion above (manipulating position outside of React state transition flow), you should see a greatly improved, much smoother, experience.

Upvotes: 1

Waseem
Waseem

Reputation: 761

It seems like what you are trying to achieve here, which to my understanding is making the content have the same position as the current scrolling position, can be done by using position: fixed on certain elements.

From MDN:

https://developer.mozilla.org/en-US/docs/Web/CSS/position

The element is removed from the normal document flow; no space is created for the element in the page layout. Instead, it is positioned relative to the screen's viewport and doesn't move when scrolled. Its final position is determined by the values of top, right, bottom, and left.

This will make the content stick on the screen regardless of your scrolling position, so it kind of "follows" you as you scroll.

Upvotes: 0

Related Questions