joppot

コピペで絶対動く。説明を妥協しない

プログラミング

入門ReactでGSAPアニメーションの使い方

投稿日:2018年11月19日 更新日:


概要

みなさんこんにちはcandleです。今回はReactでGSAPを使ってみたいと思います。
簡単にアニメーションが使えるjsライブラリはjQueryが有名です。ただ、jQueryとReactは相性が悪いらしいです。また、React自体は私の知る限りは使いやすいアニメーションの機能がありません(Transitionはあるけど)。

しかし、古今東西webサービスを作った暁にはアニメーションが必要ではない方が珍しいです。
Reactでアニメーションを使おうとすると幾つもの候補があります。下のURLにはReactで使えそうなライブラリをいくつも紹介しています。

https://qiita.com/nabepon/items/c005a7d4491fd04b453e

その中でもGSAPはボリュームがあり、大きなライブラリです。もちろん、必要な機能だけをインポートして小さく使うことも可能です。早速挑戦してみましょう。

例によってこの記事もチュートリアル形式を採用しています。

前提

  1. Reactの知識がある
  2. create-react-appコマンドがインストールされている

準備

プロジェクトを作成します。ターミナルでコマンドを実行しましょう。

create-react-app react-animation
cd react-animation

デザインを整えます。src/App.jsをこのようにします。

import React, { Component } from 'react'
import './App.css'

class App extends Component {
  render() {
    return (
      <div className="App">
        <header className="App-header">
          <div>hello</div>
        </header>
      </div>
    )
  }
}

export default App

続いてsrc/App.cssをこのようにしておきます。

.App {
  text-align: center;
}

.App-header {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-size: calc(10px + 2vmin);
}

GSAPのインストール

gsapをreactで使うには2つのライブラリ、gsapとreact-gsap-enhancerが必要です。下のコマンドでインストールしましょう。

yarn add gsap react-gsap-enhancer

1度だけアニメーションを実行

1回だけアニメーションするコンポーネントなのか、それとも2回以上あるのかコードの書き方が変わります。
最初にコンポーネントがマウントされた時だけアニメーションが実行するコンポーネントを作ってみましょう。

src/componentsを作成し、

mkdir src/components

OneTimeCardコンポーネントを作成します。

touch src/components/OneTimeCard.js

中身をこのようにします。

import React from 'react'
import GSAP from 'react-gsap-enhancer'
import { TimelineMax } from 'gsap'

class OneTimeCard extends React.Component {
  constructor(props) {
    super(props)
    this.myCard = null
  }

  componentDidMount() {
    new TimelineMax().from(this.myCard, 1, { y: 20, opacity: 0 })
  }

  render() {
    return (
      <div>
        <div style={styles.box} ref={div => (this.myCard = div)}>
          <div>Card</div>
        </div>
      </div>
    )
  }
}

const styles = {
  box: {
    width: '120px',
    height: '120px',
    zIndex: 1,
    cursor: 'pointer',
    border: '1px solid black',
    display: 'flex',
    justifyContent: 'center',
    alignItems: 'center',
  },
}

export default GSAP()(OneTimeCard)

このコードの一番重要なところはここです。

componentDidMount() {
  new TimelineMax().from(this.myCard, 1, { y: 20, opacity: 0 })
}

TimeLineMaxを使ってref参照先のDOMにアニメーションをつけています。
1秒で透明度0から1までy軸に20px動かして表示させてます。

src/App.jsを開いてこのようにします。

import React, { Component } from 'react'
import OneTimeCard from './components/OneTimeCard'
import './App.css'

class App extends Component {
  constructor(props) {
    super(props)
  }

  render() {
    return (
      <div className="App">
        <header className="App-header">
          <OneTimeCard />
        </header>
      </div>
    )
  }
}

export default App

サーバを起動して確認すると、下からふわっと現れてくるのがわかります。

yarn run start

2回以上動かすアニメーション

さて、今度はもう少し手の凝ったアニメーションコンポーネントを作ってみましょう。

ボタンを押すとアニメーションと共にカードが現れ、もう1度ボタンを押すと消えるコンポーネントを作ってみましょう。

新しくSwitchingCard.jsを作ります。

touch src/components/SwitchingCard.js

中身はこのようにします。

import React from 'react'
import GSAP from 'react-gsap-enhancer'
import { TimelineMax } from 'gsap'

class SwitchingCard extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      isBox: false,
    }
    this.myElement = null
    this.myTween = new TimelineMax()
  }

  toggleBox() {
    if (!this.state.isBox) {
      this.addAnimation(this.createAnim.bind(this))
    } else {
      this.addAnimation(this.createExitAnim.bind(this))
    }

    this.setState(prevState => ({
      isBox: !prevState.isBox,
    }))
  }

  createAnim() {
    return this.myTween
      .to(this.myElement, 0.1, { display: 'flex', clearProps: 'transform' })
      .to(this.myElement, 1, { y: 10, opacity: 1 })
  }

  createExitAnim() {
    return this.myTween
    .to(this.myElement, 1, { y: -1, opacity: 0 })
    .to(this.myElement, 0, { display: 'none' })
  }

  render() {
    const { isBox } = this.state
    return (
      <div style={styles.wrapper}>
        <div onClick={this.toggleBox.bind(this)} style={styles.button}>
          Show
        </div>

        <div style={styles.box} ref={div => (this.myElement = div)}>
          <div>Card</div>
        </div>
      </div>
    )
  }
}

