

快速总结
如果操作得当,css的动画也是非常强大的。然而,用CSS创建耳目一新的动画是很棘手的。接下来是 Framer Motion的到来。有了Framer Motion,你不需要成为一个CSS专家来制作花里胡哨的动画。Framer Motion为我们提供了开箱即用的动画和低成本API,我们可以恰当使用,将这些动画集成到我们的应用程序中。
在这篇文章中,我们将仔细看看Framer Motion如何帮助我们创建令人生畏的动画。我们将学习Framer Motion如何提高生产力。我们将研究如何使用Framer运动制作手势触发、定时和滚动动画。在此过程中,我们将使用我们所学到的东西来构建五个演示应用程序。
本教程将有提升那些对在React.js应用程序中集成动画的读者兴趣。
注意:本文要求对React和CSS有基本的了解。
什么是Framer Motion?
Framer Motion github
Framer Motion 文档


Framer Motion是一个动画库,使创建动画变得容易。它简化的API帮助我们抽象动画背后的复杂性,并允许我们轻松地创建动画。
Framer Motion的使用
使用Framer Motion动画化一个h1标签。首先,我们安装帧运动库并导入。我们在其中定义了要制作动画的组件的属性。当组件挂载到DOM中时,我们定义的属性将被动画化。
1 2
| npm i framer-motion import { motion } from 'framer-motion';
|
然后我们将h1转换为运动组件
1 2 3
| <motion.h1 animate={{x: 20, y: -20}}> </motion.h1>
|
这将导致h1在加载时向右滑动20px并向上移动20px。当没有添加单位时,使用像素进行计算。但是,您可以显式设置计算所基于的单位
1
| animate={{x: "20rem ",y: "-20rem"}}>
|
如果我们想让h1从左边进来,我们用initial来控制它。
1 2 3 4
| <motion.h1 initial={{x: -1000}} animate={{x: 20}}> </motion.h1>
|
Framer Motion不局限于单一的动画。我们可以在值数组中定义一系列称为关键帧的动画。每个值将按顺序动画化。
1 2 3 4
| <motion.h1 initial={{x: -1000}} animate={{x: [20, 50, 0, -70, 40] }}> </motion.h1>
|
transition过渡属性允许我们定义动画以何种状态发生。使用它,我们定义值如何从一个状态移动到另一个状态。除此之外,我们可以使用这个属性定义动画的持续时间、延迟和类型。
1 2 3 4 5 6 7 8 9
| <motion.h1 initial={{ x: -1000 }} animate={{ x: 0 }} transition={{ type: "tween", duration: "2", delay: "1" }}> </motion.h1>
|
假设我们同时为几个运动组件制作动画,如下面的代码片段所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| <div className="App"> <motion.h1 initial={{ x: -1000 }} animate={{ x: 0 }} transition={{ type: "tween", duration: "2", delay: "1" }}> </motion.h1> <motion.h2 initial={{ y: -1000 }} animate={{ y: 0 }} transition={{ type: "tween", duration: "1", delay: ".4" }}> </motion.h2> <motion.h3 initial={{ x: 100, opacity: 0 }} animate={{ x: 0, opacity: 1 }}> </motion.h3> <motion.h4 initial={{ scale: 0.7 }} animate={{ scale: 1.7 }} transition={{ type: "tween", duration: "2", delay: "1" }}> </motion.h4> </div>
|
当这工作时,Framer Motion中的变量使我们能够将动画定义提取到变量对象中。变体不仅使我们的代码更清晰,而且允许我们创建更强大和复杂的动画。
将我们的动画定义提取到变量对象中,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| const H1Variants = { initial: { x: -1000 }, animate: { x: 0 }, transition: { type: "tween", duration: 2, delay: 1 } } const H2Variants = { initial: { y: -1000 }, animate: { y: 0 }, transition: { type: "tween", duration: 1, delay: .4 } } const H3Variants = { initial:{ x: 100, opacity: 0 }, animate:{ x: 0, opacity: 1 } } const H4Variants = { initial:{ scale: 0.7 }, animate:{ scale: 1.7 }, transition:{ type: "tween", duration: "2", delay: "1" } }
|
我们没有将动画定义直接传递到组件的initial和animate的props中,而是将这些定义提取到独立的变体对象中。在变体对象中,我们定义变体名称,将每个动画的名称描述为变体。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| <div className="App"> <motion.h1 variants={H1Variants} initial='initial' animate='animate' > </motion.h1> <motion.h2 variants={H2Variants} initial='initial' animate='animate' > </motion.h2> <motion.h3 variants={H3Variants} initial='initial' animate='animate' > </motion.h3> <motion.h4 variants={H4Variants} initial='initial' animate='animate' > </motion.h4> </div>
|
在variants prop中,我们为每个运动组件传递变量对象的名称,然后将动画传递给initial props和animate props。
我们可以进一步利用现有的变体来减少重复。使用变量,我们可以通过DOM从父动作组件向下传播动画属性。为了实现这一点,我们为父运动创建了motion.div在其变体对象中具有与其子对象相似的动画名称。通过这样做,我们将不必将动画名称传递给每个子组件,同时父元素为我们处理这些。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| const ContainerVariants = { initial: {}, animate: {} }; const H1Variants = { initial: { x: -1000 }, animate: { x: 0 }, transition: { type: "tween", duration: 2, delay: 1 } };
<motion.div className="App" variants={ContainerVariants} initial="initial" animate="animate" > <motion.h1 variants={H1Variants}>h1</motion.h1> <motion.h2 variants={H2Variants}>h2</motion.h2> <motion.h3 variants={H3Variants}>h3</motion.h3> <motion.h4 variants={H4Variants}>h4</motion.h4> </motion.div>
|
现在我们了解了Framer Motion的基本原理。让我们开始
构建一个演示应用–图标列表

