LINE株式会社は、2023年10月1日にLINEヤフー株式会社になりました。LINEヤフー株式会社の新しいブログはこちらです。 LINEヤフー Tech Blog

Blog


2023年にパララックスをCSS、JS、Vue3で実装!LINEポイント10周年記念サイトの実装例

LINE株式会社 UIT3室 Front-end Dev3チームに所属しているキョウショウヨウです。

2023年6月20日に「LINEポイント10周年記念サイト」を公開しました。このサイトはパララックスデザインが採用されたLPです。今回はライブラリを使わずにパララックスデザインCSSJSVue3で実装する作り方について説明します。

LINEポイント10周年Anniversary
LINEポイント10周年記念サイト
※ 期間限定公開のため 、2023年8月以降は見れない可能性があります。

LINEポイント10周年記念サイトでは3つの大きな取り組みをしています。

  1. SNSシェア時に利用する、OGP画像の大量生成ツール & 生成APIの作成
  2. Nuxt3を利用した、SSR、SSGハイブリットアプリの開発
  3. パララックスデザイン の実装

今回は3番の、Vue3Nuxt3)で作成したLPパララックスデザインを導入するための作り方についてお話します。

私、個人としては7年ぶりにLP開発をおこないました。今回改めてパララックスデザインの実装方法について調査をおこなったのですが、フロントエンド技術の進化を感じる事ができました。

2023年現在は、JavaScriptのライブラリを利用しなくても、簡単なCSSJSパララックスデザインのサイトが作れるようになっていたので、実装方法も含めてご紹介します。

パララックスデザインとは

パララックスデザインは、パララックス(視差効果)を使ったスクロールエフェクト演出を取り入れたWEBデザイン、WEBサイト、WEBページを示す言葉です。パララックスサイト、パララックスページ、パララックスLPという呼称で呼ぶ方も多いです。

2次元であるWEBページに立体感や奥行きを感じられる動きが実装され、アニメーション等の視覚的エフェクトを多く実装しているのが特徴です。

パララックスデザインの多くは、スクロールの動きに合わせてアニメーションをおこないます。スクロール時に遅延して背景を移動させたり、スクロールに合わせてコンテンツや装飾要素がアニメーションで動きます。

立体感のある先進的なデザインを表現できるため、LPを見るユーザーに強い印象を残すことができます。

パララックスとは

視差(しさ)は、二地点での観測地点の位置の違いにより、対象点が見える方向が異なること、または、その角度差。パララックス (英:parallax)ともいう。

  • 近距離の視差
  • 距離の指標としての視差

引用:  視差 - Wikipedia

皆様は電車や車の窓から外を見た時に、近くにある木は速く移動して、遠くにあるビルはゆっくり移動する光景をご覧になったことはありますか?

簡単にまとめると、近い距離のもと遠い距離のもので物体の移動速度や大きさ、配置位置、色が変わる現象のことをいいます。

デッサンで言う遠近法や絵画空間をイメージしてもらうのが早いと思います。紙などの平面に空間の奥行きを感じてもらうために使われる理論です。詳細は 絵画空間と遠近法 | デッサンの描き方と基礎技法 を参照されると良いと思います。

パララックスの注意点

The Journal of Usability Studiesは、パララックスに関する研究をおこないました。
しかし、この調査では、パララックスのサイトを使用した参加者のうちの2人が、「パララックスのウェブサイトとやり取りを行っている間に、乗り物酔いと深刻なユーザビリティに関する問題」に苦しんでいたことがわかりました。

引用:パララックスを採用する前に考えるべき3つの注意事項 | UX Milk

一般的に言われているパララックスの注意点の1つに「乗り物酔いが発生する可能性がある」というものがあります。

絵画空間と遠近法 の話にあるように、適切なパララックスを実現するためには正確な計算をおこない移動量などを算出する必要性があります。人の脳は潜在意識下で様々な情報の処理をしているので、ここでの計算ミスが違和感や潜在的ストレス、嫌悪感等を引き起こす可能性が少なからずあると私は考えています。

LINEポイント10周年記念サイトで実装した内容について

今回のLINEポイント10周年記念サイトは、Vue3Nuxt3)を使用して実装しました。

パララックスデザインを実現するために2つの機能を実装しました。

No
機能
概要
1 パララックススクロールエフェクト

CSSperspectiveプロパティを利用。

