很 fancy 的效果。
簡述
這是最近碰到的一個套件,因為覺得還不錯用所以順便記錄一下。
想知道更多細節可以到 官方文件 來看,我覺得寫得很不錯,推一個!
用法
1. 建立 CSS 動畫
因為「轉圈圈」的元件是透過 CSS 動畫來實作的,所以要記得開一支 CSS 來寫入以下內容:
| 12
 3
 4
 5
 6
 7
 8
 
 | @keyframes spinner {0% {
 transform: rotate(0deg);
 }
 100% {
 transform: rotate(360deg);
 }
 }
 
 | 
2. 建立所需的元件
簡單來說我們要建立一個 Progress 元件,這個元件中還會包含其他元件,分別為 Bar、Container 和 Spinner,不太懂的話直接 code 吧:
| 12
 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
| 12
 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
| 12
 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
| 12
 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. 拿來用(結束)
| 12
 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)