javascript - 使用 react-spring 构建可重用的动画组件而不渲染包装元素

标签 javascript reactjs animation react-spring

我想用 react-spring 构建一个可重复使用的动画组件. 这是我到目前为止所拥有的:

codesandbox

const Fade = ({
  show,
  as = "div",
  children
}: {
  show: boolean;
  as?: keyof JSX.IntrinsicElements;
  children: React.ReactNode;
}) => {
  const transitions = useTransition(show, null, {
    from: { position: "absolute", opacity: 0 },
    enter: { opacity: 1 },
    leave: { opacity: 0 },
    unique: true
  });

  const AnimatedComponent = animated[as];

  return (
    <>
      {transitions.map(({ item, key, props }) => {
        if (!item) {
          return null;
        }

        return (
          <AnimatedComponent key={key} style={props}>
            {children}
          </AnimatedComponent>
        );
      })}
    </>
  );
};

但是,现在存在动画组件引入“副作用”的问题,即在我想要动画的 child 周围添加一个“div”包装器。 这会导致现有组件出现样式问题,需要进行大量更改。

因此,作为一种解决方法,我尝试将 animated 用作函数,并将子元素作为非实例化的 react 元素传递。但是随后会出现明显的卡顿,动画有时不会完成并在中途停止,例如,检查动画元素,并注意不透明度往往会停止在 0.98883393 而不是 1。

codesandbox

const Fade = ({
  show,
  children
}: {
  show: boolean;
  children: React.ReactElement;
}) => {
  const transitions = useTransition(show, null, {
    from: { position: "absolute", opacity: 0 },
    enter: { opacity: 1 },
    leave: { opacity: 0 },
    unique: true
  });

  // Here's the change. We wrap children in a functional component and clone
  // children inside
  const AnimatedComponent = animated(({ style }) =>
    React.cloneElement(children, { style })
  );

  return (
    <>
      {transitions.map(({ item, key, props }) => {
        if (!item) {
          return null;
        }

        return (
          <AnimatedComponent key={key} style={props}>
            {children}
          </AnimatedComponent>
        );
      })}
    </>
  );
};

我注意到引入额外的“div”包装器似乎是其中一些基于动画的库的副作用,例如 react-springframer-motion

是否有建议的方法来使用 react-spring 构建可重用的动画组件,而不会带来引入额外 DOM 元素的副作用

最佳答案

这是 react-spring 的旧版本,从那以后事情发生了变化,所以很难准确地解决为什么你有这种奇怪的行为,而 opacity 却没有达到 1.0。尽管如此,您还在控制台中收到有关更新未安装组件的警告,并且 key 属性始终为 undefined 因此我不确定您是否正确使用了转换。但由于该 api 文档已不复存在,因此很难进行故障排除。

尽管如此,我可以告诉您,没有必要在您的动画内容周围添加额外的包装器元素。在第一种情况下,您明确告诉 react-spring 添加一个元素 (animated.XXX),它当然会这样做。在你的第二个(损坏的)例子中,没有包装器,我认为如果你研究如何在那个旧版本中使用 useTransition ,你可以让它工作。

但是你应该知道 react-spring 在其最佳状态下使用 ref 来更新 React 之外的元素。这就是为什么 animated(animated.divanimated.span 等...)提供的所有基本 HTML 元素都具有这种包装行为看到。如果你使用 animated 来包装你自己的组件,你基本上有两种选择:

  • 确保您的自定义组件可以保存一个 ref 并将 animated 中的任何给定 ref 提供给它(使用 forwardRef)。现在您的组件将不会一直重新渲染,而只会重新渲染一次,并且更新将在 React 之外处理。使用此方法,您可以确定要将引用提供给哪个元素(而不是 react-spring 使用包装器为您简化此操作)。
  • 不要让您的自定义组件采用引用并接受您的组件将在每个动画帧上重新渲染一次(这就是您的示例中发生的情况,记录每个渲染以验证这一点)。从性能的 Angular 来看,这是次优的,但仍然有效。

总而言之,这并不能解释 opacity 的奇怪之处,但它确实解释了为什么 react-spring 更喜欢使用包装器组件, 因为这样它可以保证它可以以有效的方式为该元素的所有内容设置动画。幸运的是,react-spring 还为您提供了选择所需元素类型的选项。

关于javascript - 使用 react-spring 构建可重用的动画组件而不渲染包装元素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66805256/

相关文章:

objective-c - UIBarButtonItem 中的 UIButton 放弃了动画效果

javascript - 如何检查内部div中的任何span元素是否具有空值

javascript - 在事件的 Accordion 面板下选择一个类

javascript - 如何将 css 应用于 textarea 标签内以逗号分隔的文本? (React.js 元素)

android - 与 CoordinatorLayout 和 CollapsingToolbarLayout 结合使用时,共享元素转换不起作用

html - 如何在删除上层 div 时进行转换

javascript - jQuery,AJAX 使用 JSON 发布 Javascript 数组

javascript - 如何在 Javascript 中使由循环创建的 2 个按钮一起工作

reactjs - 使用 reselect 计算派生状态时如何避免 React 重新渲染

reactjs - 在组件库包中使用 StorybookJS,以 React 作为对等依赖项