こんにちは、CADCのLP開発チームです。皆さん、11/29-11/30に開催されたCADC2024をご覧になっていただけましたか?

まだの方は、以下のリンクから是非セッションをお楽しみください!

https://cadc.cyberagent.co.jp/2024/

本記事では、CADC 2024 LP がどのように作られたのかご紹介します。

Astro による軽量な LP の実現

前年度は Next.js の Static Generation を用いて、社内に蓄積している Next.js や React の知見を活かした効率的な開発ができました。

一方で、JS を多用したことによるパフォーマンスの劣化が課題となりました。

本年度は新たな技術への知見を深めつつ、課題だったパフォーマンス面を改善すべく Astro を採用しました。

https://astro.build

Astro は Static Generation が可能なフレームワークです。.astro ファイルに HTML に似た構文で、Single File Component として UI を実装できます。

また、アイランド という仕組みで、React や Vue などで実装した UI を埋め込めます。既存のコンポーネントや実装方法を再利用しつつ、アイランド以外の JS はビルド時に削除されるため、より良いパフォーマンスが期待できます。

結果と所感

トップページでは、Lighthouse のパフォーマンススコアが前年比 +10 以上向上し、目標としていた 80 を達成しました。

開発効率としては、Astro へのキャッチアップは求められるものの、チーム全員が Web フロントエンド経験者であったことから、あまり大きな足かせにはなりませんでした。

むしろ、前年度の React による実装をそのまま使いまわせたり、Next.js と似た HTML の生成ができたりと、従来の知見を活かして効率よく進められました。

課題として、Astro には状態管理の仕組みがありません。今回のような複雑な UI が盛り込まれた LP では、React や nanostores に頼ってしまい、Astro の静的さを存分に活かし切れませんでした。

Docs や Blog など、ほとんど静的な Web ページには非常に適していますが、今回のような LP においては再考の余地があると感じました。

ライブラリを用いたスタイリングの整備

Astro と併せてスタイリングの規約を整備し、開発効率の向上を図りました。

クラス名の命名規則

クラス名にはBEM(Block Element Modifier)を採用しました。BEMは、CSSクラスの命名規則で、コードの可読性と再利用性を高めるための方法です。

BEMは以下の3つの部分から構成されます:

    • Block(ブロック): 独立した再利用可能なコンポーネントを表します。例: headermenubutton
    • Element(エレメント): ブロックの一部で、ブロックとの関連性を持つものを表します。例: header__titlemenu__itembutton__icon
    • Modifier(モディファイア): ブロックやエレメントの外観や振る舞いを変更するものを表します。例: header--largemenu__item--activebutton--primary

これらは、stylelint を使ってルール化を行いました。以下は、設定した .stylelintrc.json です。

{
  "extends": [
    "stylelint-config-html/astro",
    "stylelint-config-recess-order"
  ],
  "plugins": [
    "stylelint-selector-bem-pattern"
  ],
  "rules": {
    "plugin/selector-bem-pattern": {
      "preset": "bem",
      "componentName": "[A-Z]+",
      "componentSelectors": {
        "initial": "^\\.{componentName}(?:-[a-z]+)?$",
        "combined": "^\\.{componentName}(?:-[a-z]+)?(?:__|--)[a-z]+(?:-[a-z]+)*$"
      },
      "utilitySelectors": "^\\.u-[a-z]+(?:-[a-z]+)*$"
    }
  }
}

ブレークポイントの管理

ブレークポイントを変数で管理するために、postcss-custom-media を導入しました。
これは @custom-media を PostCSS のプラグインとして導入できるライブラリです。

導入理由は以下になります。

  1. media queries で変数を組み合わせて表現を行いたい
    → container queries を使用すれば、変数との組み合わせができるが、今回の用途に合わない
  2. スタイリングは css でシンプルに扱いたい
    → 同様に共通利用の定義ファイル(global.css)も CSS で扱いたい
    → SCSS を使えば mixin 方式で動作させることができるが、SCSS の性質上複雑になるリスクがある

postcss-custom-media を導入すると、以下のような形で記述できます。

@custom-media --breakpoint-mobile (width <= 768px);
@custom-media --breakpoint-tablet (width <= 1200px);

