ARTICLES

Next.js で PageTransition では難しいページ間のトランジションを行う

2019-04-17

PageTransition というコンポーネントを使うと、React アプリケーション上でページ間のトランジションを実現できます。 例えば、ページ遷移をする際に一旦フェードアウトし、遷移後にフェードインして表示するといった具合です。

Next.js にも next-page-transitions というライブラリが存在し、.page-transition-enter.page-transition-enter-active.page-transition-exit.page-transition-exit-active などのクラスをつけることで、 CSS アニメーションを行うことが可能です。 ただし、ページ間の CSS の transition でうまくできないことがあります。

前述のフェードイン・フェードアウトであれば実現が出来ますが、トップページから下位ページに遷移したときにヘッダーが小さくなるといったアニメーションを行おうとするとうまくいきません。

理由としては、page 以下にコンポーネントを配置して各ページを定義している場合、ページの切り替え時に一旦 #__next 以下の HTML エレメントが除去され、再びマウントされるためです。ということで、一見 transition の設定が出来そうに思えてもうまく動作しないのです。

大雑把な流れをページのトランジションに焦点を当てて書いてみると

  1. ページが表示される
  2. Link で定義されたリンクボタンをクリック
  3. #__next 以下の HTML エレメントが除去
  4. リンク先のコンポーネントがマウント

というような順になります。

ではどうしたらいいのでしょうか。
僕は、以下のように実装することで実現することが出来ました。

  1. 状態管理の機構を用意 (今回は unstated を使っています)
  2. ページのレイアウト状態を管理する変数などをアプリケーションの状態として設定
  3. Link で定義されたリンクボタンをクリック
  4. ページ遷移時にはアニメーションさせず、componentDidMount 時にページ遷移後にリンク元のレイアウトをアプリケーションの状態より復元
  5. 復元時に componentDidUpdate が走るので、そこでこのページで設定したいレイアウトの状態に変更
  6. アニメーションが行われる

実際のコードはこんな感じですね。

componentDidMount() {
    this.props.model.setLayout(this.props.model.state.layout)
}
componentDidUpdate() {
    if(this.props.model.state.layout !== Enum.layout.header.TOP) {
    this.props.model.setLayout(Enum.layout.header.TOP)
    }
}

componentDidMount 時にレイアウトを変更してしまうと、リンク元のレイアウト状態がないため、transition も発生しなくなるので、このようにしました。他にもやり方があるかもしれませんが、現状これでうまくいっているので、よしとします。

ということで、このサイトのトップページから下位階層のページに遷移するときなどに、ヘッダー部分の遷移アニメーションを追加してみました。


ARTICLES


    AUTHOR

    原 一浩 の顔写真

    (はら) 一浩(かずひろ)

    カンソクインダストリーズ代表 / グレーティブ合同会社代表

    1998年に独立し、同年、ウェブデザイン専門のメールメディア DesignWedgeの発行を開始。Webデザイン業の傍ら、海外のWebデザインに関する情報発信を行う。
    雑誌への寄稿多数。主な著書に『はじめてのフロントエンド開発』『プロセスオブウェブデザイン』、『Play framework徹底入門』、『ウェブデザインコーディネートカタログ』など。自社製のWebデザインのクロール&キャプチャシステムvaqumをベースに、様々なリサーチを行っている。Web 検定プロジェクトメンバー

    著作一覧はこちら