首页 > 解决方案 > React 中不稳定的视差。什么是最佳做法?

问题描述

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

我想根据滚动位置直接更新元素的位置。目标是让元素看起来是固定的。我尝试了一些不同的方法,但总是在 Mac、有时是 Chrome 和某些设备上的 Safari 中遇到断断续续/闪烁的问题。

我已经尝试修复主体并让根 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)。

标签: reactjsparallax

解决方案


将滚动位置保持在组件状态确实需要在每次触发滚动事件时重新渲染所述组件。这种触发率是巨大的,这就是为什么应该避免 DOM 突变

在您的情况下,React 不仅必须更新 state 和reconcile,它还必须改变 DOM 以替换 last 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>
  )
}

推荐阅读