/* 通常(PC)のスタイル */

@media (--breakpoint-tablet) {
  /* タブレットサイズのスタイル */
}

@media (--breakpoint-mobile) {
  /* スマートフォンサイズのスタイル */
}

今回は PC ファーストでスタイリングを行ったので、タブレット(<= 1200px)とスマートフォン(<= 768px)のブレークポイントを用意して実装を行いました。
これによって、メディアクエリには CSS 変数を指定できないデメリットを回避して @custom-media を使用することが出来ます。

導入の所感

今回のプロジェクトは、全員がフロントエンド経験者で構成されていたので、BEM で特に苦戦するポイントはありませんでした。
postcss-custom-media に関しては、可読性やメンテナンス性も高く開発を行うことができ、導入して良かったと感じております。

Momento と AWS による堅牢なリアルタイムなチャット

EXPERT Day のライブセッションでは、リアルタイムなチャット機能を提供しました。

以下はアーキテクチャ図です。

前年度に引き続き、Momento の CacheTopics を活用しつつ、AWS のサービスを組み合わせた Token Vending Machine(TVM) を導入し、よりセキュアに実装しました。

TVM は、Momento の API にアクセスするための一時的な認証情報を発行する仕組みです。この認証情報を用いることで、LP に Momento の Credential を埋め込むことなく、安全に API にアクセスできます。

公式のサンプル を参考に、AWS CDK を用いて TVM のインフラを構築しました。

また、チャットの投稿内容は Orion を用いてフィルタリングし、ユーザーが快適に利用できるようにしました。Orion については、CADC 2024 でのセッションをご参照ください。

https://cadc.cyberagent.co.jp/2024/sessions/content-moderation-ai

開発の所感

イベント当日は、フィルタリングの調整ミスはあったものの、特に目立った障害なくチャット機能を提供できました。

また、いいねボタンの設置などによる UI/UX 改善で、今年度のコメント数は前年度の倍以上の563件にのぼりました。

前年度の課題だったチャットの安全性について、セキュリティ面は TVM の導入で、コンテンツの品質面は Orion によるフィルタリングで向上しました。

これらは、Momento 社と Orion チームによるサポートのもと実現できました。ありがとうございました。

Github REST APIとGoogle Workspace APIを活用したコンテンツ管理

前年度まではGoogle Apps ScriptとGoogle Spread Sheetを連携したコンテンツ管理を行っていました。
詳しくは、スプシ + GAS で GitHub と同期するカンファレンスサイトのコンテンツ管理 をご覧ください。

同記事の執筆時点から打って変わって、CyberAgent Developers Conferenceの開催規模は大きく成長し、下記のような課題が見えてきました。

  • 機能拡張や複数人でのバージョン管理が難しい
  • 画像の取り込みや軽量化ができない
  • 登壇者情報の変更から本番反映までのリードタイムが長い

本セクションでは、これらの課題を解決すべく私たちが開発した自動コンテンツ管理ツール「AssetSmith」を紹介します。

TL;DR

  • Google Spread Sheetをデータベースとして、Google Driveをメディア類のストレージとして扱い、登壇者やセッション情報を管理しました
  • Github Actions上のNode.js環境からスプシとドライブの情報を出力して、データを Git リポジトリに同期する仕組みを実装しました
  • repository_dispatchを使ってコンテンツ管理とサイト本体のソースコードを分離しました

技術選定について

AssetSmithの開発で用いた技術を紹介します。

Github Actions

こちらは「機能拡張や複数人でのバージョン管理が難しい」という課題を解決するために選定しました。

当初、clasp と呼ばれるGoogle製のCLIを用いて

  • バージョン管理: clasp
  • 実行環境: Google Apps Script

として用いる選択肢も検討しましたが、clasp自体にはブランチを切る機能は存在しないため、これ以上GAS上での管理を継続することは難しいと判断し、今年度は実行環境をGASからGithub ActionsのNode.js環境に移行しました。

Google Workspace API

こちらは「画像の取り込みや軽量化ができない」という課題の解決が目的となっています。

Google Workspace API とは、Google Workspace(旧 G Suite)の各種サービスをコードから操作するためのインターフェースを提供するAPI群です。