2

スクロールを検知してアニメーションする機能

JSIntersection Observer APIを利用してVue.jsのカスタムディレクティブを作成しスクロールを検知。
CSSを利用してアニメーションを実装。

1. パララックススクロールエフェクトの作り方

背景の模様がスクロール時に遅延して移動する

今回はCSSを使い3D空間を生成。奥行きであるZ軸にbackgroundの要素を配置する方法で実装しました。

大きな理由としては、パララックス(視差効果)を人間の目が違和感を感じないように実装するためです。この方法を利用すればブラウザ側で適切なパララックスを自動計算して反映してくれます。

パララックスのHTML&CSS実装例(サンプルコード)

src/layout/default.vue

<template>
    <div class="layout-default">
        <div class="layout-default_scroll">
            <main class="layout-default_main">
                  メインコンテンツが入ります。(最前面に配置)
             </main>
            <div class="layout-default_bg1">
                   背景要素が入ります。(2番目に配置)
            </div>
            <div class="layout-default_bg2">
                   背景要素が入ります。(3番目に配置)
             </div>
            <div class="layout-default_bg3">
                   背景要素が入ります。(最背面に配置)
            </div>
        </div>
    </div>
</template>
 
<style lang="scss" scoped>
.layout-default {
    &_scroll {
        scroll-behavior: smooth;
        overflow-y: scroll;
        overflow-x: hidden;
        position: relative;
        perspective: 200px;
    }
    &_main {
       position: relative;
       transform: translateZ(0);
       z-index: 4;
    }
    &_bg1,
    &_bg2,
    &_bg3 {
        position: absolute;
        top: 0;
        bottom: 0;
        left: 0;
        right: 0;
        width: 100%;
    }
   &_bg1 {
        transform: translateZ(-50px) scale(1.25);
        z-index: 3;
   }
  &_bg2 {
        transform: translateZ(-100px) scale(1.5);
        z-index: 2;
   }  
   &_bg3 {
        transform: translateZ(-150px) scale(1.75);
        z-index: 1;
   }  
}
</style>

スクロール対象要素に perspective を設定して3D空間を生成して、スクロール時に遠近法に伴った動きをするようにしています。その子要素に translateZ を設定して指定のZ座標に配置しています。この設定をすると遠近法で要素のサイズが小さくなるので、scaleを設定して元のサイズに戻す対応をしています。

注意点

flexと同じく直下の子要素にしか適応されない点です。
ただし、Firefoxの場合はスクロール要素以外に perspective を設定してもスクロール連動で動いてしまうブラウザ依存問題があります。

また、背景要素の高さについても注意が必要です。若干のブラウザ依存はあるらしく、<main> よりも背景要素が長くなってしまい画面下部に余白が発生してしまう現象がありました。

&_bg1 {
   transform: translateZ(-50px) scale(1.25);
   z-index: 3;
    
   &_cnt {
       height: calc(var(--scroll-contents-height) / 1.25);
       overflow: hidden;
   }
}

上記のようなスタイルを追加。<main>の高さをJSで取得しCSSカスタム変数 に登録する形で対応をおこないました。

今回の実装はレイアウトへの影響が大きいので、事前に挙動を把握した上で、HTMLの設計に取り掛かりました

2. スクロールを検知してアニメーションする機能の作り方

今回はJSIntersection Observer APIを利用したVue.jsのカスタムディレクティブを作成して対応しています。

パララックスのJS実装例(サンプルコード)

src/layout/default.vue

<script setup lang="ts">
    import { vScrollAnimationClass } from '@/directives/scrollAnimationClass';
</script>
<template>
    <div class="layout-default">
        <div class="layout-default_scroll" v-scroll-animation-class="{ targetQueries: ['.component-a', '.component-b', '.component-c'] }">
            <main class="layout-default_main">
                  メインコンテンツが入ります。(最前面に配置)
             </main>
            <div class="layout-default_bg1">
                   背景要素が入ります。(2番目に配置)
            </div>
            <div class="layout-default_bg2">
                   背景要素が入ります。(3番目に配置)
             </div>
            <div class="layout-default_bg3">
                   背景要素が入ります。(最背面に配置)
            </div>
        </div>
    </div>