组件
App.js
:保存标题文本。
Card.jsx
:在这里,我们定义了图标卡的动画。
CardContainer.jsx
:我们导入并循环图标。
styles.js
:创建、样式化和导出运动组件。我使用样式组件对组件进行样式化。
从App.js开始
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import { H1, H2 } from "./Styles"; import CardContainer from "./CardContainer";
return ( <div> <H1 initial={{ y: -100 }} animate={{ y: 0, transition: { delay: 1 } }}> </H1> <H2 initial={{ x: -1000 }} animate={{ x: 0, transition: { delay: 1 } }}> </H2> <CardContainer /> </div> );
|
导入在Styles.js文件中创建H1和H2的motion组件。由于它们是motion组件,我们使用initial和animate props来定义它们在mount之前和mount时的行为。在这里,我们还导入并显示CardContiner组件。
现在是CardContainer.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import { Container } from "./Styles"; import Card from "./Card"; import { ReactComponent as AddIcon } from "./assets/add.svg"; import { ReactComponent as AirplaneIcon } from "./assets/airplane.svg"; import { ReactComponent as AlarmIcon } from "./assets/alarm.svg";
const icons = [ <AddIcon />, <AirplaneIcon />, <AlarmIcon />, ];
const CardContainer = () => { return ( <Container initial={{ x: -1000 }} animate={{ x: 0 }}> {icons.map((icon) => ( <Card icon={icon} /> ))} </Container> ); };
|
在这里,我们导入SVGs、Container motion组件和Card组件。
类似于App.js中的H1和H2,我们使用initial和animate props定义Container的动画。当它加载时,它将创建一个基础的效果,从浏览器的左边滑动。
现在Card.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| import { CardBox, IconBox } from "./Styles"; const CardVariants = { beforeHover: {}, onHover: { scale: 1.1 } }; const IconVariants = { beforeHover: { opacity: 0, y: -50 }, onHover: { opacity: 1, y: 0, scale: 1.5, transition: { type: "tween" } } };
const Card = ({ icon }) => { return ( <CardBox variants={CardVariants} initial="beforeHover" whileHover="onHover"> <IconBox variants={IconVariants}>{icon}</IconBox> </CardBox> ); };
|
在这里,我们用beforeHover和onHover动画创建了两个不同的对象。在CardVariants对象中,我们一开始不想做任何逻辑,因此beforeHover是一个空对象。onHover时,我们增加card box的尺寸。
在IconVariants对象中,我们在其beforeHover中定义了IconBox的初始状态。我们设置它的不透明度为0,并向上推50px。然后,在onHover中,我们将不透明度设置为1,将其推回默认位置,并将过渡类型更改为tween。然后我们将变量传递给它们各自的运动组件。我们使用了propagation,所以我们不需要显式地为IconBox组件设置initial和animate props
尾声
我们已经了解了Framer Motion的基础知识,并看到了演示项目,使我们一瞥了可以创建的动画范围。然而,你可以用它做更多的事情。钻研文档,尽情发挥。