見出し画像

AQUARINGサイトリニューアル フロントエンドBreakdown


こんにちは、AQUARING かに です。
今回は昨年12月にリニューアルしたAQUARINGサイトのフロントエンドについて解説していきます。

WebGL表現

まずGLSLとp5.jsで2種類のスケッチを作成しました。
メタボール円のスケッチ(GLSL)

画像1

距離関数を用いてふたつの円を重ねて、44行目のstep関数を通すことで画像を二値化してマスクを作成し、最後に水色で塗っています。
この行をコメントアウトすると二値化前の状態が確認できます。

mask = step(0.5, mask);


マウスで円がもにょもにょ動くスケッチ(p5.js)

画像2

p5.jsでの実装では正多角形で円形を表現することになり、マウスが境界にきたときにカクカクなのがバレてしまうのと、GLSLで実装するようななめらかなメタボール表現が作りにくいため、最終的に three.js + GLSL での実装となりました。(p5.jsでもGLSLは扱えますが、パフォーマンスの観点からthree.jsにしました)


メニュー内のホバー及びカテゴリトップページのメインビジュアルのアニメーションは、メタボール円を描画するGLSLのuniform変数をGSAPから更新して動かしています。

↓フラグメントシェーダーのuniform変数の一部

uniform vec3 uLowerBackgroundColor;// 下層ページの背景色
uniform vec3 uLowerRingColor;// 下層ページの円の描画色
uniform vec2 uLowerRingPositionArr[8];// 下層ページの円の座標の配列
uniform float uLowerRingRadiusArr[8];// 下層ページの円の半径の配列
uniform float uLowerRingLineWidthArr[8];// 下層ページの円の線幅の配列

GLSLでは可変長配列が扱えないため、同時に表示する円の数を最大8個までとして、それぞれの位置、半径、線幅を配列で定義して各アニメーションで変数を共有しています。

↓three.jsでShaderMaterialを作っているところ

const mat = new ShaderMaterial({
  transparent: true,
  uniforms: {
    ...
    uLowerBackgroundColor: { value: (isBackground ? vecColorWhite : vecColorGray2).clone() },
    uLowerRingColor: { value: (isBackground ? vecColorWhite : vecColorGray).clone() },
    uLowerRingPositionArr: { value: new Array(ARTWORK_LOWER_RING_NUM).fill(0).map(() => new Vector2(0, 0)) },
    uLowerRingRadiusArr: { value: new Array(ARTWORK_LOWER_RING_NUM).fill(0.0) },
    uLowerRingLineWidthArr: { value: new Array(ARTWORK_LOWER_RING_NUM).fill(0.0) },
    ...
  },
  vertexShader,
  fragmentShader
});

↓GSAPでひとつめの円を開くアニメーションを定義しているところ

const animation = gsap.timeline();
const obj = { radius: 0, lineWidth: 0 };
const target = { radius: 150, lineWidth: 30 };
animation.to(obj, {
 radius: target.radius,
 lineWidth: target.lineWidth,
 duration: 1,
 ease: 'back.inOut(1.7)',
 onUpdate: () => {
   uniforms.uLowerRingRadiusArr.value[0] = obj.radius;
   uniforms.uLowerRingLineWidthArr.value[0] = obj.lineWidth;
 }
}, 'open-main');

GSAPのto関数の第一引数にはObjectが入る仕様のため、一時Objectを作ってonUpdateでuniformを更新しています。

ABOUTページの一連のWebGL演出もすべてGLSLとGSAPだけで実装しているので、ぜひご覧ください!

マウスストーカー

マウスストーカーも上記と同じWebGL(GLSL + GSAP)で実装しています。

背景のcanvas一枚だけだとDOM要素が上にきたときにカーソルが背景にある不自然な見た目になってしまうため、前景canvasと背景canvasでDOMを挟みこむ形で配置しています。

画像3

最背面
↓背景canvas(背景のWebGL + メタボール表現のため背景にもカーソル)
↓DOM要素
↓前景canvas(カーソル + メニュー内のWebGL)
最前面

通常、全画面の要素を手前に持ってきてしまうと、マウスイベントがその要素に取られてしまって下側に存在する要素を選択できなくなってしまいますが、CSSの「pointer-events: none;」 ​を設定することで前景canvasのマウスイベントを透過させて下にあるDOM要素を触れるようにしています。

慣性スクロール

WebGLのcanvas要素が全画面fixedでDOM要素をスクロール追従する演出のため、そのまま実装するとドキュメントのスクロール更新レートとJSのrequestAnimationFrameの更新レートが合わずジャンク(ガタつき)が発生します。

これを防ぐためにはドキュメントとWebGLのスクロールを同時に更新する必要があるため、ページ全体に慣性スクロールを適用しています。

非同期遷移

ページ間の非同期遷移にはbarba.jsを使用しました。

フロントエンド技術だけで考えればNuxt.jsNext.jsなどのフレームワークでもできるのですが、AQUARINGサイトはSitecoreというCMSでHTMLモジュールとコンテンツの管理を行っているため、CMSから出力されたHTMLの状態からでも利用できるbarba.jsを採用しました。

非同期遷移が入ることで、通常では考える必要のないトランジション前後のインスタンスの死活管理や更新処理、WebGLアニメーションのシームレスな切り替えなど、いろいろ気を遣わないといけない部分が多く大変でした。

さいごに

プレスリリース直前にIE11のプライベートモードだけイントロ動画が再生されない謎現象が発覚したときは本当に肝が冷えました、、
(結局プレスリリースまで残り1 ~ 2分というところでテスト環境のBasic認証のせいだと判明して大丈夫だったのですが、、デバッグ神ありがとうのきもち⛩🙏)

いつもは自分ひとりで実装することが多いWebGL案件ですが、今回は自社サイトのリニューアルということでユニットメンバーみんなを巻き込んだ総力戦でした。
普段他のメンバーは自分ほどWebGLに触れる機会が多くないので、今回の実装をきっかけに社内のJSスキルのベースアップに繋がっていくといいなと思います。引き続きWebGL勉強会などもやっていきたいですね。

この記事が気に入ったらサポートをしてみませんか?