简介
renature 是一个基于物理学的React动画库,灵感来自于自然界。它从我们宇宙的物理学中获得灵感,并对现实世界的力量进行建模,如重力、摩擦、空气阻力和流体动力学。
renature 受其他流行的基于物理学的动画库的影响,如react-spring和Framer 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 中的每个钩子都期望有from 和to 属性,在这些属性中,我们描述了我们想要动画化的 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.start 和controller.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.