React動畫實現(xiàn)方案Framer Motion讓頁面自己動起來
前言
相信很多前端同學都或多或少和動畫打過交道。有的時候是產(chǎn)品想要的過度效果;有的時候是UI想要的酷炫動畫。但是有沒有人考慮過,是不是我們的頁面上面的每一次變化,都可以像是自然而然的變化;是不是每一次用戶點擊所產(chǎn)生的交互,都可以在頁面上活過來呢?
歡迎你打開了新的前端動畫世界——《Framer Motion》
效果體驗
這里,我在framer官網(wǎng)上面給大家錄制了一下大概的使用效果。
在我們的常規(guī)認知中,實現(xiàn)這樣的效果其實需要很多的css來實現(xiàn),或者說需要我們進行大量的定制化邏輯編寫。但是如果我們使用framer motion
的話,只需要如下代碼:
import { AnimatePresence, motion } from 'framer-motion'; const [selectedId, setSelectedId] = useState(null); {items.map(item => ( <motion.div layoutId={item.id} onClick={() => setSelectedId(item.id)}> <motion.h5>{item.subtitle}</motion.h5> <motion.h2>{item.title}</motion.h2> </motion.div> ))} <AnimatePresence> {selectedId && ( <motion.div layoutId={selectedId}> <motion.h5>{item.subtitle}</motion.h5> <motion.h2>{item.title}</motion.h2> <motion.button onClick={() => setSelectedId(null)} /> </motion.div> )} </AnimatePresence>
從上面的實現(xiàn)我們可以看出,framer-motion可以說是我們在用react動效開發(fā)過程中的必備利器。那么接下來,我給大家簡單介紹一些framer motion的基礎(chǔ)用法。
快速開始
Framer Motion 需要 React 18 或更高版本。
安裝
從npm安裝framer-motion
npm install framer-motion
輸入
安裝后,您可以通過framer-motion
引入Framer Motion
import { motion } from "framer-motion" export const MyComponent = ({ isVisible }) => ( <motion.div animate={{ opacity: isVisible ? 1 : 0 }} /> )
使用方式
Framer motion
的核心API是motion
的組件。每個HTML
和SVG
標簽都有對應(yīng)的motion
組件。
他們渲染的結(jié)果與對應(yīng)的原生組件完全一致,并在其之上增加了一些動畫和手勢相關(guān)的props
。
比如:
<motion.div /> <motion.span /> <motion.h1 /> <motion.svg /> ...
示例
比如我們現(xiàn)在想要實現(xiàn)一個側(cè)邊欄效果。
節(jié)點的掛載與卸載(mount、unmount)
如果我們自己來實現(xiàn)的話,可能要考慮它的keyframe,它的初始狀態(tài)與最終的css樣式。那么如果用framer-motion
來如何實現(xiàn)呢?
首先我們來設(shè)計一個會動的按鈕Icon:
import * as React from "react"; import { motion } from "framer-motion"; const Path = props => ( <motion.path fill="transparent" strokeWidth="3" stroke="hsl(0, 0%, 18%)" strokeLinecap="round" {...props} /> ); const MenuToggle = ({ toggle }) => ( <button onClick={toggle}> <svg width="23" height="23" viewBox="0 0 23 23"> <Path variants={{ closed: { d: "M 2 2.5 L 20 2.5" }, open: { d: "M 3 16.5 L 17 2.5" } }} /> <Path d="M 2 9.423 L 20 9.423" variants={{ closed: { opacity: 1 }, open: { opacity: 0 } }} transition={{ duration: 0.1 }} /> <Path variants={{ closed: { d: "M 2 16.346 L 20 16.346" }, open: { d: "M 3 2.5 L 17 16.346" } }} /> </svg> </button> );
接下來,就由這個按鈕來控制側(cè)邊欄的展示(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 ( <motion.nav initial={false} animate={isOpen ? "open" : "closed"} custom={height} ref={containerRef} > <motion.div className="background" variants={sidebar} /> <MenuToggle toggle={() => toggleOpen()} /> </motion.nav> ); };
也就是說,其實我們更多需要做的事情,從思考如何設(shè)計各元素之間的css聯(lián)動與keyframe書寫變成了如何按照文檔寫好framer-motion的配置。哪個更輕松相信大家一目了然。
列表
側(cè)邊欄一般都是帶有菜單的,那么我們是不是可以讓這個側(cè)邊欄也有一個逐次出現(xiàn)的效果呢?就像這樣:
這里我們是不是已經(jīng)開始肌肉記憶般的計算延遲時間,思考如何進行整體效果的分配。那么如果這里我們使用frame motion
,它的實現(xiàn)方式應(yīng)該是怎么樣的呢?
首先我們先來進行單個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 ( <motion.li variants={variants} whileHover={{ scale: 1.1 }} whileTap={{ scale: 0.95 }} > <div className="icon-placeholder" style={style} /> <div className="text-placeholder" style={style} /> </motion.li> ); };
然后我們在已封裝Item
的基礎(chǔ)上,再進行整個菜單的封裝:
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 = () => ( <motion.ul variants={variants}> {itemIds.map(i => ( <MenuItem i={i} key={i} /> ))} </motion.ul> );
沒錯,動畫!就是這么簡單!
更多API
更詳細、更具體的功能大家可以參考下官方的使用文檔,我就不在這里一一列舉了。
美中不足
其實不難看出,不論是實現(xiàn)的效果,還是使用方式,對于前端的同學來說framer-motion
都是非常友好的工具。這一點從npm的Weekly Downloads
以及github的star
上面都不難看出。
但是目前也有一個問題,那就是包的體積問題。
這個包的大小對于部分的系統(tǒng)來說,還是不夠友好。這也是很多人不選擇使用它的原因。
以上就是React動畫實現(xiàn)方案Framer Motion讓頁面自己動起來的詳細內(nèi)容,更多關(guān)于React 動畫 Framer Motion的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
react路由跳轉(zhuǎn)傳參刷新頁面后參數(shù)丟失的解決
這篇文章主要介紹了react路由跳轉(zhuǎn)傳參刷新頁面后參數(shù)丟失的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-06-06Remix后臺開發(fā)之remix-antd-admin配置過程
這篇文章主要為大家介紹了Remix后臺開發(fā)之remix-antd-admin配置過程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-04-04React Navigation 使用中遇到的問題小結(jié)
本篇文章主要介紹了React Navigation 使用中遇到的問題小結(jié),主要是安卓和iOS中相對不協(xié)調(diào)的地方,特此記錄,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-05-05