巷のウェブサイトでよく見る、画面内に要素が入ってきた瞬間に発火するフェードインエフェクト。
UI/UX向上の為の小技として使われるアニメーションで、よく使われるテクニックである。
今回はReactでアニメーションのコンポーネントを作成したので、コードと使い方を記載する。
#使用技術
###react-inview-monitor
https://github.com/snipsco/react-inview-monitor
画面内に要素が入ってきた時に、子要素にクラス名をつけたり、propsを渡したりできる。
色々探したけど、これが汎用性が高く、使いやすい。
###intersection-observer
https://www.npmjs.com/package/intersection-observer
safariやIE用のpolyfill。基本的にスクリプトタグを貼りつけるだけでいいが、
Gatsbyを使う場合は別の方法もあるので後述。
###styled-components
https://www.styled-components.com/
CSS in JSをする為のライブラリ。要は、JavaScript内でCSSを書ける。
個人的にはSass(Scss)より便利で、愛用。
###animate.css
https://daneden.github.io/animate.css/
フェードイン含め、多くのアニメーションスタイルが書かれている。
CSSアニメーションをする際の参考資料。
#インストール
まずはReact(もしくはGatsby)のプロジェクト内で
npm install react-inview-monitor intersection-observer styled-components
と叩いて、各種ライブラリをインストール。ReactとかGatsbyのセットアップの仕方は割愛。
create-react-appや、npx gatsby newとか使って、環境構築して欲しい。
#ポリフィル
基本的にドキュメント通りにスクリプトを貼り付ければOK。
他のスクリプトよりも先にポリフィルを読み込ませるようにしよう。
<!-- Load the polyfill first. -->
<script src="path/to/intersection-observer.js"></script>
<!-- Load all other JavaScript. -->
<script src="app.js"></script>
ただしGatsby使う場合はスクリプト貼りつける代わりに、gatsby-browser.jsに以下を書けばOK。
export const onClientEntry = () => {
// IntersectionObserver polyfill for Safari, IE
if (!(`IntersectionObserver` in window)) {
import(`intersection-observer`)
console.log(`# IntersectionObserver is polyfilled!`)
}
}
#完成コンポーネント
では、早速作ったコンポーネントを掲載する。
import React from "react"
import styled from "styled-components"
import InViewMonitor from "react-inview-monitor"
export const Up = ({ children, delay, margin = "-20%" }) => (
<InViewMonitor childPropsInView={{ isActive: true }} intoViewMargin={margin}>
<FadeInUp isActive={false} delay={delay}>{children}</FadeInUp>
</InViewMonitor>
)
export const Down = ({ children, delay, margin = "-20%" }) => (
<InViewMonitor childPropsInView={{ isActive: true }} intoViewMargin={margin}>
<FadeInDown isActive={false} delay={delay}>{children}</FadeInDown>
</InViewMonitor>
)
export const Left = ({ children, delay, margin = "-20%" }) => (
<InViewMonitor childPropsInView={{ isActive: true }} intoViewMargin={margin}>
<FadeInLeft isActive={false} delay={delay}>{children}</FadeInLeft>
</InViewMonitor>
)
export const Right = ({ children, delay, margin = "-20%" }) => (
<InViewMonitor childPropsInView={{ isActive: true }} intoViewMargin={margin}>
<FadeInRight isActive={false} delay={delay}>{children}</FadeInRight>
</InViewMonitor>
)
const FadeIn = styled.div`
opacity: 0;
transition: opacity 1s ease, transform 1s ease;
${({ isActive }) => isActive && `opacity: 1;`}
${({ delay }) => delay && `transition-delay: ${delay}ms;`}
`
const FadeInUp = styled(FadeIn)`
transform: translateY(30px);
${({ isActive }) => isActive && `transform: translateY(0px);`}
`
const FadeInDown = styled(FadeIn)`
transform: translateY(-30px);
${({ isActive }) => isActive && `transform: translateY(0px);`}
`
const FadeInLeft = styled(FadeIn)`
transform: translateX(-30px);
${({ isActive }) => isActive && `transform: translateX(0px);`}
`
const FadeInRight = styled(FadeIn)`
transform: translateX(30px);
${({ isActive }) => isActive && `transform: translateX(0px);`}
`
このコンポーネントをfadeIn.jsとか名前つけて、componentsディレクトリに保存。
#コンポーネントの使い方
このコンポーネントを使うときは、フェードインさせたい要素をwrapする。
下から上にフェードインさせたい時は、FadeIn.Up、
上から下はFadeIn.Down、
左からはFadeIn.Left、
右からはFadeIn.Right
という要素で包むだけでOK。
要は、* as FadeInで../components/fadeIn内の全てのexportされたコンポーネントを、inportして使うという認識。
import React from "react"
import * as FadeIn from "../components/fadeIn"
export default () => {
return (
<FadeIn.Up>
{ "フェードインさせたい要素" }
</FadeIn.Up>
)
}
#コンポーネントの解説
コードを掲載しただけではカスタマイズしにくいと思うので、
書いたコードの解説をする。
###基本コンポーネント
export const Up = ({ children, delay, margin = "-20%" }) => (
<InViewMonitor childPropsInView={{ isActive: true }} intoViewMargin={margin}>
<FadeInUp isActive={false} delay={delay}>{children}</FadeInUp>
</InViewMonitor>
)
このコンポーネントでは、引数として、
children(ラップする対象)、
delay(transitionの遅延)、
margin(フェードインさせる要素が発火する画面の範囲。例えば-20%だったら、上下から20%引いた、60%分の画面範囲)
を受け取る。
delayや、marginをpropsとして渡さなければ、デフォルトの値が入る。(delayはundifined,marginは-20%を初期状態と設定している)。
そして、InViewMonitorコンポーネントが、発火した時、以下のコードで子要素に{isActive: true}というpropsが渡される。
childPropsInView={{ isActive: true }}
そして子要素のスタイルドコンポーネントがisActiveを受け取り、それに応じて、opacityが変化して、フェードインされるという仕組み。
###スタイルドコンポーネント
const FadeIn = styled.div`
opacity: 0;
transition: opacity 1s ease, transform 1s ease;
${({ isActive }) => isActive && `opacity: 1;`}
${({ delay }) => delay && `transition-delay: ${delay}ms;`}
`
const FadeInUp = styled(FadeIn)`
transform: translateY(30px);
${({ isActive }) => isActive && `transform: translateY(0px);`}
`
Up,Down,Left,Right共通のスタイルドコンポーネントとしてFadeInコンポーネントを作り、
それをFadeInUp等としてwrapしてスタイルの追加を行う。
デフォルトでは、opacityを0にしておき、フェードインのアニメーションを設定したいので、transitionを追加。
そして、isActiveを引数として受け取り、isActiveがtrueになった時に、opacityや、transformによる位置を変化させる。
基本的に、スタイルドコンポーネントを調整したり、あとはanimate.cssからいろんなスタイルを引っ張ってきて、inViewMonitorに発火させれば、色々なアニメーションを実装できる。
#最後に
Reactを使わず、プレーンなhtml,css,javascriptで構成する場合は、
in-view.js
https://github.com/camwiegert/in-view
をよく利用するが、今回はReactでin-viewアニメーションを実装してみた。
小技的な感じで使えると思うので、UI/UXのスパイスとして使ってみて欲しい。