トップページのアニメーションについて

この度、トップページにインパクトを持たせるため、Three.jsを使用して動的なアニメーションを制作しました。

Three.jsは3D空間をブラウザ上で表現するためのライブラリであり、今回のようなアニメーション効果の実装に最適です。

特に、視覚的な面白さと自然さを両立するため、頂点の動きやカメラワークにこだわり、ユーザーに印象的な体験を提供できるよう工夫しました。

以下に、各アニメーションの構成要素やその狙いについて詳細に解説していきます。

1. ランダムに生成した頂点

まず、アニメーションの基礎となる「頂点」をランダムに配置しました。約300の頂点を生成し、3D空間上にばらばらに配置することで、複雑で奥行きのある構造が生まれます。このランダムな頂点配置によって、同じシーンでも角度やカメラの動きに応じて異なる印象を与えることができ、訪問者がサイトにアクセスするたびに新鮮さを感じられるようになっています。

// 頂点の作成
const vertices = [];
for (let i = 0; i < 300; i++) {
    vertices.push(new THREE.Vector3(
        (Math.random() - 0.5) * 10,
        (Math.random() - 0.5) * 10,
        (Math.random() - 0.5) * 10
    ));
}

2. カメラの動きとその設計

アニメーションのもう一つの大きな特徴は「カメラの動き」です。カメラの動きはランダムに動かすのではなく、一定の周期と目標を設定することで、ユーザーに自然な視覚体験を提供しています。

5秒ごとの目標位置設定

カメラには、5秒ごとに新しい「目標位置」を設定するようにしています。具体的には、ランダムな場所に新しい目標を決め、そこに向かってカメラが移動する仕組みです。この動きを取り入れることで、シーンにゆっくりとした変化が生まれ、画面に緊張感を持たせています。

中央を注視するカメラ

カメラは常に空間の中心を注視しています。つまり、カメラが動きながらも視線は一貫して中央に固定されている状態です。これにより、視点がブレずに安定感を与え、訪問者が視覚的に混乱することを避けています。カメラが動き続けても、中央に注視していることで「中心に吸い寄せられるような」不思議な感覚が生まれます。

線形でない移動の実現

カメラの移動は、直線的な動きではありません。目標位置に一直線に向かうよりも、少し「暴走する車」をイメージした動きにしています。

具体的には、カメラが進む方向を目標座標に近づけることで、滑らかで自然な動きを実現しています。

この「向かっている方向を傾けていく」方法は、動的なカメラワークの手法であり、シーンに対して緩やかな変化をもたらしています。

カメラアニメーションの概念図
function animate(time) {
    requestAnimationFrame(animate);

    // 5秒ごとに目標位置を更新
    if (time - lastUpdateTime > 5000) {
        updateTargetPosition();
        lastUpdateTime = time;
    }

    // カメラが目標位置の近くに来たら目標位置を更新
    if (camera.position.distanceTo(targetPosition) < 1) {
        updateTargetPosition();
    }

    // カメラの移動方向を目標位置に向けてゆるやかに更新
    direction.lerp(targetPosition.clone().sub(camera.position).normalize(), 0.02);

    // カメラを移動
    camera.position.add(direction.clone().multiplyScalar(speed));

    // カメラが常に中央を見る
    camera.lookAt(scene.position);

    renderer.clear();
    renderer.render(scene, camera);
}

3. 頂点同士の接続方法

各頂点はランダムに配置されているため、そのままでは面白みにかけました。そこで、頂点同士を線でつなぐことで、より一体感のあるシーンを作り上げました。また、キーワードの「共創」にもかけています。

ドロネー分割の代替手法

頂点同士の接続には、本来ドロネー分割を使用したかったのですが、Three.jsの範囲内で3D空間におけるドロネー分割は実現が難しいため、代替手法を採用しました。

具体的には、各頂点から最も近い上位5つの頂点を選び、そこに線を引くことで接続を行っています。