</template>
src/directives/scrollAnimationClass.ts
/**
 * scrollして画面内に入った要素に`_animateStart`などのClass名を付与するディレクティブ
 * @name v-scroll-animation-class
 * @example: v-scroll-animation-class="{ targetQueries: ['.hoge','.fuga'] }
 */
export const vScrollAnimationClass: Directive = {
  mounted(el: HTMLElement, binding: DirectiveBinding) {
 
    // 交差を検知した際に "_animateStart" を付与する処理
    const addAnimationClassName = (entries: IntersectionObserverEntry[]): void => {
      entries.forEach((entry: IntersectionObserverEntry) => {
        const ratio = Math.ceil(entry.intersectionRatio * 10);
        const targetEl = entry.target as HTMLElement;
        const classList = targetEl.classList;
 
        // 表示開始 className 追加
        if (ratio >= 1.9) {
          const isAnimationEnded = !!classList.contains('_animateStart');
          if (!isAnimationEnded) {
            classList.add('_animateStart');
          }
        }
      });
    };
 
    // observer
    const observer = new IntersectionObserver(addAnimationClassName, {
      root: el,
      threshold: [0, 0.2, 0.4, 0.6, 0.8, 1],
    });
 
    // 監視する要素を登録
    const targetQueries = binding.value.targetQueries;
    targetQueries.forEach((query: string) => {
      const el = document.querySelector(query) as HTMLElement;
      if (el) {
        observer.observe(el);
      }
    });
  },
};

※ 基本的な処理だけ抜粋して記載しています。unmounted 処理は読者の判断で追加してご利用ください。

アニメーション実行タイミングをVue.jsのカスタムディレクティブで検知し"_animateStart"をクラス名に付与。それに合わせてCSSでアニメーションを動かしています。

カスタマイズ

実際に利用したコードではMittを利用した通知機能も入っています。それにより、UI1が画面内に入った場合、UI2をアニメーションさせる対応も実現しています。

他にも、"ratio==2"の場合"_animate20par"のクラス名を付与する形でカスタマイズすると、簡単なスクロール連動のアニメーションを実現することも可能です。

注意点と対策

"scroll-behavior: smooth;"指定時に素早くスクロールをするとイベントが発火しないケースがあります。その為、thresholdを細かく設定し検知精度をあげています。

デザインから実装までの業務フロー

今回、パララックスデザインなどのアニメーションを多く使ったページを作る際に懸念となったのは、デザイナーとのコミュニケーションコストでした。「フロントエンド専門職」と「デザイン専門職」で分業する体制を取っている場合、アニメーションの各種細かい調整で時間がかかってしまいます。そのため、手戻りを少なく進める業務フローを事前に用意し開発に臨みました。

今回の業務フロー

手順
ジョブ
項目
note
1 FrontEnd & Design 事前にヒアリングを実施
2 FrontEnd 技術選定とプロトタイプを作成
3 FrontEnd デザインのインプット方法を伝達
4 Design デザインファイルとアニメーション指示書を作成
5 FrontEnd 画面実装 提案がある場合、複数案作成
6 Design 1回目チェック 提案内容のジャッジ、基本挙動確認
7 FrontEnd チェックバック事項の反映
8 Design 最終チェック チェックバック事項の反映確認

1回のチェックで完了できる業務フローで実施しました。

LINEポイント10周年記念サイトで利用したパララックスデザインサイトのアニメーション定義書

手順4のアニメーションの指示書に関しては、上記のようにシンプルな形で作って頂いています。プロトタイプで作成していたCSSJSの実装に基づいて、背景の位置と各アニメーションの実行タイミングを定義してもらっています。

アニメーションの詳細に関しては要望を聞いた上で、フロントエンドメンバー側で最適だと言えるアニメーションを複数案準備しました。長年一緒に仕事をしているので、信頼いただいているからこそ実現ができたという側面はあります。

これにより、稼働時間が限られていたプロジェクトでしたが、パララックスデザインのサイトを素早く作ることができました。

最後に

通常のLPにパララックスエフェクトを少し足すだけであれば、JavaScriptライブラリを使わなくても簡単なCSSJSで対応は可能です。ただしスクロールに応じて画面が切り替わるストーリーテーリング形式のパララックスデザインでは、今回の実装では対応が難しいケースがあります。その際は、アニメーション要件を確認のうえ、JavaScriptライブラリや使用技術を選定するのが良いでしょう。

ぜひ、一つのパララックスの実装例として参考にしていただければと思います。