React中的断续视差。最佳做法是什么?

时间:2019-08-03 19:59:17

标签: reactjs parallax

这是一个关于如何在React中最好地产生视差效果的普遍问题!

我想基于滚动位置直接更新元素的位置。目的是使元素看起来固定。我尝试了一些不同的方法,但在Mac(有时是Chrome)和某些设备上的Safari中,总是出现断断续续/闪烁的问题。

我尝试使主体固定并具有root div滚动(以摆脱某些浏览器的弹性影响),但存在相同的问题。我也尝试过将translateZ(0)添加到活动3d渲染中,但也遇到了同样的问题。

这是由于某些机制导致React更新组件的频率还是浏览器问题?

有没有更好的方法来创建这种效果?

import React, { useState, useEffect } from "react";

const useScroll = () => {
  const [scroll, setScroll] = useState(0);
  useEffect(() => {
    window.addEventListener("scroll", scrollHandler);
    return () => {
      window.removeEventListener("scroll", scrollHandler);
    };
  }, []);
  const scrollHandler = () => {
    setScroll(window.scrollY);
  };
  return scroll;
};

export default function App() {
  const scrollPos = useScroll();

  return (
    <main style={{ height: "300vh" }}>
      <div>Normal Div</div>
      <div style={{ transform: `translateY(${scrollPos}px)` }}>Fixed Div</div>
    </main>
  );
}

这是问题的简单再现。对我来说,它也会产生相同的问题(某些浏览器中不稳定,尤其是Mac上的Safari)。

1 个答案:

答案 0 :(得分:0)

将滚动位置保持在组件状态确实确实需要在每次触发滚动事件时重新渲染该组件。射击率很高,这就是DOM mutations should be avoided的原因。

在您的情况下,React不仅必须更新状态和reconcile,而且还必须对DOM进行更改以替换最后一个div,因为它的一个道具style,每个渲染都不同。随着组件的变大,震荡的影响将越来越严重。

=>即使React的性能很高,滚动事件也是特定的野兽,因此有资格获得命令性处理:

const useScrollHandler = (handler) => {
  useEffect(() => {
    window.addEventListener('scroll', handler)
    return () => {
      window.removeEventListener('scroll', handler)
    }
  }, [])
}

const FixedDiv = (props) => {
  const ref = useRef()
  const handler = () => { ref.current.style.transform = `translateY(${window.scrollY}px)` }
  useScrollHandler(handler)
  return <div ref={ref} {...props} />
}

function App() {
  return (
    <main style={{ height: '300vh' }}>
      <div>Normal Div</div>
      <FixedDiv>Fixed Div</FixedDiv>
    </main>
  )
}