• 赚钱入口【需求资源】限时招募流量主、渠道主,站长合作;【合作模式】CPS长期分成,一次推广永久有收益。主动打款,不扣量;

详解React中的FLIP动画

CSS/HTML rin, seun 1年前 (2020-06-19) 320次浏览 0个评论

react flip动画

通过Safari的最新更新,现在所有现代浏览器(IE除外)都支持Web动画API(WAAPI),而无需标记。这是一个方便的笔,您可以在其中检查浏览器支持的功能。WAAPI是一种制作动画的好方法(需要用JavaScript完成),因为它是本机的-意味着不需要其他库即可工作。如果您完全不熟悉WAAPI,这里是Dan Wilson的一个很好的介绍。

react flip是最有效的动画方法之一。FLIP需要一些JavaScript才能完成工作。

让我们看一下使用WAAPI,FLIP并将所有这些集成到React中的交集。但是我们首先要没有React,然后再开始。

FLIP和WAAPI

WAAPI使FLIP动画变得更加容易!

FLIP快速复习:一个好主意是将元素放置在希望其首先结束的位置。接下来,应用变换将其移动到起始位置。然后取消应用这些转换。

动画变换非常有效,因此FLIP非常有效。在使用WAAPI之前,我们必须直接操纵元素的样式来设置转换并等待下一帧取消设置/反转:

// FLIP Before the WAAPI
el.style.transform = `translateY(200px)`;requestAnimationFrame(() => {
  el.style.transform = '';
});

许多库都基于这种方法。但是,这有几个问题:

  • 一切都感觉像是一个巨大的黑客。
  • 反转FLIP动画非常困难。移除类后,CSS转换将“免费”撤消,但事实并非如此。在先前的FLIP运行时启动新的FLIP可能会导致故障。反转需要getComputedStyles在设置新动画之前使用解析矩阵并使用它来计算当前尺寸。
  • 高级动画几乎是不可能的。例如,为了防止使缩放后的父级的孩子失真,我们需要在每个帧中访问当前缩放值。这只能通过解析变换矩阵来完成。
  • 浏览器有很多陷阱。例如,有时要使FLIP动画在Firefox中完美运行,就需要调用requestAnimationFrame两次:
requestAnimationFrame(() => {
  requestAnimationFrame(() => {
    el.style.transform = '';
  });
});

使用WAAPI时,我们不会遇到这些问题。通过该reverse 功能可以轻松完成反向操作。还可以对儿童进行反缩放。而且,当存在错误时,很容易找出确切的罪魁祸首,因为我们只使用诸如animate 和的简单功能reverse,而不是像requestAnimationFrame方法那样进行组合。

这是WAAPI版本的概述:

el.classList.toggle('someclass');
const keyframes = /* Calculate the size/position diff */;
el.animate(keyframes, 2000);

FLIP和React

要了解FLIP动画如何在React中工作,重要的是要知道它们如何以及最重要的是为什么它们可以在纯JavaScript中工作。回忆一下FLIP动画的解剖结构:

详解React中的FLIP动画

具有紫色背景的所有内容都需要在“绘制”渲染步骤之前进行。否则,我们会暂时看到新样式的闪烁,这是不好的。由于所有的DOM更新都是为我们完成的,因此在React中事情变得更加复杂。

FLIP动画的魔力在于,在浏览器有机会绘画之前先对元素进行转换。那么我们如何知道React中的“涂装前”时刻?

满足了useLayoutEffect钩。如果您甚至想知道它的用途是……就这样!凡是我们在这个回调传递同步发生后, DOM更新,但之前的油漆。换句话说,这是设置FLIP的好地方!

让我们做一些FLIP技术非常有用的事情:动画DOM位置。如果要对元素如何从一个DOM位置移动到另一个DOM进行动画处理,CSS将无能为力。(想象一下在待办事项列表中完成一项任务,然后将其移至“已完成”任务列表中,例如单击下面的笔中的项目。)

让我们看一个最简单的例子。单击以下笔中的两个正方形中的任何一个,即可使它们互换位置。没有FLIP,它将立即发生。

