📜
React 18(Next.js)でGSAPする
- ブルーピリオド展のような縦スクロールしても横スクロールするサイトが作りたいWEBフロントエンドエンジニアよるReactとGSAPを使った試み
- リポジトリはこちら
- 完成したサイトはこちら
環境
-
yarn create next-app --typescript
で生成 - Next.js 12.3.1
- react 18.2.0
- gsap 3.11.3
ざっくり解説
- 完成品をもとに抜き出して解説していきます
GSAPの準備
import { gsap } from 'gsap'
import { ScrollTrigger } from 'gsap/dist/ScrollTrigger'
gsap.registerPlugin(ScrollTrigger)
- 使う機能を
registerPlugin
しておく- 今回は『スクロールで発火する処理』を作るので、
ScrollTrigger
をいれておく
- 今回は『スクロールで発火する処理』を作るので、
- React内では
/dist/
つきのパスを読み込む必要がある
GSAPをhookするためのrefを作成する
-
setupGsap
はGSAPの設定のための関数。次で解説する。- GSAPが必要とするのは
current
の中身の方なので、current
の中身だけ取り出して渡しておくと良い
- GSAPが必要とするのは
- React18からデフォルトでマウントが2回実施されるようになったが、GSAPのセットアップは1回でないと困るので
didEffect
のようにして1回にする。
const pagesWrapperRef = useRef<HTMLDivElement | null >(null);
const pagesRef = useRef<HTMLDivElement | null >(null);
const didEffect = React.useRef(false);
useEffect(() => {
if (didEffect.current) return
didEffect.current = true;
const pagesElement = pagesRef?.current;
if(!pagesElement) return
const pagesWrapperElement = pagesWrapperRef?.current;
if(!pagesWrapperElement) return
setupGsap(pagesElement, pagesWrapperElement)
}, [])
縦スクロールを横スクロールにするGSAPを設定する
-
setupGSap
という関数にまとめておく- 関数化してるのは趣味なので
useEffect
にべた書きでも問題ない。お好みでどうぞ。
- 関数化してるのは趣味なので
const setupGsap = (pagesElement: HTMLDivElement, pagesWrapperElement: HTMLDivElement) => {
gsap.to(pagesElement, {
x: () => -(pagesElement.clientWidth - pagesWrapperElement.clientWidth),
ease: 'none',
scrollTrigger: {
trigger: '#horizontal-scroll-section',
start: 'top top',
end: () => `+=${pagesElement.clientWidth - pagesWrapperElement.clientWidth}`,
scrub: true,
pin: true,
anticipatePin: 1,
invalidateOnRefresh: true,
},
})
}
<main className={styles.main}>
<section className={styles.section} id="horizontal-scroll-section">
<div className={styles.container}>
<div className={styles.pagesWrapper} ref={pagesWrapperRef}>
<div className={styles.pages} ref={pagesRef} id="pages">
{IMAGE_LIST.map((data) => {
return (<Page key={data.id} srcpath={createPublicImagePath(data.id.toString())}/>)
})}
<PageLast/>
</div>
</div>
</div>
</section>
</main>
.main {
background: black;
}
.section {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
.container {
width: 100%;
height: 100vh;
}
.pagesWrapper {
position: relative;
height: 100vh;
}
.pages {
position: absolute;
top: 0;
left: 0;
display: flex;
height: 100%;
justify-content: center;
align-items: center;
}
- 参考文献: GSAPでこんなこともできる!途中から横にスクロールするページを作る方法 | 株式会社LIG(リグ)|コンサルティング・システム開発・Web制作
- 詳細は参考文献の説明がうまいので参考文献をぜひ読んでください
- HTML/CSSは『横スクロールしたいリストのulだけが画面からはみ出てる状態』が正
- Reactの場合は
trigger
はidがおすすめ
【おまけ】スクロールして一定の場所を超えたら背景色を変更する
- 本来の目的である『縦スクロールしたら横スクロールする』機能はすでに完成していますが、映画館のごとく最後に明るくしたかったので、もう少し書きます
-
setupGsap
に下記を仲間入りさせます
gsap.to('main', {
scrollTrigger: {
trigger: '#horizontal-scroll-section',
start: 'top center',
end: () => `+=${pagesElement.clientWidth - pagesWrapperElement.clientWidth + 200}`,
scrub: true,
onEnter: () => gsap.to('main', {
backgroundColor: '#000',
duration: 1.4
}),
onLeave: () => gsap.to('main', {
backgroundColor: '#fff',
duration: 1.4
}),
onEnterBack: () => gsap.to('main', {
backgroundColor: '#000',
duration: 1.4
}),
onLeaveBack: () => gsap.to('main', {
backgroundColor: '#fff',
duration: 1.4
}),
},
})
- 縦スクロールを横スクロールに変化させる機能のGSAPを少し魔改造したもの
-
onEnter
,onLeave
,onEnterBack
,onLeaveBack
は名の通り。Backを追加しなければ『通過時一度だけ』な変化が作れる。 - 背景色はもともと黒。
start: 'top top'
にするとonEnter
が発火せず、onLeave
も発火しないので、start: 'top center'
にする
おわり
- 集大成としてできたのがこれ
- React + GSAPの事例がすくなくて動くまでが山場だった
- useEffectが2回マウントは、普段React17なのもあって気づくのがかなり遅れた
- 動いてからはとても楽しかったのでもう少しGSAPに触れてみようと思う
Discussion