概要
縦スクロールする(マウスホイールする)と、ページが横に切り替わるコンポーネントを実装します。
スタイリングには、emotion/css(CSS in JS)を使用しています。
実装
実装のイメージ図です。主要なCSSプロパティを記述しています。
.tsx
import React, { useEffect, useRef, VFC } from 'react';
import { css } from '@emotion/css';
export const ParallaxHorizontalScrollPage: VFC = () => {
const screenRef = useRef<HTMLDivElement>(null)
useEffect(() => {
screenRef.current!.onwheel = ev => {
ev.preventDefault()
let delta = (ev.deltaY / Math.abs(ev.deltaY)) * window.innerWidth
if (delta > 0) {
delta += screenRef.current!.scrollLeft
delta = Math.floor(delta / window.innerWidth) * window.innerWidth
} else {
delta += screenRef.current!.scrollLeft
delta = Math.ceil(delta / window.innerWidth) * window.innerWidth
}
screenRef.current!.scrollLeft = delta
}
}, [])
return (
<div ref={screenRef} className={styles.screen}>
<div className={styles.container}>
<div className={styles.page('#0085d1')}>
<div className={styles.text}>Page 1</div>
</div>
<div className={styles.page('#db0063')}>
<div className={styles.text}>Page 2</div>
</div>
<div className={styles.page('#ffcd43')}>
<div className={styles.text}>Page 3</div>
</div>
</div>
</div>
)
}
const styles = {
screen: css`
position: relative;
width: 100vw;
height: 100vh;
overflow: auto;
scroll-behavior: smooth;
`,
container: css`
width: 300vw;
height: 100%;
display: flex;
`,
page: (bgColor: string) => css`
position: relative;
width: 100vw;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
background-color: ${bgColor};
`,
text: css`
font-size: 3rem;
color: white;
`
}
useEffectの中で、screen(CSS名のdiv要素)に対してスクロール量を調整しています。
deltaは横のスクロール量で、1回の縦スクロールで画面幅(innerWidth)だけ横スクロールします。
縦のスクロール量を絶対値で割ることで、+/-を取得しています。
.tsx
let delta = (ev.deltaY / Math.abs(ev.deltaY)) * window.innerWidth
以下のコードでは、ページを途中まで手動で横スクロールしているときに、縦スクロールをしたときの横のスクロール量を決めています。
これによって、デモ(記事上部のGIF)のような動作になります。
.tsx
if (delta > 0) {
delta += screenRef.current!.scrollLeft
delta = Math.floor(delta / window.innerWidth) * window.innerWidth
} else {
delta += screenRef.current!.scrollLeft
delta = Math.ceil(delta / window.innerWidth) * window.innerWidth
}