CSS transform: scale very shaky/choppy/jerky in Safari

Viewed 580

I'm trying to implement a scroll based shrinking effect on on a div element. As the user scrolls, the div shrinks in height (using scale) until it finally disappears. I'm using React to render the HTML and styled-components for the CSS.

I'm having problems with the performance of this effect, specifically only with Safari, Version 13.1 (15609.1.20.111.8). In Safari, the transition effect is very janky and does not happen smoothly.

Chrome and Firefox work perfectly.

Here is the React component:

const MyComponent = React.forwardRef((_, ref) => {
  const [scaleOffset, setScaleOffset] = React.useState(1)

  const onScroll = React.useCallback(debounceWindowEventCallback(() => {
    const paddingOffset = window.scrollY - ref.current.offsetTop
    if (paddingOffset >= 0) setScaleOffset(1 - (paddingOffset / 100))
  }), [ref]);

  React.useEffect(() => {
    if (scaleOffset <= 0) {
      window.removeEventListener('scroll', onScroll)
      console.log('Border has been fully shrunk')
    }
  }, [onScroll, scaleOffset])

  React.useEffect(() => {
    window.addEventListener('scroll', onScroll)
    return () => window.removeEventListener('scroll', onScroll)
  }, [])

  return (
    <RootElement ref={ref}>
      <ShrinkBorderTop scaleOffset={scaleOffset} />
    </RootElement>
  );
});

And the relevant styled components are:

const ShrinkBorderTop = styled.div`
  position: absolute;
  background-color: white;
  transition: transform .175s ease-out;
  width: 100%;
  height: 200px;
  transform: ${({ scaleOffset }) => `scale(1, ${scaleOffset})`};
`

What I've tried:

  • I initially thought its because my component was rendering too much (every scroll event) so I implemented the debounceWindowEventCallback function which uses window.requestAnimationFrame to ensure that there's only a single paint per frame. Unfortunately, that didn't help.
  • I then tried adding will-change: transition, transform, height, width; to ShrinkBorderTop, after googling the problem. This also didn't help.
  • Then I tried adding perspective: 1000; backface-visibility: hidden; to ShrinkBorderTop, this also didn't work.

I'm stumped as to what could be causing this problem, in my view its quite a standard animation and I've applied all the performance boosts I'm aware of, in addition to scale being hardware accelerated by default, but the problem still persists, only in safari.

What could be causing this?

0 Answers
Related