在React中用renature创建基于物理学的动画

172 阅读6分钟

简介

renature 是一个基于物理学的React动画库,灵感来自于自然界。它从我们宇宙的物理学中获得灵感,并对现实世界的力量进行建模,如重力、摩擦、空气阻力和流体动力学。

renature 受其他流行的基于物理学的动画库的影响,如react-springFramer Motion 的元素。在这篇文章中,我们将通过建立几个例子和应用来学习如何在renature 中创建动画。

基本实现

首先:通过运行下面的任一命令来安装renature 包。

npm install --save renature
# or
yarn add renature

renature 提供了六个钩子,我们可以在创建动画时使用。前三个是useFriction,useGravity, 和useFluidResistance 钩子。这些钩子对单个元素的属性进行动画处理。

它们都以类似的方式工作,但正如它们的名字所暗示的,它们所模拟的自然力和它们所依赖的物理学将有所不同。而且,根据你所使用的力,配置对象在不同的钩子之间是不同的。

还有useFrictionGroup,useGravityGroup, 和useFluidResistanceGroup 钩子,我们可以用它们来同时对一组元素的属性进行动画化。

让我们看一下使用useFriction 钩子的基本实现。

import { useFriction } from "renature";
import "./styles.css";

export default function App() {
  const [props] = useFriction({
    from: {
      transform: "translateX(100px)"
    },
    to: {
      transform: "translateX(300px)"
    },
    config: {
      mu: 0.2,
      mass: 20,
      initialVelocity: 5
    },
    repeat: Infinity
  });

  return (
    <div className="App">
      <div {...props} className="box" />
    </div>
  );
}

如果你熟悉 react-spring,你会发现renature 有一个类似的声明式 API。renature 中的每个钩子都期望有fromto 属性,在这些属性中,我们描述了我们想要动画化的 CSS 状态。如果在from 中为某个属性设置了一个值,但在to 中却没有,那么这个属性将被忽略,它将不会被制作成动画。

有时我们可能希望我们的动画能无限地运行而不停止。我们可以通过在我们的动画配置中应用repeat: Infinity 来做到这一点。

由于我们使用了useFriction 钩子,我们传入了相应的物理参数。正如它的名字所示,useFriction 钩子是按照摩擦力来建模的。这些参数是。

  • mu ,这是动能摩擦的系数
  • mass ,是运动体的质量
  • initialVelocity ,是运动的初始速度

renature 的奇妙之处在于,你不需要成为一个科学大师就能做出令人敬畏的动画。

为多个属性制作动画renature

所以我们已经看到了useFriction 钩子是如何工作的,以及如何对一个元素的transform 属性进行动画。在必要的时候,我们也可以同时给多个属性做动画。

import { useFriction } from "renature";
import "./styles.css";

export default function App() {
  const [props] = useFriction({
    from: {
      transform: "translateX(100px), rotate(0deg)",
      background: "red",
      borderRadius: "0%"
    },
    to: {
      transform: "translateX(300px), rotate(360deg)",
      background: "steelblue",
      borderRadius: "50%"
    },
    config: {
      mu: 0.2,
      mass: 20,
      initialVelocity: 5
    },
    repeat: Infinity
  });

  return (
    <div className="App">
      <div {...props} className="box" />
    </div>
  );
}

要给一个元素的多个属性设置动画,请添加更多你想设置动画的元素的属性。

在组中创建动画

我们可能想同时给多个元素做动画。分组动画允许我们指定要动画化的元素数量,并为每个元素独立设置配置。

import { useGravityGroup } from "renature";
import "./styles.css";
export default function App() {
  const [nodes] = useGravityGroup(3, (i) => ({
    from: {
      transform: "translate(0px, 0px) scale(1) skewY(0deg)",
      opacity: 0
    },
    to: {
      transform: "translate(20px, 20px) scale(1.2) skewY(5deg)",
      opacity: 1
    },
    config: {
      moverMass: 10000,
      attractorMass: 10000000000000,
      r: 10
    },
    repeat: Infinity,
    delay: i * 500
  }));
  return (
    <div className="App">
      <div className="container">
        {nodes.map((props, i) => {
          return <div className="box" key={i} {...props} />;
        })}
      </div>
    </div>
  );
}

我们使用useGravityGroup 来创建上面的分组动画。所有的分组动画钩子都采取类似的形式。