const styles = {
  wrapper: {
    position: 'relative',
  },
  button: {
    width: '110px',
    cursor: 'pointer',
    border: '1px solid black',
    padding: '5px 5px',
  },
  box: {
    position: 'absolute',
    bottom: '55px',
    width: '120px',
    height: '120px',
    zIndex: 1,
    cursor: 'pointer',
    border: '1px solid black',
    display: 'none',
    justifyContent: 'center',
    alignItems: 'center',
    opacity: 0,
  },
}

export default GSAP()(SwitchingCard)

src/App.jsを開いて、利用するコンポーネントを変更します。

import './App.css'
import React, { Component } from 'react'
import SwitchingCard from './components/SwitchingCard'

class App extends Component {
  constructor(props) {
    super(props)
  }

  render() {
    return (
      <div className="App">
        <header className="App-header">
          <SwitchingCard />
        </header>
      </div>
    )
  }
}

export default App

解説の前に、動かしてみましょう。
どうですか、ボタンを押すと、カードが表示され、もう一度押すと消えますね。

コードの解説をします。

アニメーション対象DOMはrefで管理

アニメーションの対象はReactのrefで行います。constructor()で対象のDOMを代入する変数this.myElement = nullを初期化しています。

これは、JSX内で対象を定義します。

<div style={styles.box} ref={div => (this.myElement = div)}>
  <div>Card</div>
</div>

ちなみに、React16.xで導入されたReact.createRef()は使えませんでした。

アニメーションのインスタンスは使い回す

アニメーションを何度も記述する場合はその度にTimelineMax()のインスタンスを作るのではなく、constructor()中でthis.myTween = new TimelineMax()インスタンスを作り再利用します。

これは、createAnim()関数とcreateExitAnim()関数の中で使われています。

 createAnim() {
    return this.myTween
      .to(this.myElement, 0.1, { display: 'flex', clearProps: 'transform' })
      .to(this.myElement, 1, { y: 10, opacity: 1 })
  }

  createExitAnim() {
    return this.myTween.to(this.myElement, 1, { y: -1, opacity: 0 }).to(this.myElement, 0, { display: 'none' })
  }

連続するアニメーションをGSAPに追加する

2回以上アニメーションが連続する場合はreact-gsap-enhancerのthis.addAnimation()を使う必要があります。これはアニメーションがそれぞれで独立して動くのではなく前のアニメーションの動きを引き継ぐ必要があるからです。

この部分で作成したアニメーションがGSAPに追加されています。

   if (!this.state.isBox) {
      this.addAnimation(this.createAnim.bind(this))
    } else {
      this.addAnimation(this.createExitAnim.bind(this))
    }

仮に、この部分を下のように変更して直接実行すると最初のcreateAnim()のアニメーションとcreateExitAnim()のアニメーションが独立して実行されて変になります。

 if (!this.state.isBox) {
   this.createAnim()
 } else {
   this.createExitAnim()
 }

アニメーションが続く場合は必ずaddAnimationしましょう。

まとめ

どうでしょうか、基本的なReactでのGSAPを使ったアニメーションを試してみました。GSAPは大変ボリュームがあるアニメーションライブラリなので、色々試して使ってみてください。

GSAP公式のGetting startedとreactの部分は一度読んでおくと良いでしょう。

Getting Started with GSAP

https://greensock.com/get-started-js

Getting Started: React and GSAP Animations

https://greensock.com/react

スポンサードリンク

「為になったなぁ」と思ったら、シェアお願いします。

-プログラミング
-, ,

執筆者:


comment

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

関連記事

reactで最もシンプルなドロップダウンメニューを作成

概要 みなさんこんにちはcandleです。今回はreactで最もシンプルなドロップダウンメニューを作ってみたいと思います。 前提 Reactの知識がある 準備 以下のコマンドでreactプロジェクトを …

Mysqlで今日以降の最も近い日付を取得する

概要 みなさんこんにちはcandleです。今回はmysqlで今日以降の最も近い日にちを取得する方法を紹介します。 ライブや、イベントなどで、今日から見て、最も近い日のデータを取得したみたいなことってあ …

MysqlのSELECT FROMの結果を美しく、見やすく表示する

概要 みなさんこんにちはcandleです。今回はmysqlのデータベースに関する簡単な記事です。 データベース系の言語は最近、様々出てきましたが、私は未だにMysqlくらいしか触っていません。 私はp …

Mysqlのdatetime型とtimestanp型で保存されているデータを年月日だけを指定して任意の日にちのデータを取得する方法

概要 みなさんこんにちはcandleです。今日はmysqlのdatetime型とtimestanp型におさめられているデータの取得を紹介します。 datetimeやtimestanpは多くの場合下のよ …

react0.14 + gulp + express + babelでReactのチュートリアル環境構築をする

概要 みなさんこんにちはcandleです。今回は、react0.14のnodeの環境構築をします。 react0.14はまだまだ、開発段階にあり、しばしば仕様変更が行われています。 0.13の時では使 …


ベンチャー企業のCTOをやってます。大学時代にプログラミングを始め、javaから入門し、C++へて、PHPへと進み、会社ではRailsを使用。自動化が大好きなプログラマー