はじめに
以下の対応をするにあたりハマったのでメモです。
- Gatsby.jsにてスクロールに連動してふわっとフェードインしたい
- 複数要素に対応させたい
実装方法
react-intersection-observerを使用する方法もあるようですが、上手く複数要素に適応させることができなかったので、IntersectionObserverAPIを使用することにしました。
useContext 使用
参考サイト:
上記サイトでIntersectionObserverAPIをuseContextを利用してどこのコンポーネントでも使いまわせるようにする方法が書いてありました!
それを参考にさせてもらいました!
srcと並列階層にproviderフォルダを作成し、IntersectionObserverProvider.jsxを作成しました。
import React, { createContext, useRef } from 'react'
export const ObserverContext = createContext({})
export function IntersectionObserverProvider(props) {
const { children } = props
const options = {
rootMargin: '-20% 0px', // ここはオプションなので任意で
once: false,
}
const targets = useRef([])
const toTargets = el => {
if (el && !targets.current?.includes(el)) {
targets.current.push(el)
}
}
return (
<>
<ObserverContext.Provider value={{ toTargets, targets }}>
{children}
</ObserverContext.Provider>
</>
)
}
参考サイトにはuseEffect以下のソースがありますが、私の環境の場合(Gatsby.jsのせいか、、?)はそれでは動かなかったので別に分けています。
ここでグローバルで使用できるtoTargetsとtargetsという変数を定義できました。
IntersectionObserverを使用する関数
参考サイト内にあるuseEffect以下のソースはutils等に別の共通関数として切り出しました。
export const handleObserver = (targets) => {
targets.current.forEach(target => {
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('show')
}
})
})
observer.observe(target)
})
}
今回はスクロールがターゲットまで来たらshowというクラスを付与する感じです。
Provider
useContextで実装したグローバルの変数を使用するには親のコンポーネントでProviderで囲む必要があります。
参考サイトはサイト全体に適応したいのでapp.jsxを親コンポーネントとしています。
ですが、Gatsby.jsにはapp.jsx的なファイルは存在しません。
GatsbyでContextを使いグローバルな状態管理を実装する方法は以下を参考にしました。
プロジェクトのルートにgatsby-browser.jsを作成し、そこに記述すれば良さそうです!
import React from "react"
import { IntersectionObserverProvider } from "./src/provider/IntersectionObserverProvider"
export const wrapRootElement = ({ element }) => (
<IntersectionObserverProvider>{element}</IntersectionObserverProvider>
)
各コンポーネント
ここからは各コンポーネントで先ほど実装したものを適応していきます。
ターゲットにしたい各要素にref={toTargets}を追記。
import React, { useEffect, useContext } from 'react'
import { ObserverContext } from "../provider/IntersectionObserverProvider";
import { handleObserver } from '../utils/IntersectionObserver'
export default () => {
const { toTargets, targets } = useContext(ObserverContext);
useEffect(() => {
handleObserver(targets)
}, [])
return (
<Layout>
<section>
<h2 ref={toTargets}>見出し</h2>
<div ref={toTargets}>
テキストテキストテキストテキストテキスト
</div>
<div ref={toTargets}>
・・・
</div>
</section>
<section>
<h2 ref={toTargets}>見出し</h2>
<p ref={toTargets}>テキストテキストテキストテキストテキスト</p>
</section>
</Layout>
)
}
全体のおおまかな構成
実際には他にファイルはありますが、上記で出てきたファイルは以下のような構成になりました。
├── gatsby-browser.js
│
├── src
│ ├─ pages
│ │ └─ index.jsx
│ │
├─ provider
│ └─ IntersectionObserverProvider.jsx
│
├─ utils
│ └─ IntersectionObserver.js
最後に
IntersectionObserverAPIはJavaScriptなのでDOMを直接操作する例が多く、Reactで利用する方法を今回探して実装しました。
多くの記事はGatsby.js用ではなかったので、いろいろと異なり参考記事通りでは上手く動かなかったので、メモとして残すことにしました。
どなたかの参考になれば嬉しいです。