react-nprogress 懶人包

很 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 元件,這個元件中還會包含其他元件,分別為 BarContainerSpinner,不太懂的話直接 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 }) {
// 套件提供的 hook
// isFinished = 是否顯示進度條
// progress = 進度條當下的長度
const { isFinished, progress } = useNProgress({
animationDuration,
incrementDuration,
isAnimating,
minimum
})

// 最後回傳 template
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)
React-關於 useContext 更好的寫法 React-來捏一個 Custom hook
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×