React Spring Tutorial: Making Animated React Apps
将动画整合到UI设计中可能是一件棘手的事情。 去年12月,我们发表了一篇文章,描述了使用Framer Motion制作的React应用中的"黄油平滑"动画。 Framer Motion的问题是缺少有关如何做最简单的事情的教程。 研究React Spring库可以使人们面临相反的问题。
尽管其文档井井有条,详细,易于使用,并提供了许多令人印象深刻的示例,但是如果您的目的是获取基础知识,则其中大多数都太复杂了。 因此,我们认为编写一些React Spring教程可能会有所帮助。
本文对从React Spring开始的人很有用。 我们将研究各种方法,这些方法可以将多个弹簧组合在一起以制作更复杂的动画,包括示例。
React Spring库的5个钩子
动画为设计和改善应用的用户体验增添了生气。 React Spring是一个基于物理的动画库。 它可以计算出幕后的所有机制,从而获得精美流畅的动画,并允许我们为用户部分或完全配置它。 而且,React Spring比使用纯CSS更容易(至少,没有疯狂的复杂且难以维护的大量代码)。
目前,React Spring库包含 5个钩子:
React Spring库的所有这5个钩子在本质上都是相似的,但是每个钩子都有其独特之处。 让我们更详细地检查其中的每一个。
useSpring
我们将从最简单的一个开始-
如果console.log由Spring返回的值,我们将得到一个包含动画道具的对象:
对于第一个示例,我们将创建一个简单的动画组件,该组件将在单击时调整大小并移动
首先,让我们导入所需的钩子和动画组件:
对于外观动画,只需指出以下内容即可:
1 | import { Animated, useSpring } from"React Spring"; |
对于外观动画,只需指出以下内容即可:
1 2 3 4 | const springProps = useSpring({ from: { opacity: 0, ... }, to: { opacity: 1, ... } }) |
Spring可以对从初始
此外,可以省略
1 2 3 4 | const springProps = useSpring({ opacity: 1, from: { opacity: 0 }, }) |
您可以设置几乎所有CSS属性的动画或使用任意名称,保留关键字除外。
接下来,您需要将
需要以下样式组件/情感/等组件:
1 | const AnimatedBox = styled(Animated(ComponentName/TagName))`...`; |
或只是任何HTML标记,并带有"动画"前缀:
剩下的就是将prop传递给所需的组件:
1 |
React Spring钩子可以接受几乎任何值或颜色,HTML属性,甚至可以是字符串模式(例如,
<iframex style =" BORDER-BOTtoM:0像素; BORDER-LEFT:0像素;宽度:100%;高度:500像素;溢出:隐藏; BORDER-toP:0像素; BORDER-RIGHT:0像素;边框半径:4像素" title = react-spring-useSpring src ="https://codesandbox.io/embed/react-spring-useSpring-irxxw?fontsize=14&hidenavigation=1&theme=dark&view=preview" sandbox ="allow-modals allow-forms allow-popups allow- 脚本allow-same-origin"> </iframex>
从下面的示例中可以看到,如果需要,可以对动画状态进行重构。 例如,当使用任意名称或插值时,您需要一次描述多个组件的动画状态时,这很有用(稍后将对其进行详细说明)。
我们将使用
1 2 3 4 5 6 7 8 9 10 11 | const [clicked, setClicked] = usestate(false); const { size, ..., ...springProps } = useSpring({ size: clicked ? 300 : 200, backgroundPosition: clicked ?"50% 100%" :"50% 0%", ... from: { size: 200, backgroundPosition:"50% 0%" ... } }); |
我们将解构
剩下的只是将动画值传递给我们的组件,并且当然,在单击时更改其状态:
1 2 | setClicked(!clicked)} /> |
现在,在单击之后,状态将从
也许值得一提的是此示例中使用的
1 | counter: clicked ? 100 : 0, |
重要的是不要忘记解构计数器,因为我们将需要在传递给
为了从0到100迭代计数器值,我们将使用一些插值:
1 2 |
换句话说,内插使您可以迭代函数的值或指定值的范围。 请注意,传递到动画图元的插值工作效率更高,并且占用的空间更少。
很简单,对-
useSprings
1 | import { Animated, useSprings } from"React Spring"; |
useSprings挂钩与先前的挂钩略有不同。 它为静态列表创建多个spring动画。
<iframex style =" BORDER-BOTtoM:0像素; BORDER-LEFT:0像素;宽度:100%;高度:500像素;溢出:隐藏; BORDER-toP:0像素; BORDER-RIGHT:0像素;边框半径:4像素" title = react-spring-useSprings src ="https://codesandbox.io/embed/react-spring-useSprings-rsk8x?fontsize=14&hidenavigation=1&theme=dark&view=preview" sandbox =" allow-modals allow-forms allow-popups allow- 脚本allow-same-origin"> </iframex>
在此示例中,配色方案列表是我们的列表,该列表使用
1 | const [index, setindex] = usestate(null); |
1 2 3 | const springs = useSprings(list.length, list.map(item => ({ ... })); |
在此示例中,
1 2 3 4 5 6 7 8 9 10 11 12 13 | const springs = useSprings( colorScheme.length, colorScheme.map((item, i) => ({ background: item.hex, color: item.fontColor, opacity: index === null | i === index ? 1 : 0.6, height: index === null ? 120 : 60, from: { opacity: 0, height: 120 } })) ); |
背景和颜色值取自
1 2 3 4 | const colorScheme = [ { name:"Red munsell", hex:"#ec0b43", fontColor:"#fff" }, ... ]; |
乍一看,springs参数似乎令人困惑,但实际上,它非常简单。 在单击列表元素之一之前(即,当index = null时),所有有色块的不透明度值=1。如果已经单击了列表元素之一,则当前元素的不透明度= 1。 所有其他元素的不透明度= 0.6。 在高度值的情况下,它甚至更简单。 在我们单击其中一个色块之前,其值= 120,而在被单击后= 60。
我们仍然必须将
1 2 3 4 5 6 7 | <GridContainer pt={1}> {springs.map((prop, i) => ( { setindex(i); onItemClick(i); }} style={prop} /> ))} </GridContainer> |
单击时定义状态索引并执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | const [springProps, setspringProps] = useSpring(() => ({ from: { height: 0, opacity: 0 } })); const onItemClick = i => { const { name, hex, fontColor } = colorScheme[i]; setspringProps({ name, background: hex, color: fontColor, height: 200, opacity: 1 }); }; |
在这里,我们使用第二种方法来确定
它仅保留将
1 2 3 4 5 | {springProps.name}</AnimatedBox> {index !== null && colorScheme[index].hex} </AnimatedBox> </AnimatedItem> |
在这种情况下,十六进制值直接取自对象的原始数组,因为如果我们从spring中获取此值,则将获得RGBA格式的颜色插值,这与我们的预期不完全相同。
usetrail
1 | import { Animated, usetrail, interpolate } from"React Spring"; |
usetrail允许您通过单个配置创建多个spring动画,并且每个后续spring在上一个spring之后执行。 用于交错动画。
1 | const trail = usetrail(list.length, { ... }); |
对于此示例,配置大致如下所示:
1 2 3 4 5 6 | const [trail, set] = usetrail(imgList.length, () => ({ opacity: 1, ... from: { opacity: 0, ... } })); |
将线索传递到组件中:
1 2 3 4 5 6 | const [trail, set] = usetrail(imgList.length, () => ({ opacity: 1, ... from: { opacity: 0, ... } })); |
在此示例中,您应该注意的是一组值的插值,它允许您一次更改多个变换函数的
1 | transform: x.interpolate(x => `translateX(${x}px)`) |
但是原理是一样的。
结果,我们得到了一个轨迹动画,该动画更改了transform(
useTransition
1 | import { Animated, useTransition } from"React Spring"; |
useTransition允许您创建动画过渡组。 它接受列表的元素,其键和生命周期。 动画在元素出现和消失时触发。
您可以将转换用于阵列,在组件之间切换或用于同一组件的安装/卸载。
<iframex style =" BORDER-BOTtoM:0像素; BORDER-LEFT:0像素;宽度:100%;高度:500像素;溢出:隐藏; BORDER-toP:0像素; BORDER-RIGHT:0像素;边框半径:4像素" title = react-spring-useTransition src =" https://codesandbox.io/embed/react-spring-useTransition-iz27j?fontsize=14&hidenavigation=1&theme=dark&view=preview" sandbox =" allow-modals allow-forms allow-popups allow- 脚本allow-same-origin"> </iframex>
让我们借助
1 2 3 4 5 6 7 8 9 | const [[index, dir], setindex] = usestate([0, 0]); const transitions = useTransition(slides[index], item => item.url, { from: { opacity: 0, transform: `translate3d(${dir === 1 ? 100 : -100}%,0,0) scale(0.5)` }, enter: { ... }, leave: { ... } }); |
使用已经熟悉的原理,我们将列表的长度传输到
让我们将配置传递给组件:
1 2 3 4 5 6 7 8 | {transitions.map(({ item, prop, key }) => ( <Box key={key}> <Slide style={prop} background={`url(${item.url}`} /> </Box> ))} |
单击箭头控件时,
1 2 | slideLeft()}/> slideRight()}/> |
其中
1 2 | const slideLeft = () => setindex([(index - 1 + slides.length) % slides.length, -1]); const slideRight = () => setindex([(index + 1) % slides.length, 1]); |
结果,我们得到了一个滑块,可以在其中安装/卸载幻灯片,并在转换
控制子弹使用已经熟悉的
useChain
useChain允许您设置先前定义的动画挂钩的执行顺序。 为此,您需要使用refs,这将随后阻止动画的独立执行。
1 | import { Animated, useChain } from"React Spring"; |
让我们使用此挂钩为汉堡菜单元素设置动画。 在我们的例子中,首先将执行
<iframex style =" BORDER-BOTtoM:0像素; BORDER-LEFT:0像素;宽度:100%;高度:500像素;溢出:隐藏; BORDER-toP:0像素; BORDER-RIGHT:0像素;边框半径:4像素" title = react-spring-useChain src =" https://codesandbox.io/embed/react-spring-useChain-l07f8?fontsize=14&hidenavigation=1&theme=dark&view=preview" sandbox =" allow-modals allow-forms allow-popups allow- 脚本allow-same-origin"> </iframex>
为
1 2 3 4 5 | const springRef = useRef(); const { ... , ...springProps } = useSpring({ ref: springRef, ... }); |
现在我们要做的就是用
1 2 3 4 | useChain( open ? [springRef, springsRef] : [springsRef, springRef], open ? [0, 0.25] : [0, 0.75] ); |
在此示例中,如果状态为
设置动画顺序后,还可以指定
奖金
使用已经熟悉的React Spring钩子的一些奖励示例:
手风琴– useSpring
<iframex style =" BORDER-BOTtoM:0像素; BORDER-LEFT:0像素;宽度:100%;高度:500像素;溢出:隐藏; BORDER-toP:0像素; BORDER-RIGHT:0像素;边框半径:4像素" title = react-spring-accordion src =" https://codesandbox.io/embed/react-spring-accordion-ymljk?fontsize=14&hidenavigation=1&theme=dark&view=preview" sandbox =" allow-modals allow-forms allow-popups allow- 脚本allow-same-origin"> </iframex>
卡清单– useSprings
<iframex style =" BORDER-BOTtoM:0像素; BORDER-LEFT:0像素;宽度:100%;高度:500像素;溢出:隐藏; BORDER-toP:0像素; BORDER-RIGHT:0像素;边框半径:4像素" title = react-spring-cards-list src =" https://codesandbox.io/embed/react-spring-cards-list-9onoj?fontsize=14&hidenavigation=1&theme=dark&view=preview" sandbox =" allow-modals allow-forms allow -popups allow-scripts allow-same-origin"> </iframex>
图片库– useChain
<iframex style =" BORDER-BOTtoM:0像素; BORDER-LEFT:0像素;宽度:100%;高度:500像素;溢出:隐藏; BORDER-toP:0像素; BORDER-RIGHT:0像素;边框半径:4像素" title = react-spring-image-gallery src =" https://codesandbox.io/embed/react-spring-image-gallery-rnt6t?fontsize=14&hidenavigation=1&theme=dark&view=preview" sandbox =" allow-modals allow-forms allow -popups allow-scripts allow-same-origin"> </iframex> 要带走的要点
React Spring库的优势:
动画基于物理学。 无需(除非您有意地想要)自定义持续时间或缓动。 结果是平滑,柔和和自然的动画。
易于使用且清晰的文档。
React Spring的创建者Paul Henschel的CodeSandbox页面上的文档+中提供了许多有趣而美丽的演示。
如有必要,您可以使用use-gesture中的一组非常有用的钩子。
库存储库正在不断更新和维护。
图书馆周围形成了一个很小但很活跃的社区。
保罗·亨舍尔(Paul Henschel)经常在他的Twitter页面上分享有趣的见解,演示以及更多内容。
React Spring库的缺点:
没有简单有效的循环动画的方法,文档中的示例建议使用无限循环,实际上会导致严重的性能问题:
1 2 3 4 5 6 | const { value } = useSpring({ to: async next => { while (1) await next({ ... }) }, ... }) |
正如我们已经提到的,React Spring的另一个非常不幸的局限性是不可能在单个道具中为
另外,该库不允许您为
现在,您已经了解了在React中使用动画的一种相对新的相对简单的方法,请尝试对应用程序的各个方面进行动画处理。 我们开始做吧!