https://developers.google.com/workspace?hl=ja

Node.jsによりJavaScriptのエコシステムの活用が可能になったため、npmで公開されている googleapis を経由してAPIを利用しました。

Google Sheets APIのチュートリアルGoogle Drive APIのチュートリアル を参考に実装しました。

Sharp

こちらはGoogle Drive APIで取得した画像の軽量化・Webpへの変換のために用いました。

Sharpは、C言語製の画像処理ライブラリであるlibvipsを搭載しているため、Node.js上でも高速で動作します。

同じような画像処理系ツールとして Squoosh が有名ですが、こちらは2023年1月に 事実上のメンテナンス終了 が告げられているため、中長期での運用に向かないと判断しました。

repository_dispatchを使ったソースコードの分離

取り立てて新しい機能ではありませんが、Github Actionsではrepository_dispatchと呼ばれる他のリポジトリのworkflowを呼び出す機能を提供しています。

repository_dispatchを使うためには、呼び出される側のworkflowのonにrepository_dispatchを指定します。

1つのリポジトリから複数のワークフローが呼び出される時はrepository_dispatchtypesというフィールドに固有の識別子を指定します。

name: 呼び出される側のワークフロー

on:
  repository_dispatch:
    types: [呼び出されるワークフローの識別子]

...

これで呼び出されるワークフローの設定は完了です。

ワークフローはcURLで呼び出すことができます。

name: 呼び出す側のワークフロー

on:
  schedule:
    - cron: '0 10 * * *'  # 毎日10時に実行

jobs:
  trigger:
    runs-on: ubuntu-latest
    steps:
      - name: 呼び出し処理
        env:
          GITHUB_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
        run: >
          curl -X POST https://api.github.com/repos/${ORGANIZATION_NAME}/${REPOSITORY_NAME}/dispatches \
            -H "Accept: application/vnd.github+json" \
            -H "Authorization: token ${{ secrets.AUTHER_PAT }}" \
            -d '{
              "event_type": "呼び出されるワークフローの識別子",
              "client_payload": {
                <!-- 呼び出されるワークフローで固有の処理を行いたい場合、ここで固有のパラメータを渡すことができます -->
              }
            }'

AssetSmithは今年度に限らず、来年度以降もサイト開発時に活用する想定で作られたため、機能を半ばAPIとして提供する必要がありました。
そこで、上記のような呼び出される側のワークフローをスプシからのデータ抽出やドライブからの画像抽出といった機能ごとに作成し、それぞれにtypesを振り分け、利用側からcURL経由で利用する形態を取りました。

実際のコードを下記に示します。

CADC2024 LP本体側

name: Get SpreadSheet Raw Data

on:
  schedule:
    - cron: '0 10 * * *'  # 毎日10時に実行
  workflow_dispatch:  # 手動でのトリガーを許可

jobs:
  trigger:
    runs-on: ubuntu-latest
    steps:
      - name: Get SpreadSheet
        env:
          GITHUB_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
        run: >
          curl -X POST https://api.github.com/repos/xxxxxxxx/xxxxxxxx/dispatches \
            -H "Accept: application/vnd.github+json" \
            -H "Authorization: token ${{ secrets.AUTHER_PAT }}" \
            -d '{
              "event_type": "spreadsheet",
              "client_payload": {
                "BASE_BRANCH_NAME": "main",
                "TARGET_BRANCH_NAME": "bot/spreadsheet",
                "TARGET_REPOSITRY": "xxxxxxxx",
                "TARGET_OWNER": "xxxxxxxx",
                "TARGET_PATH": "xxxxxxxx"
              }
            }'

AssetSmith側(スプレッドシートの情報抽出用のworkflow)

name: Dispatch Create Spreadsheet

on:
  repository_dispatch:
    types: [spreadsheet]
  workflow_dispatch:


