快速总结

如果操作得当,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";
// 更多svg

const icons = [
<AddIcon />,
<AirplaneIcon />,
<AlarmIcon />,
// 更多icon
];

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的基础知识,并看到了演示项目,使我们一瞥了可以创建的动画范围。然而,你可以用它做更多的事情。钻研文档,尽情发挥。