この方法により、ランダムに生成された頂点同士が自然な形でつながり、複雑でありながらも規則性を感じさせる構造が出来上がりました。

線の配置と美しさのバランス

頂点の接続において、線の数が増えすぎると画面が乱雑に見える可能性があるため、近接する5つの頂点に限定しました。

これにより、シンプルでありながらも豊かなネットワークが構築され、訪問者に視覚的な面白さを与えます。

この手法は、ドロネー分割のように完全な構造ではありませんが、それでも自然なネットワークのように見えます。

// 距離が近い点同士を線で結ぶ
const maxDistance = 5.0; // 最大距離を5に設定
const edges = [];
const colors = [];
for (let i = 0; i < vertices.length; i++) {
    const distances = [];
    for (let j = 0; j < vertices.length; j++) {
        if (i !== j) {
            const distance = vertices[i].distanceTo(vertices[j]);
            if (distance < maxDistance) {
                distances.push({ index: j, distance: distance });
            }
        }
    }
    distances.sort((a, b) => a.distance - b.distance);
    for (let k = 0; k < Math.min(5, distances.length); k++) { // 最大5本に制限
        const j = distances[k].index;
        const distance = distances[k].distance;
        edges.push(vertices[i].x, vertices[i].y, vertices[i].z);
        edges.push(vertices[j].x, vertices[j].y, vertices[j].z);
        const alpha = Math.min(0.1, 1.0 - (distance / maxDistance)); // アルファ値を最大0.1に制限
        colors.push(1.0, 1.0, 1.0, alpha);
        colors.push(1.0, 1.0, 1.0, alpha);
    }
}

const lineGeometry = new THREE.BufferGeometry();
lineGeometry.setAttribute('position', new THREE.Float32BufferAttribute(edges, 3));
lineGeometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 4));
const lineMaterial = new THREE.LineBasicMaterial({
    vertexColors: true,
    transparent: true,
    blending: THREE.AdditiveBlending
});
lines = new THREE.LineSegments(lineGeometry, lineMaterial);
scene.add(lines);

4. 頂点の描画スタイル

最後に、頂点そのものの描画について説明します。

頂点の描画にはシェーダーを使用し、ふわっとした円形のデザインを採用しました。

ふわっとした円形の描画

各頂点は柔らかい円形として描画されています。この円形の頂点は、描画時にシェーダーで生成することで、頂点が揺れるような動きを見せながらも自然に見えるように調整しています。

このふわっとした円形は、頂点がただの「点」ではなく「空間に漂うオブジェクト」のように見せる効果があります。

// グロー効果のシェーダーマテリアルを作成
const vertexShader = `
    varying vec3 vColor;
    void main() {
        vColor = color;
        vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
        gl_PointSize = 0.1 * (300.0 / -mvPosition.z); // 点のサイズを調整
        gl_Position = projectionMatrix * mvPosition;
    }
`;

シェーダーの使用と演出効果

シェーダーを用いることで、単なる幾何学的なオブジェクトではなく、動きのあるオブジェクトとして頂点を表現できました。

シェーダーによる円形描画は、ブラウザ上で滑らかに動くため、訪問者に「空間に存在する柔らかな粒子」のような印象を与えることができています。

まとめ

このトップページのアニメーションは、Three.jsを最大限に活用し、動きのあるインタラクティブな空間を構築しています。カメラの滑らかな動きや頂点の接続、シェーダーによる柔らかい描画スタイルなどが一体となり、訪問者に没入感を提供するデザインに仕上がっています。カメラや頂点の動きによって、サイト全体が「生きている」ように感じられるため、印象に残りやすく、訪問者の興味を引きつけることができます。

このようなアニメーション効果を取り入れることで、単なる静的なページではなく、訪問者が楽しみながら体験できるページデザインを実現しました。今後も、ユーザー体験を重視したデザインの工夫を続けていきたいと考えています。

全体のソースコードはこちらにアップしています。
ご自由にご活用ください。

上部へスクロール