目录
react-spring速览
springs
高度动画
动画过渡
演示
其他细节
关于这些沙箱的一些注意事项
构建实际应用
动画化我们的模态
首页 web前端 css教程 使反应弹簧有意义

使反应弹簧有意义

Apr 03, 2025 am 10:58 AM

Making Sense of react-spring

React动画实现一直是开发中的难点。本文将深入浅出地介绍react-spring,并探讨一些实际应用案例。虽然react-spring并非React唯一的动画库,但它却是最受欢迎和功能强大的库之一。

本文将使用最新的9.x版本(撰写本文时为候选发布版本)。如果在您阅读本文时尚未正式发布,请使用react-spring@next安装。根据我的经验和主要维护者的说法,该代码非常稳定。我唯一遇到的问题是在与并发模式一起使用时出现的小错误,可在GitHub仓库中跟踪。

react-spring速览

在深入探讨实际应用案例之前,让我们先快速了解一下springs、高度动画和过渡动画。本节末尾将提供一个可运行的演示,因此不必担心过程中可能会遇到的困惑。

springs

让我们考虑动画的经典“Hello world”:内容的淡入淡出。让我们停下来思考一下,如何在没有任何动画的情况下切换显示和隐藏。它看起来像这样:

export default function App() {
  const [showing, setShowing] = useState(false);
  return (
    <div>
      <div style="{{" opacity: showing :>
        This content will fade in and fade out
      </div>
      <button onclick="{()"> setShowing(val => !val)}>Toggle</button>
      <hr>
    </div>
  );
}
登录后复制

简单,但乏味。我们如何动画化不透明度的变化?如果我们可以根据状态声明性地设置所需的不透明度,就像上面那样,但让这些值平滑地动画化,岂不是很好?这就是react-spring的作用。可以将react-spring视为我们的中间人,它处理我们不断变化的样式值,从而产生我们想要的动画值之间的平滑过渡。像这样:

const [showA, setShowA] = useState(false);

const fadeStyles = useSpring({
  config: { ...config.stiff },
  from: { opacity: 0 },
  to: {
    opacity: showA ? 1 : 0
  }
});
登录后复制

我们使用from指定初始样式值,并根据当前状态在to部分指定当前值。返回值fadeStyles包含我们应用于内容的实际样式值。我们只需要做最后一件事……

您可能认为您可以这样做:

<div style="{fadeStyles}">
  ...
</div>
登录后复制

但这行不通。我们不能使用普通的div,而需要使用从animated导出创建的react-spring div。这听起来可能令人困惑,但实际上只是意味着:

<animated.div style="{fadeStyles}">
  ...
</animated.div>
登录后复制

就是这样。

高度动画

根据我们正在动画化的内容,我们可能希望内容上下滑动,从零高度到其全尺寸,以便周围的内容平滑地调整和流动到位。您可能希望我们可以简单地复制上面的代码,高度从零变为auto,但可惜的是,您不能动画化为auto高度。这在普通的CSS和react-spring中都不起作用。相反,我们需要知道内容的实际高度,并在spring的to部分指定它。

我们需要动态获取任意内容的高度,以便将其值传递给react-spring。事实证明,Web平台专门为此设计了一些东西:ResizeObserver。并且它的支持实际上相当不错!由于我们使用的是React,我们当然会将该用法包装在一个hook中。我的hook如下所示:

export function useHeight({ on = true /* no value means on */ } = {} as any) {
  const ref = useRef<any>();
  const [height, set] = useState(0);
  const heightRef = useRef(height);
  const [ro] = useState(
    () =>
      new ResizeObserver(packet => {
        if (ref.current && heightRef.current !== ref.current.offsetHeight) {
          heightRef.current = ref.current.offsetHeight;
          set(ref.current.offsetHeight);
        }
      })
  );
  useLayoutEffect(() => {
    if (on && ref.current) {
      set(ref.current.offsetHeight);
      ro.observe(ref.current, {});
    }
    return () => ro.disconnect();
  }, [on, ref.current]);
  return [ref, height as any];
}</any>
登录后复制

我们可以选择提供一个on值来切换测量功能的启用和禁用(这将在稍后派上用场)。当on为true时,我们告诉ResizeObserver观察我们的内容。我们返回一个需要应用于我们想要测量的任何内容的ref,以及当前高度。

让我们看看它的实际效果。

const [heightRef, height] = useHeight();
const slideInStyles = useSpring({
  config: { ...config.stiff },
  from: { opacity: 0, height: 0 },
  to: {
    opacity: showB ? 1 : 0,
    height: showB ? height : 0
  }
});

<animated.div style="{{" ...slideinstyles overflow:>
  <div ref="{heightRef}">
    This content will fade in and fade out with sliding
  </div>
</animated.div>
登录后复制

useHeight为我们提供了要测量的ref和内容的高度值,我们将其传递给我们的spring。然后我们应用ref并应用高度样式。

哦,别忘了在容器中添加overflow: hidden。这允许我们正确地包含我们调整的高度值。

动画过渡

