CSS
アニメーション
2026/02/06
2026/2/2
透明感のあるガラス表現や、光を反射する3Dオブジェクトは、それだけで画面の印象をぐっと引き締めてくれます。
このデモでは、反射がきれいなガラスの輪っかと立体的な文字を組み合わせた演出を、できるだけシンプルな構成で作っています。
画面サイズに合わせて自動でレイアウトが調整されるため、PCでもスマホでも破綻しません。
<canvas class="webgl"></canvas>
body {
margin: 0;
overflow: hidden;
background-color: #000;
}
.webgl {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
outline: none;
}
import * as THREE from "https://esm.sh/three@0.173.0";
import {
FontLoader
} from "https://esm.sh/three@0.173.0/examples/jsm/loaders/FontLoader.js";
import {
TextGeometry
} from "https://esm.sh/three@0.173.0/examples/jsm/geometries/TextGeometry.js";
// --- シーン設定 ---
const scene = new THREE.Scene();
const canvas = document.querySelector(".webgl");
const renderer = new THREE.WebGLRenderer({
canvas,
antialias: true
});
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
let torus, textMesh;
// --- フォント読み込み ---
const fontLoader = new FontLoader();
fontLoader.load("https://raw.githubusercontent.com/danielyl123/person/refs/heads/main/fonts/helvetiker_regular.typeface.json",
(font) => {
// 文字を「KUMONOSU」に変更
const textGeometry = new TextGeometry("KUMONOSU", {
font,
size: 1,
depth: 0.2,
curveSegments: 5,
bevelEnabled: true,
bevelThickness: 0.03,
bevelSize: 0.02,
bevelSegments: 4,
});
textGeometry.center();
textMesh = new THREE.Mesh(textGeometry, new THREE.MeshBasicMaterial({
color: 0xffffff
}));
scene.add(textMesh);
handleResize(); // テキスト生成後に位置を確定
});
// --- トーラス(虹色・透過) ---
const torusGeometry = new THREE.TorusGeometry(0.7, 0.4, 100, 60);
const torusMaterial = new THREE.MeshPhysicalMaterial({
metalness: 0,
roughness: 0,
iridescence: 1,
iridescenceIOR: 1.5,
iridescenceThicknessRange: [100, 324],
transmission: 1,
ior: 1.2,
thickness: 0.8,
});
torus = new THREE.Mesh(torusGeometry, torusMaterial);
torus.position.z = 1; // 文字より手前に配置
scene.add(torus);
// --- ライト ---
scene.add(new THREE.AmbientLight(0xffffff, 1));
const addLight = (x, y, z) => {
const light = new THREE.PointLight(0xffffff, 20);
light.position.set(x, y, z);
scene.add(light);
};
addLight(-2, 3, 2);
addLight(2, -3, 2);
// --- レスポンシブ対応 ---
function handleResize() {
const width = window.innerWidth;
const height = window.innerHeight;
const aspect = width / height;
renderer.setSize(width, height);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
camera.aspect = aspect;
// 表示したいコンテンツの想定幅
const contentWidth = 9;
const fovInRadians = (camera.fov * Math.PI) / 180;
if (aspect < 1) {
// 縦長:横幅に合わせる
camera.position.z = (contentWidth / aspect) / (2 * Math.tan(fovInRadians / 2));
} else {
// 横長:固定距離
camera.position.z = contentWidth / (2 * Math.tan(fovInRadians / 2));
}
camera.updateProjectionMatrix();
}
window.addEventListener("resize", handleResize);
handleResize();
// --- アニメーションループ ---
const clock = new THREE.Clock();
const tick = () => {
const elapsedTime = clock.getElapsedTime();
if (torus) {
torus.rotation.x = elapsedTime * 0.5;
torus.rotation.y = elapsedTime * 0.1;
}
renderer.render(scene, camera);
requestAnimationFrame(tick);
};
tick();
このサンプルでは、以下のような表現を行っています。
特別な操作はなく、ページを開くだけで常に画面に収まるバランスで3D演出が表示されます。
輪っか形状は、
が同時に存在するため、ガラス表現の違いがとても分かりやすい形です。
そのためこのデモでは、「ガラスっぽさ」「反射の気持ちよさ」を確認するためのモチーフとしてあえてシンプルな輪っかを使っています。
画面の縦横比に応じて、
を自動で調整しています。
そのため、
どちらでも、文字や輪っかが切れずに自然なサイズで表示されます。
コード内を少し調整するだけで、次のような変更が可能です。
ファーストビュー演出や、ポートフォリオ・デモページの装飾としても使いやすい構成です。