jobs:
  build:
    runs-on: ubuntu-latest
    env:
      GOOGLE_CLOUD_TOKEN_TYPE: ${{ secrets.GOOGLE_CLOUD_TOKEN_TYPE }}
      GOOGLE_CLOUD_TOKEN_CLIENT_ID: ${{ secrets.GOOGLE_CLOUD_TOKEN_CLIENT_ID }}
      GOOGLE_CLOUD_TOKEN_CLIENT_SECRET: ${{ secrets.GOOGLE_CLOUD_TOKEN_CLIENT_SECRET }}
      GOOGLE_CLOUD_TOKEN_REFRESH_TOKEN: ${{ secrets.GOOGLE_CLOUD_TOKEN_REFRESH_TOKEN }}
      AUTHER_PAT: ${{ secrets.AUTHER_PAT }}
      BASE_BRANCH_NAME: ${{ github.event.client_payload.BASE_BRANCH_NAME }}
      TARGET_BRANCH_NAME: ${{ github.event.client_payload.TARGET_BRANCH_NAME }}
      TARGET_REPOSITRY: ${{ github.event.client_payload.TARGET_REPOSITRY }}
      TARGET_OWNER: ${{ github.event.client_payload.TARGET_OWNER }}
      TARGET_PATH: ${{ github.event.client_payload.TARGET_PATH }}
    steps:
      - name: Checkout code
        uses: actions/checkout@v3
      - name: Setup Node
        uses: actions/setup-node@v3
        with:
          node-version-file: 'package.json'
          cache: yarn
      - name: Install packages
        run: yarn
      - name: Run a script
        run: yarn prd:spreadsheet

また、本年度は下記のWorkflowをサイト本体のリポジトリに設置し、cronによる定期実行でコンテンツの変更を自動検出することで運用コストの削減を図りました。

システムの全体像

ここまでを踏まえたシステムの全体像は下記の通りとなっています。

所感

前年度から、CyberAgent Developers Conferenceは若手主体のカンファレンスであるCA BASE NEXTと統合したことでカンファレンスの規模が大きくなりました。

本年度はAssetSmithの活用によって、その規模のカンファレンスコンテンツを安定的に運用することができたと感じています。
実際にサイト開発以外の運営メンバーからも「コンテンツ管理の手間が減って助かった」という旨を多数伺い、会全体のパフォーマンスの向上に貢献できたと実感しました。

次年度以降のCyberAgent Developer Conferenceでも開発や運用フローの効率化を通して、より高いクオリティのサイト開発に注力してまいります。

AssetSmithの開発にあたって、さまざまな記事・企業様の開発事例を大変参考にさせていただきました。
この場を借りて御礼申し上げます。

エンドロールページにおける3DCG表現

このセクションではエンドロールページの制作にあたり、技術的に挑戦したことや最終的な成果物に至るまでの一連の過程を共有し、得られた学びについてお伝えします。

記事と併せて、ぜひエンドロールページもご覧いただけると幸いです。

https://cadc.cyberagent.co.jp/2024/endroll/

制作の流れ

エンドロールページの制作は「WebGLを全面的に活用した3Dグラフィックを実現し、イベントを締めくくること」を目標に、LPを完成させた後に1つのプロジェクトとして何も決まっていない状態からエンジニア主導で開発を進めました。次項では、具体的な制作過程について詳しく説明します。

コンセプトメイキング

まずは、エンドロールページで何を表現するのかについて話し合いました。以下の点を留意しました。

  • CADC2024のコンセプトとズレがないこと
  • エンドロールページの役割を入れること

CADC2024は「Expanding Inspiration」をコンセプトに掲げ、このコンセプトを構成するキーワードとして「共存共栄」「拡張」「創造」を定義しています。詳細は以下の記事をご覧ください。

若手デザイナーチームが手がける CADC2024クリエイティブディレクションの全貌 | CADC 2024

エンドロールページでは、「CADC2024の締めくくり」と「運営スタッフの労いの場」として、このテーマを反映する必要がありました。特にデザイナーとのミーティングで以下の点について議論を重ねました。

  • 全体コンセプトとの整合性:
    • CADC2024のコンセプトを表現したトップページとのつながりを持たせることで、一貫性を保つことを重視
  • エンドロールの役割:
    • イベントの締めくくりとして、感謝と労いの意味を込めたエンドロールページにするためのコンセプトを練る
  • テーマの具体化:
    • 「共存」「創造」「拡張」をWebGLを用いてどう視覚的に表現するかを検討