const [props] = use<Force>Group(n: number, fn: (index: number) => Config);

renature 你可以通过在你的动画配置中指定delay 属性来延迟动画。delay 希望得到一个以毫秒为单位的数字,一旦指定的delay 已经过去,就会开始动画。这最常用于分组动画,你想以固定的时间间隔错开孩子的动画。

控制动画状态

让我们看看如何在renature 中控制动画状态。我们可以启动、停止、暂停、延迟和运行特定次数的动画。我们已经看过了如何重复和延迟动画。

为了控制动画状态,renature 提供了一个controller API。controller 是由renature 钩子返回的第二个对象,它有三个方法 -start,pause, 和stop - 用于与你的动画的播放状态交互。

import { useFriction } from "renature";
import "./styles.css";
import Button from "./Button";

export default function App() {
  const [props, controller] = useFriction({
    from: {
      transform: "translateY(0px)",
      opacity: 1,
      borderRadius: "10%"
    },
    to: {
      transform: "translateY(50px)",
      opacity: 0,
      borderRadius: "50%"
    },
    repeat: Infinity,
    pause: true //Signal that the animation should not run on mount.
  });

  return (
    <div className="App">
      <div className="btn-box">
        <Button action={controller.start} text="start" />
        <Button action={controller.pause} text="pause" />
        <Button action={controller.stop} text="stop" />
      </div>
      <div className="box" {...props} />
    </div>
  );
}

我们使用controller API中的controller.start 方法来启动动画。要做到这一点,我们需要在我们的动画配置中包含pause: true ,以防止动画在加载时立即运行。

要暂停一个正在运行的动画,我们使用controller.pause 方法。这个方法将停止帧的循环,但保留动画状态。这意味着我们可以在任何时候使用controller.start 来恢复一个动画。

要停止一个正在运行的动画,我们使用controller.stop 方法。不像controller.pause ,这个方法会破坏动画状态,所以我们应该只在确定要结束动画的时候使用。

renature 演示

现在我们知道了如何用renature 来创建动画,让我们看看一些实际的应用。

卡片悬停演示

你是否曾经将鼠标悬停在一个卡片上,看到图像沿着其容器的宽度和高度上下缩放?让我们用renature 来重现这种效果。

这里是我们将建立的一个沙盒。

虽然CSS不是本主题的重点,但我们需要一些CSS来使这个效果正常工作。

.card-box {
  width: 200px;
  border: 1px solid grey;
  overflow: hidden;
  margin-bottom: 2rem;
}

我们必须为card-box div设置一个overflow: hidden ,以确保当图像缩放时,它仍然被限制在其容器中。在解释了这一点之后,让我们回到renature

利用我们学到的关于控制动画状态的知识,每当我们在img-box div上悬停时,我们就调用controller.startcontroller.pause 方法。

export default function Card({ imgUrl, props, controller }) {
  return (
    <div className="card-box">
      <div
        className="img-box"
        {...props}
        onMouseEnter={controller.start}
        onMouseLeave={controller.pause}
      >
        <img src={imgUrl} />
      </div>

      //more stuff below...

通知吐司演示

通知祝酒词是应用程序中常用的组件之一。让我们使用renature 创建一个。

这里有一个我们将建立的沙盒。

对于CSS,我们必须将敬酒的不透明度设置为0,以便它只在按钮被点击时显示。

return (
    <div className="App">
      <button onClick={controller.start}>show toast</button>
      <Toast props={props} />
    </div>
);

当按钮被点击的时候,我们调用controller.start 方法。这样就会触发动画,吐司就会掉落到视野中。

可访问性问题

不是每个人都喜欢装饰性的动画或过渡,有些用户在面对视差滚动、缩放效果等时,会有彻头彻尾的心理和生理困难。因此,我们必须确保我们的动画不会给应用程序的用户带来可及性问题。

renature'的配置对象中,我们可以包括一个可选的reducedMotion 属性。如果这个属性没有被指定,并且最终用户喜欢减少动作,那么动画元素将被立即设置为to 状态,以避免潜在的有害动画。

总结

虽然我在本文中做了一些演示,展示了renature 在现实世界中的应用,但如果该团队包括更多可能对开发者有用的实际演示,那就太棒了。不管怎么说,renature 是一个很棒的基于物理的动画库。

The postCreating physics-based animations in React with renatureappeared first onLogRocket Blog.