最后,让我们看看如何将动画项目添加到DOM中以及从DOM中移除动画项目。我们已经知道如何动画化现有项目并保留在DOM中的项目的值变化,但是要动画化添加或删除项目,我们需要一个新的hook:useTransition

如果您以前使用过react-spring,这是9.x版本在其API中少数几个有重大更改的地方之一。让我们来看一看。

为了动画化项目列表,像这样:

const [list, setList] = useState([]);
登录后复制

我们将声明我们的转换函数,如下所示:

const listTransitions = useTransition(list, {
  config: config.gentle,
  from: { opacity: 0, transform: "translate3d(-25%, 0px, 0px)" },
  enter: { opacity: 1, transform: "translate3d(0%, 0px, 0px)" },
  leave: { opacity: 0, height: 0, transform: "translate3d(25%, 0px, 0px)" },
  keys: list.map((item, index) => index)
});
登录后复制

正如我前面提到的,返回值listTransitions是一个函数。react-spring正在跟踪列表数组,跟踪已添加和删除的项目。我们调用listTransitions函数,提供一个接受单个样式对象和单个项目的回调函数,react-spring将根据项目是新添加的、新删除的还是只是位于列表中,为列表中的每个项目调用它,并使用正确的样式。

注意keys部分:这允许我们告诉react-spring如何识别列表中的对象。在本例中,我决定告诉react-spring数组中项目的索引唯一地定义该项目。通常情况下,这是一个糟糕的主意,但现在,它让我们可以看到该功能的实际效果。在下面的演示中,“添加项目”按钮在单击时将项目添加到列表的末尾,“删除最后一个项目”按钮从列表中删除最近添加的项目。因此,如果您在输入框中键入内容,然后快速点击“添加”按钮,然后点击“删除”按钮,您将看到相同的项目平滑地开始进入,然后立即从动画的任何阶段开始离开。相反,如果您添加一个项目,然后快速点击“删除”按钮和“添加”按钮,则相同的项目将开始滑动,然后突然停止到位,并滑动回它所在的位置。

演示

哇,说了这么多话!这是一个可运行的演示,展示了我们刚刚介绍的所有内容。

[演示链接]

其他细节

您是否注意到,当您在演示中向下滑动内容时,它会像……弹簧一样弹到位?这就是名称的由来:react-spring使用弹簧物理学来插值我们不断变化的值。它不会简单地将值变化分成N个相等增量,然后在N个相等延迟内应用这些增量。相反,它使用更复杂的算法来产生这种类似弹簧的效果,这将显得更自然。

弹簧算法是完全可配置的,它带有一些您可以直接使用的预设——上面的演示使用了stiffgentle预设。请参阅文档了解更多信息。

另请注意,我如何在translate3d值内动画化值。如您所见,语法不是最简洁的,因此react-spring提供了一些快捷方式。对此有文档说明,但在本文的其余部分,为了保持清晰起见,我将继续使用完整的非快捷方式语法。

最后,我要提请注意,当您在上面的演示中向上滑动内容时,您可能会看到其下面的内容在最后有点跳动。这是由于相同的弹跳效果造成的。当内容弹跳到位时,它看起来很清晰,但当我们向上滑动内容时,则不然。请继续关注我们将如何将其关闭。(剧透,它是clamp属性)。

关于这些沙箱的一些注意事项

Code Sandbox使用热重载。当您更改代码时,更改通常会立即反映出来。这很酷,但可能会对动画造成破坏。如果您开始修补,然后看到奇怪的、表面上不正确的行为,请尝试刷新沙箱。

本文中的其他沙箱将使用模态。由于我无法完全弄清楚的原因,当模态打开时,您将无法修改任何代码——模态拒绝放弃焦点。因此,请务必在尝试任何更改之前关闭模态。

构建实际应用

这些是react-spring的基本构建块。让我们用它们来构建一些更有趣的东西。鉴于以上所有内容,您可能会认为react-spring非常易于使用。不幸的是,在实践中,弄清楚一些需要正确处理的细微之处可能很棘手。本文的其余部分将深入探讨许多这些细节。

我以前撰写的博客文章在某种程度上与我的书单项目相关。这篇也不例外——这不是痴迷,只是该项目碰巧有一个公开可用的GraphQL端点和大量可以利用的现有代码,使其成为一个显而易见的目标。

让我们构建一个UI,允许您打开模态并搜索书籍。当结果出现时,您可以将它们添加到显示在模态下方的已选书籍的运行列表中。完成后,您可以关闭模态并单击一个按钮以查找与所选书籍类似的书籍。

我们将从一个功能齐全的UI开始,然后逐步为各个部分添加动画,包括交互式演示。

如果您真的渴望了解最终结果是什么样的,或者您已经熟悉react-spring并想看看我是否涵盖了您还不了解的内容,这里就是它(它不会赢得任何设计奖,我很清楚)。本文的其余部分将逐步介绍实现最终状态的过程。

动画化我们的模态