結果、トップページで表現された円を拡張し、別世界の視点からCADC2024のコンセプトを視覚的に表現することに決定しました。このテーマを基にWebGLを用いた3D表現に挑戦しました。

モックの作成 (デザイン決め)

デザインの初期段階では、モックアップを実装で作成しながらコンセプトを具現化しました。
複数のアイデアのモックをエンジニアが作成しデザイナーと検討した結果、視覚的にユニークな効果を持つメタボールを採用しました。

採用を見送ったモックの紹介

採用を見送ったモックの一部を紹介します。

モック1:

 

モック2:

 

モック3:

 

これらのモックは GLSL やThree.jsのパーティクル技術を使用して実装しましたが、パフォーマンスの観点や世界観のズレなどの理由から採用を見送りました。
CADCのオープニング映像で用いられた3Dモデルから着想を得て、WebGLを駆使した3D表現に挑戦しました。

実装

これらの過程を経て、チーム内で最終アウトプットの認識を共通化できました。 この項では、具体的な実装について実際のコードを交えて紹介します。

技術選定

エンドロールページの実装にあたり ReactReact Three Fiber(以下R3F) を採用しました。

選定理由
  1. 開発スピード:
    開発メンバーがReactに書き慣れており、最も開発パフォーマンスを発揮できるためです
  2. 柔軟性と再利用性:
    Reactのコンポーネントベースのアプローチは、3Dオブジェクトやアニメーションの再利用を容易にし、開発効率を向上させます。また3D表現をする上で複雑な状態管理をシンプルに実装できるためです
懸念
  1. パフォーマンスの課題:
    大規模な3Dシーンや複雑なアニメーションでは、パフォーマンスが問題になる可能性があります
  2. 自由度の低さ:
    Reactに依存することで、Reactのエコシステム内での実装に制限されます。細かい挙動やUIの調整が難しい場合もあります

これらの懸念を踏まえつつも、短期間の開発スケジュールで最もパフォーマンスを発揮できるReactとR3Fを採用し、以下の具体的な実装に取り組みました。

メタボールの実装

メタボールの実装にはマーチングキューブ法を用いています。マーチングキューブ法とは、ボリュームデータから等値面を抽出し、3Dモデルを生成するためのアルゴリズムです。このアルゴリズムを使用することで、メタボールの特徴的な滑らかな曲面を再現することができます。
ここでは詳細の説明は省きますが気になる方は以下の論文などで理解を深めることができます。

https://www.cs.toronto.edu/~jacobson/seminar/lorenson-and-cline-1987.pdf

メタボールの実装

今回はR3Fのユーティリティライブラリである react-three/drei を使用して、簡単にマーチングキューブ法使ったメタボールの実装方法を紹介します。

import { MarchingCube, MarchingCubes } from "@react-three/drei";
import type FC from "react";
import * as THREE from "three";

type MetaballGeometryProps = {
  color: THREE.Color;
  position: [number, number, number];
};

export const MetaballGeometry: React.FC<MetaballGeometryProps> = ({
  color,
  position,
}) => {
  return (
    <group position={position}>
      <MarchingCube strength={0.8} subtract={13} color={color} />
    </group>
  );
};

export const Metaball: FC = () => {
  return (
    <MarchingCubes
      resolution={80}
      maxPolyCount={20000}
      enableUvs={true}
      enableColors
    >
      <meshStandardMaterial vertexColors roughness={0.3} />
      <MetaballGeometry
        color={new THREE.Color("#00bfff")}
        position={[-0.25, 0.25, 0]}
      />
      <MetaballGeometry
        color={new THREE.Color("#00bfff")}
        position={[0.3, 0.1, 0.1]}
      />
      <MetaballGeometry
        color={new THREE.Color("#00bfff")}
        position={[0, -0.1, -0.1]}
      />
    </MarchingCubes>
  );
};

上記のMetaballコンポーネントをシーンに追加すると以下のようなメタボールが描画されます。マーチングキューブ法によってそれぞれの球体の境界面が形成されていることが分かります。

レンダリング結果

細部の調整

