首页 > 解决方案 > 在动画更新时从父组件更新子组件 DOM

问题描述

我设置了以下 Codepen 以供参考: Animate React Ref GSAP

我有一个我正在处理的 React 项目,例如,其中存在 2 个组件:一个 Pitch(如在足球场中)和一个 Player。Pitch 是 Player 的父级,aforwardRef()用于管理可能在 Pitch 组件本身的球场上的每个球员的动画。

您还将在 Player 组件中看到render()。SVG 包含一个<g id="Position">...</g>分组,其中圆圈的位置应随着动画的进行而更新。这是我不知所措的地方。

在这个项目中,我使用 GSAP 来管理动画,但它实际上可以是任何动画库 - 如果我要通过状态更改重新渲染 Player 组件来制作动画,甚至可能没有,但性能可能会令人担忧。GSAPonUpdate为其timeline.to()函数调用提供了一个选项。可以从 ref 读取 SVG 的位置值,但尝试使用ReactDOM.findDOMNode(component) [reference]从父级更新其 DOM 是不好的做法。

因此,我的问题真的归结为:

如果我还没有完全说清楚,请告诉我,我会尽力澄清任何事情或一切!

标签: javascriptreactjs

解决方案


在设计 React 组件时,您应该遵循一些关键原则。

  1. 状态是事实的来源,所以你的组件如何显示(渲染)完全取决于状态(&prop)中的数据如何。永远不应该出现一种状态可以以不同方式呈现的情况。

考虑到这一点,拥有此代码timeline.to(playerRef.current, { x: 0, y: 0, duration: 2 });并不是真正的“反应方式”,因为它不会将玩家位置存储到状态中,而是使用另一件事(gsap)来操纵事物的显示方式(x,y 位置)

这是更多的反应方式

// Pitch
const Pitch = () => {
  const defaultPosition = { x: 210, y: 136 }
  const [playerPosition, setPlayerPosition] = useState(defaultPosition);
  
  const playMe = () => {
    setPlayerPosition({ x: 0, y: 0 });
  };

  const resetMe = () => {
    setPlayerPosition(defaultPosition);
  };

  return (
    <>
      <button type="button" onClick={playMe}>Play</button>
      <button type="button" onClick={resetMe}>Reset</button>
      <hr />
      <svg width="420px" height="272px" viewBox="0 0 420 272">
        <g id="Pitch">
          <rect id="Pitch-Background" fill="#409D41" x="0" y="0" width="420" height="272"></rect>
        </g>
        <Player position={playerPosition} />
      </svg>
    </>
  );

}

所以下一个问题将是“我们如何管理动画?”

React Component 中 90% 的动画,你会想要在状态之间做动画。状态 A 到状态 B 之间的转换。

在 React 中管理动画方面,我可以分为 3 组。

  1. 从预定义状态 A 到预定义状态 B 的转换(例如 div open x = 200 到 div close x = 0)
  2. 动态从状态 A 转换到状态 B(例如移动到发生鼠标单击的位置)
  3. 状态 A 到状态 B 之间的时间线/动画序列(例如,三个元素必须一个接一个地进行动画处理才能完成整个动画序列)

如果您的用例属于 1. 那么 csstransition就是您的答案。

如果您的用例属于 2. 那么 csstransition可以是您的答案,也可以是一些动画库,例如react-motion, react-spring,framer motion等。

如果您的用例属于 3。那么 GSAP 时间线就是您的大男孩。但是有一定的方法可以写这个。在这一点上,我不会详细介绍。


同样,在您的示例中......假设它是 2。(从状态 A 动态转换到状态 B)。我们可以用 css 解决这个问题transition

const Player = ({ position }) => {
  return (
    <g
      style={{ transition: `all 1s ease-out` }}
      transform={`translate(${newPosition.x},${newPosition.y})`}
    >
      <circle id="Oval" fill="#FF0000" cx="23" cy="39" r="19.5"></circle>
    </g>
}

这是我所描述的工作代码。 https://codepen.io/Doppy/pen/XWjNmdB

PS。我在示例中添加了如何让玩家移动到鼠标点击的位置:)

祝你有美好的一天!


推荐阅读