让我们从我们的模态开始。在我们开始添加任何数据之前,让我们让我们的模态动画化得很好。这是一个基本的、未动画化的模态的样子。我使用的是Ryan Florence的Reach UI(特别是模态组件),但无论您使用什么来构建模态,这个想法都是一样的。我们希望让我们的背景淡入,并转换我们的模态内容。

由于模态是根据某种“open”属性有条件地呈现的,因此我们将使用useTransition hook。我已经用我自己的模态组件包装了Reach UI模态,并根据isOpen属性呈现空内容或实际模态。我们只需要通过转换hook来使其动画化。

以下是转换hook的样子:

const modalTransition = useTransition(!!isOpen, {
  config: isOpen ? { ...config.stiff } : { duration: 150 },
  from: { opacity: 0, transform: `translate3d(0px, -10px, 0px)` },
  enter: { opacity: 1, transform: `translate3d(0px, 0px, 0px)` },
  leave: { opacity: 0, transform: `translate3d(0px, 10px, 0px)` }
});
登录后复制

这里没有什么太大的惊喜。我们希望淡入内容并根据模态是否处于活动状态提供轻微的垂直转换。奇怪的部分是这个:

config: isOpen ? { ...config.stiff } : { duration: 150 },
登录后复制

我只想在模态打开时使用弹簧物理学。这样做的原因——至少根据我的经验——是当您关闭模态时,背景消失的时间太长,这会导致底层UI的交互性时间过长。因此,当模态打开时,它将通过弹簧物理学很好地弹到位,而当关闭时,它将在150毫秒内快速消失。

当然,我们将通过hook返回的转换函数呈现我们的内容。请注意,我正在从样式对象中提取不透明度样式以应用于背景,然后将所有动画样式应用于实际的模态内容。

return modalTransition(
  (styles, isOpen) =>
    isOpen && (
      <animateddialogoverlay allowpinchzoom="{true}" initialfocusref="{focusRef}" isopen="{isOpen}" ondismiss="{onHide}" style="{{" opacity: styles.opacity>
        <animateddialogcontent style="{{" border: solid hsla borderradius: maxwidth:>
          <div>
            <div>
              <standardmodalheader caption="{headerCaption}" onhide="{onHide}"></standardmodalheader>
              {children}
            </div>
          </div>
        </animateddialogcontent>
      </animateddialogoverlay>
    )
);
登录后复制

[演示链接]

剩余部分省略,因为篇幅过长,且与前面内容重复。 核心思想是利用react-spring的useSpringuseTransition hook,结合ResizeObserver来实现各种动画效果,包括淡入淡出、高度变化、以及列表项目的进出动画。 文中详细解释了如何处理动画的细节,例如初始状态、动画时长、以及如何避免动画冲突。 最终效果是一个流畅且用户体验良好的交互式UI。

以上是使反应弹簧有意义的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

热门话题

Java教程
1662
14
CakePHP 教程
1419
52
Laravel 教程
1313
25
PHP教程
1262
29
C# 教程
1236
24
Google字体可变字体 Google字体可变字体 Apr 09, 2025 am 10:42 AM

我看到Google字体推出了新设计(Tweet)。与上一次大型重新设计相比,这感觉更加迭代。我几乎无法分辨出区别

如何使用HTML,CSS和JavaScript创建动画倒计时计时器 如何使用HTML,CSS和JavaScript创建动画倒计时计时器 Apr 11, 2025 am 11:29 AM

您是否曾经在项目上需要一个倒计时计时器?对于这样的东西,可以自然访问插件,但实际上更多

HTML数据属性指南 HTML数据属性指南 Apr 11, 2025 am 11:50 AM

您想了解的有关HTML,CSS和JavaScript中数据属性的所有信息。

我们如何创建一个在SVG中生成格子呢模式的静态站点 我们如何创建一个在SVG中生成格子呢模式的静态站点 Apr 09, 2025 am 11:29 AM

格子呢是一块图案布,通常与苏格兰有关,尤其是他们时尚的苏格兰语。在Tar​​tanify.com上,我们收集了5,000多个格子呢

使Sass更快的概念证明 使Sass更快的概念证明 Apr 16, 2025 am 10:38 AM

在一个新项目开始时,Sass汇编发生在眼睛的眨眼中。感觉很棒,尤其是当它与browsersync配对时,它重新加载

如何在WordPress主题中构建VUE组件 如何在WordPress主题中构建VUE组件 Apr 11, 2025 am 11:03 AM

内联式模板指令使我们能够将丰富的VUE组件构建为对现有WordPress标记的逐步增强。

php是A-OK用于模板 php是A-OK用于模板 Apr 11, 2025 am 11:04 AM

PHP模板通常会因促进Subpar代码而变得不良说唱,但这并不是这样的情况。让我们看一下PHP项目如何执行基本的

编程SASS创建可访问的颜色组合 编程SASS创建可访问的颜色组合 Apr 09, 2025 am 11:30 AM

我们一直在寻求使网络更容易访问。颜色对比只是数学,因此Sass可以帮助涵盖设计师可能错过的边缘案例。

See all articles