メタボールの細部を調整するために、lil-guiライブラリ を使用しました。lil-guiは、GUIを作成しパラメータを動的に調整できる便利なツールです。
以下に、lil-guiを用いてメタボールの色を調整する例を示します。

lil-guiのインストール
# 使用しているパッケージマネージャでインストールしてください
pnpm add -D lil-gui
コントロールパネルの作成

lil-guiを使ってコントロールパネルを作成し、パラメータを設定します。
(例ではcolor値をコントロールパネルによって操作しメタボールの色を調整できるようにします。)

import { MarchingCube, MarchingCubes } from "@react-three/drei";
import GUI from "lil-gui";
import type FC from "react";
import { useEffect, useState } from "react";
import * as THREE from "three";

type MetaballGeometryProps = {
  color: THREE.Color;
  position: [number, number, number];
};

const MetaballGeometry: FC<MetaballGeometryProps> = ({
  color,
  position,
}) => {
  return (
    <group position={position}>
      <MarchingCube strength={0.8} subtract={13} color={color} />
    </group>
  );
};

export const Metaball: FC = () => {
  const [color, setColor] = useState(new THREE.Color("#00bfff"));

  useEffect(() => {
    // lil-guiのインターフェース設定
    const gui = new GUI();
    gui
      .addColor({ color }, "color")
      .name("Color")
      .onChange((value: string) => {
        setColor(new THREE.Color(value));
      });

    return () => {
      gui.destroy();
    };
  }, []);

  return (
    <MarchingCubes
      resolution={80}
      maxPolyCount={20000}
      enableUvs={true}
      enableColors
    >
      <meshStandardMaterial vertexColors roughness={0.3} />
      <MetaballGeometry color={color} position={[-0.25, 0.25, 0]} />
      <MetaballGeometry color={color} position={[0.3, 0.1, 0.1]} />
      <MetaballGeometry color={color} position={[0, -0.1, -0.1]} />
    </MarchingCubes>
  );
};
アウトプット

コントロールパネルがWeb上に表示され、パネルを操作することで色をリアルタイムに調整することが可能になります。質感や位置など様々なパラメータを追加することで感覚的に細部を調整することが可能になります。lil-guiを使用することで、開発効率の向上と視覚的な品質の強化が期待できます。

シームレスさの追求

トップページから拡張世界(エンドロールページ)への遷移を表現したいかつ、ユーザー体験をシームレスに保つため、トップページ⇨エンドロールページ遷移のみView Transition APIを活用しました。このAPIにより、ページの遷移時にスムーズなアニメーション効果を実現できます。

View Transition APIの概要

View Transition APIは、ブラウザのレンダリングパイプラインを制御するためのAPIで、ページ遷移やコンポーネントの変化をアニメーションさせることができます。

Astroでは ViewTransitionsルーティングコンポーネント が提供されており、これをheadタグ内に埋め込むだけで簡単にView Transition APIを使用できます。

未対応ブラウザへのアプローチ

View Transition APIは最新の主要ブラウザでサポートされていますが、特定のバージョン以上でのみ機能します。

https://caniuse.com/?search=view%20transition%20api

ViewTransitionsルーターにはView Transition APIに対応していないブラウザに対するフォールバックサポートがデフォルトで含まれています。
実装例

<html lang="ja">
  <head>
    <ViewTransitions fallback="none"/>
    〜〜省略〜〜
  </head>
  <body>
    〜〜省略〜〜
  </body>
</html>

開発者ツールでHTMLソースを確認すると、以下のmetaタグが追加されています。

<meta name="astro-view-transitions-enabled" content="true">
<meta name="astro-view-transitions-fallback" content="none">

トランジションディレクティブ

.astroコンポーネント内の旧ページと新ページの対応する要素にtransition:*ディレクティブを指定することでナビゲーション中のページ遷移動作を細かく制御できます。

  • transition:name: 新旧コンテンツのアニメーションに関するAstroデフォルトの要素のマッチング方式をオーバーライドし、DOM要素のペアを関連付けるトランジション名を指定できます
  • transition:animate: アニメーションの種類を指定することで、旧要素を新要素で置き換える際のAstroのデフォルトのアニメーションをオーバーライドできます。Astroの組み込みのアニメーションディレクティブを使用するか、独自のトランジションアニメーションを作成します
  • transition:persist: 旧要素を新要素で置き換えるAstroのデフォルトの挙動をオーバーライドし、別のページに移動した際にコンポーネントとHTML要素を保持します