那里发生了很多事情。请注意,所有工作如何在生命周期挂钩回调中进行:useEffectuseLayoutEffect。有点令人困惑的是,仅从代码来看,FLIP动画的时间轴并不明显,因为它发生在两个 React渲染中。这是React FLIP动画的剖析,以显示不同的操作顺序:

详解React中的FLIP动画

尽管useEffect总是useLayoutEffect在浏览器绘制之后运行,但重要的是我们在第一次渲染后缓存元素的位置和大小。我们将没有机会在第二次渲染上进行此操作,因为它useLayoutEffect是在所有DOM更新之后运行的。但是该过程基本上与香草FLIP动画相同。

注意事项

像大多数事情一样,在React中使用FLIP时要注意一些注意事项。

保持在100ms以下

FLIP动画正在计算。计算需要时间,在显示平滑的60fps转换之前,您需要做很多工作。如果延迟小于100毫秒,人们将不会注意到延迟,因此请确保所有延迟都低于该延迟。DevTools中的“性能”选项卡是检查这一点的好地方。

详解React中的FLIP动画

不必要的渲染

我们不能使用useState来缓存大小,位置和动画对象,因为它们setState都会导致不必要的渲染并降低应用程序的速度。在最坏的情况下,它甚至可能导致错误。尝试useRef改为使用它,并将其视为可以在不呈现任何内容的情况下进行变异的对象。

布局

避免重复触发浏览器布局。在FLIP动画的上下文中,这意味着避免循环遍历元素并使用读取其位置getBoundingClientRect,然后立即使用动画为它们设置动画。尽可能批量“读取”和“写入”。这将使动画非常流畅。

动画取消

尝试在先前演示中的方块移动时随机单击它们,然后在它们停止后再次单击。您会看到故障。在现实生活中,用户将在移动元素时与它们进行交互,因此有必要确保其被取消,暂停和平滑更新。

但是,并非所有动画都可以通过反转reverse。有时,我们希望它们停下来然后移至新位置(例如在随机重排元素列表时)。在这种情况下,我们需要:

  • 获得移动元素的大小/位置
  • 完成当前动画
  • 计算新的大小和位置差异
  • 开始一个新的动画

在React中,这可能比看起来困难。我浪费了很多时间。当前动画对象必须被缓存。这样做的一个好方法是创建Map一个ID来获取动画。然后,我们需要获得运动元件的尺寸和位置。有两种方法可以做到这一点:

  1. 使用功能组件:只需循环浏览功能主体中的每个动画元素,然后缓存当前位置。
  2. 使用类组件:使用getSnapshotBeforeUpdate生命周期方法。

实际上,React官方文档建议使用getSnapshotBeforeUpdate“因为在“渲染”阶段生命周期(如render)和“提交”阶段生命周期(如getSnapshotBeforeUpdatecomponentDidUpdate)之间可能会有延迟。” 但是,此方法还没有钩子对应物。我发现使用功能组件的主体就足够了。

以浏览器的方式运行

我之前已经说过,但是请避免与浏览器争斗,并尝试使事情以浏览器的方式进行。如果我们需要为简单的尺寸变化设置动画,请考虑CSS是否足够(例如   transform: scale())。我发现FLIP动画最适合浏览器无法帮助的地方:

  • 动画化DOM位置变化(如上所述)
  • 共享布局动画

第二个是第一个的更复杂的版本。有两个DOM元素在起作用并看起来像一个在更改其位置(而另一个在卸载/隐藏)。此技巧可启用一些很酷的动画。例如,此动画是使用我建立的react-easy-flip一个使用以下方法的库制作的:

Library

有很多库可以简化React中的FLIP动画并抽象出样板。目前积极维护的项目包括:react-flip-toolkit和我的react-easy-flip

如果您不介意较重但可以进行更一般的动画处理,请查看framer-motion。它还可以做酷的共享布局动画!在该库中有一个视频。

喜欢 (0)

您必须 登录 才能发表评论!