前言
相信很多前端同学都或多或少和动画
打过交道。有的时候是产品想要的过度效果
;有的时候是UI想要的酷炫动画
。但是有没有人考虑过,是不是我们的页面上面的每一次变化,都可以像是自然而然的变化;是不是每一次用户点击所产生的交互,都可以在页面上活过来呢?
欢迎你打开了新的前端动画世界——《Framer Motion》
效果体验
这里,我在framer官网上面给大家录制了一下大概的使用效果。
在我们的常规认知中,实现这样的效果其实需要很多的css来实现,或者说需要我们进行大量的定制化逻辑
编写。但是如果我们使用framer motion
的话,只需要如下代码:
import { AnimatePresence, motion } from 'framer-motion';const [selectedId, setSelectedId] = useState(null);{items.map(item => ( setSelectedId(item.id)}> {item.subtitle} {item.title} ))} {selectedId && ( {item.subtitle} {item.title} setSelectedId(null)} /> )}
从上面的实现我们可以看出,framer-motion可以说是我们在用react动效开发过程中的必备利器
。那么接下来,我给大家简单介绍一些framer motion的基础用法。
快速开始
Framer Motion 需要 React 18 或更高版本。
安装
从npm安装framer-motion
npm install framer-motion
输入
安装后,您可以通过framer-motion
引入Framer Motion
import { motion } from "framer-motion"export const MyComponent = ({ isVisible }) => ( )
使用方式
Framer motion
的核心API是motion
的组件。每个HTML
和SVG
标签都有对应的motion
组件。
他们渲染的结果与对应的原生组件完全一致,并在其之上增加了一些动画和手势相关的props
。
比如:
...
示例
比如我们现在想要实现一个侧边栏效果。
节点的挂载与卸载(mount、unmount)
如果我们自己来实现的话,可能要考虑它的keyframe
,它的初始状态
与最终的css样式
。那么如果用framer-motion
来如何实现呢?
首先我们来设计一个会动
的按钮Icon:
import * as React from "react";import { motion } from "framer-motion";const Path = props => ( );const MenuToggle = ({ toggle }) => ( );
接下来,就由这个按钮来控制侧边栏的展示(mount)
与隐藏(unmount)
:
import * as React from "react";import { useRef } from "react";import { motion, useCycle } from "framer-motion";import { useDimensions } from "./use-dimensions";const sidebar = { open: (height = 1000) => ({ clipPath: `circle(${height * 2 + 200}px at 40px 40px)`, transition: { type: "spring", stiffness: 20, restDelta: 2 } }), closed: { clipPath: "circle(30px at 40px 40px)", transition: { delay: 0.5, type: "spring", stiffness: 400, damping: 40 } }};export const Example = () => { const [isOpen, toggleOpen] = useCycle(false, true); const containerRef = useRef(null); const { height } = useDimensions(containerRef); return ( toggleOpen()} /> );};
也就是说,其实我们更多需要做的事情,从思考如何设计各元素之间的css联动与keyframe书写
变成了如何按照文档写好framer-motion的配置
。哪个更轻松相信大家一目了然。
列表
侧边栏一般都是带有菜单的,那么我们是不是可以让这个侧边栏也有一个逐次出现
的效果呢?就像这样:
这里我们是不是已经开始肌肉记忆
般的计算延迟时间
,思考如何进行整体效果的分配。那么如果这里我们使用frame motion
,它的实现方式应该是怎么样的呢?
首先我们先来进行单个Item
的封装:
import * as React from "react";import { motion } from "framer-motion";const variants = { open: { y: 0, opacity: 1, transition: { y: { stiffness: 1000, velocity: -100 } } }, closed: { y: 50, opacity: 0, transition: { y: { stiffness: 1000 } } }};const colors = ["#FF008C", "#D309E1", "#9C1AFF", "#7700FF", "#4400FF"]; export const MenuItem = ({ i }) => { const style = { border: `2px solid ${colors[i]}` }; return ( );};
然后我们在已封装Item
的基础上,再进行整个菜单的封装:
import * as React from "react";import { motion } from "framer-motion";const itemIds = [0, 1, 2, 3, 4];const variants = { open: { transition: { staggerChildren: 0.07, delayChildren: 0.2 } }, closed: { transition: { staggerChildren: 0.05, staggerDirection: -1 } }};export const Navigation = () => ( {itemIds.map(i => ( ))} );
没错,动画!就是这么简单!
更多API
更详细、更具体的功能大家可以参考下官方的使用文档,我就不在这里一一列举了。
美中不足
其实不难看出,不论是实现的效果,还是使用方式,对于前端的同学来说framer-motion
都是非常友好的工具。这一点从npm的Weekly Downloads
以及github的star
上面都不难看出。
但是目前也有一个问题,那就是包的体积
问题。
这个包的大小对于部分的系统来说,还是不够友好。这也是很多人不选择使用它的原因。