Astro Docs より抜粋

具体的な使用例

以下はAstroでView Transition APIを使用しカスタムアニメーションを適用した例です。
旧ページと新ページの対応するコンポーネントにtransition:nameを共通の値で指定します。
旧ページ⇨新ページのアニメーションをカスタムする際には、新ページのコンポーネントにtransition:animateを指定してカスタムアニメーションを設定します。

旧ページの実装

//旧ページ
---
---

<section id="container" class="container" transition:name="firstview">
  <div class="children">
    <slot />
  </div>
</section>

新ページの実装

// 新ページ
---
const anim = {
  old: {
    name: "fadeOut",
    duration: "1s",
    easing: "easeOut",
    fillMode: "forwards",
  },
  new: {
    name: "fadeIn",
    duration: "1.2s",
    easing: "easeIn",
    fillMode: "forwards",
  },
};

const myFade = {
  forwards: anim,
  backwards: anim,
};
---

<section transition:name="firstview" transition:animate={myFade}>
  <div class="children">
    <slot />
  </div>
</section>

<style>
  @keyframes fadeOut {
    0% {
      opacity: 1;
    }

    100% {
      opacity: 0;
    }
  }

  @keyframes fadeIn {
    0% {
      opacity: 0;
    }
    100% {
      opacity: 1;
    }
  }
</style>

View Transition APIを使用した結果

View Transition APIを活用すると、ページ遷移時の画面のちらつきを抑えられ、シームレスな挙動を実現できます

View Transition APIを使用していない

 

View Transition APIを使用:

 

エンドロール開発のまとめ

今回のエンドロールページの制作を通じて、多くの学びを得ることができました。特に、WebGLを用いた表現ではクオリティが求められ、細部まで作り込むことで魅力的な表現になることを実感しました。以下に具体的なポイントをまとめます。

学びと成果

WebGLを用いた3D表現は、細部まで丁寧に作り込むことで視覚的なインパクトが増し、全体の完成度が高まることを実感しました。モックアップを実装することでデザインと技術が密接に連携し、短い開発期間の中で一貫したビジュアル体験を提供できました。しかし、通常のLP制作と比べて工数がかかるため、効率的な作業フローの構築が鍵となります。

技術的な挑戦と課題

ReactとR3Fを用いたアプローチは、開発スピードと柔軟性を確保する上で効果的でしたが、理想ではアイデアが固まった段階でReactを取り外し、プレーンなThree.jsで再実装することを目指していました。理想は叶いませんでしたが、今回の経験を踏まえて今後さらなる向上を目指します。

今後の展望

3Dモデルを活用すると、特に複雑な形状や質感・リアルな動きを表現でき、表現の幅が広がり、より面白いものが作成できることを実感しました。今後は効率的な3Dモデルの活用に加え、大規模な3Dシーンを扱う際にパフォーマンスと開発効率のバランスを取るため、より適切な技術やアプローチを探ることが重要です。例えば、Doraco Loaderなどを用いて比較的データ量の多い3Dモデルファイルを圧縮し効率的に読み込む手法を検討し、より最適な実装方法を見つけていきたいと考えています。

おわりに

CADC2024 の LP 開発の裏側について、開発チームのエンジニア4名が技術的な観点から深堀って解説しました。

ご精読いただきありがとうございました。

アバター画像
22年新卒入社 グループIT推進本部 CIU所属 ソフトウェアエンジニア・Next Experts(TypeScript)|Organizer for Meguro.es
アバター画像
2022年新卒入社 Webフロントエンドエンジニア CL事業部で体験設計とWebの開発をしています。
アバター画像
2023年新卒入社のwebフロントエンドエンジニアです。現在はFanbase事業部でweb開発を行っています。
アバター画像
フロントエンドエンジニアとして2023新卒入社。現在は株式会社シロクで N organic や GLOWLAB などを展開する「シロクオンラインショップ」の開発・運用を行っています。