很 fancy 的效果。
簡述
這是最近碰到的一個套件,因為覺得還不錯用所以順便記錄一下。
想知道更多細節可以到 官方文件 來看,我覺得寫得很不錯,推一個!
用法
1. 建立 CSS 動畫
因為「轉圈圈」的元件是透過 CSS 動畫來實作的,所以要記得開一支 CSS 來寫入以下內容:
1 2 3 4 5 6 7 8
| @keyframes spinner { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
|
2. 建立所需的元件
簡單來說我們要建立一個 Progress
元件,這個元件中還會包含其他元件,分別為 Bar
、Container
和 Spinner
,不太懂的話直接 code 吧:
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
| import { useNProgress } from '@tanem/react-nprogress'
import Bar from './Bar' import Container from './Container' import Spinner from './Spinner'
export default function Progress({ animationDuration, incrementDuration, isAnimating, minimum }) { const { isFinished, progress } = useNProgress({ animationDuration, incrementDuration, isAnimating, minimum })
return ( <Container animationDuration={animationDuration} isFinished={isFinished}> <Bar animationDuration={animationDuration} progress={progress} /> <Spinner /> </Container> ) }
|
簡單來說就是把 Progress 接收的 props 傳給 useNProgress
(當作 confi),然後再把對應的 state 放到 template 上就行了。
至於 template 的內容我一併附上:
Container.jsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import React from 'react'
const Container = ({ animationDuration, children, isFinished }) => ( <div style={{ opacity: isFinished ? 0 : 1, pointerEvents: 'none', transition: `opacity ${animationDuration}ms linear` }} > {children} </div> )
export default Container
|
Bar.jsx
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
| const Bar = ({ animationDuration, progress }) => ( <div style={{ background: '#29d', height: 2, left: 0, marginLeft: `${(-1 + progress) * 100}%`, position: 'fixed', top: 0, transition: `margin-left ${animationDuration}ms linear`, width: '100%', zIndex: 1031 }} > <div style={{ boxShadow: '0 0 10px #29d, 0 0 5px #29d', display: 'block', height: '100%', opacity: 1, position: 'absolute', right: 0, transform: 'rotate(3deg) translate(0px, -4px)', width: 100 }} /> </div> )
export default Bar
|
Spinner.jsx
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
| import React from 'react'
const Spinner = () => ( <div style={{ display: 'block', position: 'fixed', right: 15, top: 15, zIndex: 1031 }} > <div style={{ animation: '400ms linear infinite spinner', borderBottom: '2px solid transparent', borderLeft: '2px solid #29d', borderRadius: '50%', borderRight: '2px solid transparent', borderTop: '2px solid #29d', boxSizing: 'border-box', height: 18, width: 18 }} /> </div> )
|
3. 拿來用(結束)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import './Home.css' import React from 'react' import { useFetch } from 'hooks/useFetch' import RecipeList from 'components/RecipeList' import Progress, { progressProps } from 'components/Progress'
export default function Home() { const { data, isPending, error } = useFetch('http://localhost:3000/recipes') return ( <div className='home'> <Progress isAnimating={isPending} animationDuration={progressProps.animationDuration} incrementDuration={progressProps.incrementDuration} minimum={0} /> {isPending && <div className='loading'>Loading...</div>} {error && <div className='error'>{error}</div>} {data && <RecipeList recipes={data} />} </div> ) }
|
只要把 Ajax 的 loading
狀態當作 props 傳入就大功告成囉~
補充:每個 props 的用途
isAnimating: boolean
是否顯示進度條
animationDuration: number
從開始到結束的時間 (ms)
incrementDuration: number
進度條遞增的間隔時間(ms)
minimum: number
進度條的起始點(0~1)