こんにちは、くりちゃんです!
最近のWebサイトを見ていると、画面遷移が高速でシームレスに遷移するサイトが当たり前のようになってきています。これは非同期遷移と呼ばれる技術で、同一のページ上でHTMLをJavaScriptによって書き換えることで不要なHTTPのリクエストが減り、高速に遷移できことができます。
また非同期遷移を採用することでページ遷移のアニメーションも凝った演出を入れられるので、メリットが多いです。
デメリットとしては、やはり通常の遷移と比べて、HTMLをJavaScriptによって書き換えるので実装難易度が上がること、非同期遷移特有のケアをしないといけない点が出てくるため工数が増える点です。昨今のWeb制作ではNuxt.jsやNext.jsなどのJavaScriptフレームワークの普及によって、非同期遷移を簡単に実現することができます。
しかし、JavaScriptフレームワークを採用するのは学習コストやCMSの導入などを考慮すると、すべての案件に気軽に採用できるわけではなく、弊社ではWordPress案件を担当することが多いです。
そこでWordPress案件でも簡単に非同期遷移を実現するために採用されるのがBarba.jsというライブラリです。今回は、Barba.jsとThree.jsを組み合わせてクリエイティブなWebサイトを実装していきたいと思います。
デモ
https://hisamikurita.github.io/threejs-barbajs-demo/dist/
Three.js
まずは演出の土台となるデザインをThree.jsで実装していきます。今回はシンプルに球体をノイズで変形させてみます。
import { SphereBufferGeometry, RawShaderMaterial, Mesh } from 'three';
import vertexShader from './shaders/vertexshader.vert';
import fragmentShader from './shaders/fragmentshader.frag';
const geometry = new SphereBufferGeometry(0.24, 64, 64);
const material = new RawShaderMaterial({
vertexShader: vertexShader,
fragmentShader: fragmentShader,
uniforms: {
u_noise_length: {
type: "f",
value: 0
},
u_noise_power: {
type: "f",
value: 0
},
u_noise_range: {
type: "f",
value: 0
},
u_time: {
type: "f",
value: 0
},
},
wireframe: false,
});
this.mesh = new Mesh(geometry, material);
attribute vec3 position;
attribute vec3 normal;
attribute vec2 uv;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
uniform float u_noise_length;
uniform float u_noise_power;
uniform float u_noise_range;
uniform float u_time;
varying vec2 vUv;
#pragma glslify: cnoise3 = require(glsl-noise/classic/3d);
void main() {
vUv = uv;
vec3 newPosition = position;
float noiseLength = u_noise_length;
float noisePower = u_noise_power;
float noiseRange = u_noise_range;
float noise = cnoise3(noiseLength * vec3(position.x, position.y, position.z + u_time * 0.010));
newPosition += noiseRange * ((noise * noisePower) * normal);
gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);
}
precision mediump float;
varying vec2 vUv;
void main() {
gl_FragColor = vec4(vUv, 0.0, 1.0);
}
頂点シェーダーで球体の頂点を法線の方向に向かってランダムに動かし、ノイズをカールノイズを使用して、流体のような表現になるようにしています。ノイズはこちらのパッケージを使用させていただきました。
次に各ページでメッシュが変化する時ときのメソッドを用意しておきます。
import { gsap } from 'gsap';
import { CustomEase } from './module/CustomEase';
window.GSAP = gsap;
window.CUSTOMEASE = CustomEase;
GSAP.registerPlugin(CUSTOMEASE);
const CONSTANTS = {
fullDuration: 0.8,
transform: CUSTOMEASE.create('transform', 'M0,0 C0.44,0.05 0.17,1 1,1'),
};
window.CONSTANTS = CONSTANTS;
GSAPなどの各パッケージとサイト共通の定数はどのファイルでも使いたいのでグローバルに設定しておきます。
_setNoise() {
GSAP.to(this.mesh.material.uniforms.u_noise_length, {
duration: CONSTANTS.fullDuration,
ease: CONSTANTS.transform,
value: 80,
});
GSAP.to(this.mesh.material.uniforms.u_noise_power, {
duration: CONSTANTS.fullDuration,
ease: CONSTANTS.transform,
value: 1,
});
GSAP.to(this.mesh.material.uniforms.u_noise_range, {
duration: CONSTANTS.fullDuration,
ease: CONSTANTS.transform,
value: 0.15,
});
GSAP.to(this.mesh.position, {
duration: CONSTANTS.fullDuration,
ease: CONSTANTS.transform,
y: 0,
});
}
_setNoise()が発火した画面
_setSphere() {
GSAP.to(this.mesh.material.uniforms.u_noise_length, {
duration: CONSTANTS.fullDuration,
ease: CONSTANTS.transform,
value: 80,
});
GSAP.to(this.mesh.material.uniforms.u_noise_power, {
duration: CONSTANTS.fullDuration,
ease: CONSTANTS.transform,
value: 0,
});
GSAP.to(this.mesh.material.uniforms.u_noise_range, {
duration: CONSTANTS.fullDuration,
ease: CONSTANTS.transform,
value: 0,
});
GSAP.to(this.mesh.position, {
duration: CONSTANTS.fullDuration,
ease: CONSTANTS.transform,
y: 0,
});
}
_setSphere()が発火した画面
_setCameraZ( {
GSAP.to(this.camera.position, {
duration: CONSTANTS.fullDuration,
ease: CONSTANTS.transform,
z: 0.18,
});
}
_setCameraZ()が発火した画面
_setWireframe() {
GSAP.to(this.mesh.material.uniforms.u_noise_length, {
duration: CONSTANTS.fullDuration,
ease: CONSTANTS.transform,
value: 80,
});
GSAP.to(this.mesh.material.uniforms.u_noise_power, {
duration: CONSTANTS.fullDuration,
ease: CONSTANTS.transform,
value: 1,
});
GSAP.to(this.mesh.material.uniforms.u_noise_range, {
duration: CONSTANTS.fullDuration,
ease: CONSTANTS.transform,
value: 0.06,
});
GSAP.to(this.mesh.position, {
duration: CONSTANTS.fullDuration,
ease: CONSTANTS.transform,
y: -0.22,
});
this.mesh.material.wireframe = true;
}
_setWireframe()が発火した画面
演出の土台となるデザインはこれで決まったので、次はBarba.jsで非同期遷移を実装していきます。
Barba.js
基本的な使い方はシンプルです。@barba/coreをインポートして、オプションを設定していきます。
npm install @barba/core
<body data-barba="wrapper">
// header,footer,canvasは遷移時に再生成させたくないので、barba.jsで書き換える範囲外にする
<div id="webgl"></div>
<header class="header"></header>
<div data-barba="container" data-barba-namespace="index">
・・・ 遷移で書き換えるコンテンツ ・・・
</div>
<footer class="footer"></footer>
</body>
import barba from '@barba/core';
barba.init({
// ...
});
次に上記で用意したメッシュの各メソッドが特定のページに入った時に発火するようにします。barba.jsではviewsという機能が用意されているので、こちらを使用して実現していきます。
barba.init({
views: [{
namespace: 'index',
beforeEnter() {
// index のネームスペースを持つページで発火する
SPHERE._setNoise();
SPHERE._setWireframeReset();
STAGE._setCameraReset();
}
},
{
namespace: 'about',
beforeEnter() {
// about のネームスペースを持つページで発火する
SPHERE._setSphere();
SPHERE._setWireframeReset();
STAGE._setCameraReset();
},
{
namespace: 'service',
beforeEnter() {
// service のネームスペースを持つページで発火する
SPHERE._setNoise();
SPHERE._setWireframeReset();
STAGE._setCameraZ();
},
{
namespace: 'feature',
beforeEnter() {
// feature のネームスペースを持つページで発火する
SPHERE._setWireframe();
STAGE._setCameraWireframe();
},
}]
});
各ページでリセット用のメソッドも用意しておき、どのページにアクセスしても辻褄が合うように調整しましょう。
終わり
最後にbarba.jsは非常に便利なライブラリですが、headタグを書き換える機能は自分で用意しなければならならい点など、残念ながら個人でカバーしないといけない部分も多いです。。
非同期遷移はクオリティの高いWEBサイトには当たり前のように実装されている技術ですが、導入する際はスケジュールや非同期遷移を採用した際に考えられるリスクを十分に把握してから実装するかしないかの判断をすると事故が少なくなりますね。
本記事のデモのデザイン参考サイト↓
Updata one公式サイト
LIGはWebサイト制作を支援しています。ご興味のある方は事業ぺージをぜひご覧ください。