LoginSignup
3
1

More than 1 year has passed since last update.

Gatsby.jsでスクロールに連動させて複数要素をフェードインさせる

Posted at

はじめに

以下の対応をするにあたりハマったのでメモです。

  • Gatsby.jsにてスクロールに連動してふわっとフェードインしたい
  • 複数要素に対応させたい

実装方法

react-intersection-observerを使用する方法もあるようですが、上手く複数要素に適応させることができなかったので、IntersectionObserverAPIを使用することにしました。

useContext 使用

参考サイト:

上記サイトでIntersectionObserverAPIをuseContextを利用してどこのコンポーネントでも使いまわせるようにする方法が書いてありました!

それを参考にさせてもらいました!
srcと並列階層にproviderフォルダを作成し、IntersectionObserverProvider.jsxを作成しました。

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等に別の共通関数として切り出しました。

utils/IntersectionObserver.js
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を作成し、そこに記述すれば良さそうです!

gatsby-browser.js
import React from "react"
import { IntersectionObserverProvider } from "./src/provider/IntersectionObserverProvider"

export const wrapRootElement = ({ element }) => (
  <IntersectionObserverProvider>{element}</IntersectionObserverProvider>
)

各コンポーネント

ここからは各コンポーネントで先ほど実装したものを適応していきます。
ターゲットにしたい各要素にref={toTargets}を追記。

/pages/index.jsx
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用ではなかったので、いろいろと異なり参考記事通りでは上手く動かなかったので、メモとして残すことにしました。

どなたかの参考になれば嬉しいです。

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1