プログレスバーの作り方(React / Gatsby / Next.js)
2021.05.11
この記事は約4分で読めます
この記事の筆者:三好アキ
🔹 専門用語なしでプログラミングを教えるメソッドに定評があり、1200人以上のビギナーを、最新のフロントエンド開発入門に成功させる。
🔹 Amazonベストセラー1位を複数回獲得している『はじめてつくるReactアプリ with TypeScript』著者。
Amazon著者ページはこちら → amazon.co.jp/stores/author/B099Z51QF2
React、Next.js、TypeScriptなどのお役立ち情報や実践的コンテンツを、ビギナー向けにかみ砕いて無料配信中。登録はこちらから → 無料メルマガ登録
プログレスバーとは?
このページの上部には下のように青色のバーが出ています。
これは「プログレスバー」と呼ばれるもので、ユーザーがスクロールするのに合わせて長さが伸びていき、ページの残りの量を知らせてくれます。
ブラウザの右横のスクロールバーはデスクトップでは常時出ているケースが多いですが、モバイルではスクロールしている時以外は消えています。
そのためプログレスバーは、特にモバイルのユーザーへのUX対策として多くのウェブサイトで導入されているのです。
この記事ではReactのプログレスバーを作っていきます。
記事ページなどのように、記事の文字数や分量によってページの長さが異なるケースに対応するプログレスバーです。
もちろんReactなので、GastbyやNext.jsでも使用できます。
制作するコード
先出しすると、下がこれから作るコードの全体像です。
説明を読んでいる時間のない人は、これをコピーして使ってください。
// progressBar.js
import React, { useState, useEffect, useRef } from 'react'
import { useScrollPosition } from "./useScrollPosition"
const ProgressBar = ({ children }) => {
const [height, setHeight] = useState(0)
const ref = useRef(null)
useEffect(() => {
setHeight(ref.current.clientHeight)
}, [])
const scrollPosition = useScrollPosition()
const percentage = (1 - ((height - scrollPosition) / height)) * 100
const adjustPercentage = 100 < percentage ? 100 : percentage
return (
<div ref={ref}>
<div className="progress-bar-container">
<div style={{width: `${adjustPercentage}%`}}></div>
</div>
{children}
</div>
)
}
export default ProgressBar
// useScrollPosition.js
import { useState, useEffect } from 'react'
export const useScrollPosition = () => {
const [scrollPosition, setScrollPosition] = useState(0)
const handleScroll = () => {
const position = window.pageYOffset
setScrollPosition(position)
}
useEffect(() => {
window.addEventListener('scroll', handleScroll, { passive: true })
return () => {
window.removeEventListener('scroll', handleScroll)
}
}, [])
return scrollPosition
}
// progressBar.scss
.progress-bar-container {
position: fixed;
top: 100px;
height: 3px;
width: 100%;
z-index: 10;
div {
height: 100%;
background-color: rgb(0, 255, 255);
opacity: 0.7;
}
}
必要なもの
プログレスバーに必要なものは次の3つです。
• 現在のスクロール位置
• ページのheight
• スクロールの進み具合のパーセント( =「height」から「現在のスクロール位置」を引き、それを「height」で割ったもの)
それではそれぞれ作っていきましょう。
スクロールを位置を求めるカスタムHookを作る
最初に、現在のスクロール位置を特定するファンクションをカスタムHookとして作ります。
以下のコードになります。
// useScrollPosition.js
import { useState, useEffect } from 'react'
export const useScrollPosition = () => {
const [scrollPosition, setScrollPosition] = useState(0)
const handleScroll = () => {
const position = window.pageYOffset
setScrollPosition(position)
}
useEffect(() => {
window.addEventListener('scroll', handleScroll, { passive: true })
return () => {
window.removeEventListener('scroll', handleScroll)
}
}, [])
return scrollPosition
}
これで現在のユーザーのスクロール位置がわかるようになりました。
progressBar.jsを作る
次にプログレスバーのファイル、progressBar.js
を作りましょう。
そこにベースとなる次のコードを打ちます。
// progressBar.js
import React, { useState, useEffect, useRef } from 'react'
const ProgressBar = () => {
return (
<div>
</div>
)
}
export default ProgressBar
ページのheightを求める
次にref
とuseState
、useEffect
を使ってページの高さを求めます。
ここではブログの記事ページなどでの使用を想定しており、記事の文章量によってページの長さも変わってくるのでref
が必要です。
// progressBar.js
import React, { useState, useEffect, useRef } from 'react'
const ProgressBar = ({ children }) => {
const [height, setHeight] = useState(0)
const ref = useRef(null)
useEffect(() => {
setHeight(ref.current.clientHeight)
}, [])
return (
<div ref={ref}>
</div>
)
}
export default ProgressBar
スクロールの進み具合を求める
先ほど作ったuseScrollPosition
を使って、スクロールの進み具合を出します。
// progressBar.js
import React, { useState, useEffect, useRef } from 'react'
import { useScrollPosition } from "./useScrollPosition"
const ProgressBar = ({ children }) => {
const [height, setHeight] = useState(0)
const ref = useRef(null)
useEffect(() => {
setHeight(ref.current.clientHeight)
}, [])
const scrollPosition = useScrollPosition()
const percentage = (1 - ((height - scrollPosition) / height)) * 100
const adjustPercentage = 100 < percentage ? 100 : percentage
return (
<div ref={ref}>
</div>
)
}
export default ProgressBar
上のコードのadjustPercentage
がスクロールの進み具合になり、100 < percentage ? 100 : percentage
でパーセントが100を超えないよう制御しています。
adjustPercentage
の数値をページの幅(width)に設定すると、スクロールする毎にバーが伸びていきます。
// progressBar.js
...
return (
<div ref={ref}>
<div className="progress-bar-container">
<div style={{width: `${adjustPercentage}%`}}></div>
</div>
</div>
)
...
そしてprogressBar
コンポーネントは、使いたいページのコンポーネントを包むように配置する必要があるのでchildren
が必要になります。
progressBar.js
全体は以下のようになっています。
// progressBar.js
import React, { useState, useEffect, useRef } from 'react'
import { useScrollPosition } from "../hooks/useScrollPosition"
const ProgressBar = ({ children }) => {
const [height, setHeight] = useState(0)
const ref = useRef(null)
useEffect(() => {
setHeight(ref.current.clientHeight)
}, [])
const scrollPosition = useScrollPosition()
const percentage = (1 - ((height - scrollPosition) / height)) * 100
const adjustPercentage = 100 < percentage ? 100 : percentage
return (
<div ref={ref}>
<div className="progress-bar-container">
<div style={{width: `${adjustPercentage}%`}}></div>
</div>
{children}
</div>
)
}
export default ProgressBar
CSSを書く
最後にCSSを書きましょう。
ここではSCSSを使っています。
// progressBar.scss
.progress-bar-container {
position: fixed;
top: 100px;
height: 3px;
width: 100%;
z-index: 10;
div {
height: 100%;
background-color: rgb(0, 255, 255);
opacity: 0.7;
}
}
progressBar.jsの使い方
これを下のように使いたいページで読み込みます。
ここではブログ記事のコンポーネントに読み込むことを想定しています。
// singlePost.js
import React from 'react'
import Layout from "./layout"
import ProgressBar from "./progressBar"
import PostBody from "./postBlody"
import LatestPosts from "./latestPosts"
const SinglePost =() => {
return (
<Layout>
<ProgressBar>
<PostBlody/>
<latestPosts/>
</ProgressBar>
</Layout>
)
}
export default SinglePost
メルマガ配信中
(from 三好アキ/エンジニア)
React、Next.js、TypeScriptなど最新のウェブ開発のお役立ち情報を、ビギナー向けにかみ砕いて無料配信中。
(*配信はいつでも停止できます)