<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>好きな生き物 - リアル3Dアニメーション版</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<style>
:root {
--bg-color: #e2e8f0;
--card-bg: #ffffff;
}
body {
font-family: 'Helvetica Neue', Arial, 'Hiragino Kaku Gothic ProN', 'Hiragino Sans', Meiryo, sans-serif;
background-color: var(--bg-color);
margin: 0;
padding: 20px;
display: flex;
flex-direction: column;
align-items: center;
color: #333;
}
h1 {
color: #1e293b;
margin-bottom: 30px;
border-bottom: 4px solid #3b82f6;
padding-bottom: 10px;
font-size: 2rem;
}
.animal-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
gap: 20px;
width: 100%;
max-width: 1200px;
}
.animal-container {
background: var(--card-bg);
border-radius: 24px;
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
padding: 24px;
text-align: center;
transition: transform 0.3s ease;
}
.animal-container:hover {
transform: translateY(-5px);
}
h2 {
margin-top: 0;
padding: 12px;
border-radius: 12px;
color: #fff;
font-size: 1.5rem;
text-shadow: 1px 1px 3px rgba(0,0,0,0.3);
}
.canvas-wrapper {
width: 100%;
height: 350px;
border-radius: 16px;
background: radial-gradient(circle, #ffffff 0%, #e2e8f0 100%);
cursor: move;
overflow: hidden;
margin-top: 15px;
border: 1px solid #cbd5e1;
}
p {
margin-top: 15px;
font-weight: 600;
color: #475569;
font-size: 1.1rem;
}
.controls-hint {
font-size: 0.75rem;
color: #94a3b8;
margin-top: 8px;
display: flex;
align-items: center;
justify-content: center;
gap: 4px;
}
</style>
</head>
<body>
<h1>リアルな3Dの生き物たち</h1>
<div class="animal-grid">
<div class="animal-container">
<h2 style="background-color: #d97706;">柴犬 (Shiba Inu)</h2>
<div id="shiba-canvas" class="canvas-wrapper"></div>
<p>愛嬌たっぷりに体を揺らしているよ</p>
<div class="controls-hint">🖱️ ドラッグで回転 / スクロールでズーム</div>
</div>
<div class="animal-container">
<h2 style="background-color: #db2777;">うさぎ (Rabbit)</h2>
<div id="rabbit-canvas" class="canvas-wrapper"></div>
<p>長い耳をなびかせてジャンプ!</p>
<div class="controls-hint">🖱️ ドラッグで回転 / スクロールでズーム</div>
</div>
<div class="animal-container">
<h2 style="background-color: #2563eb;">イルカ (Dolphin)</h2>
<div id="dolphin-canvas" class="canvas-wrapper"></div>
<p>ダイナミックに海を跳ねる芸!</p>
<div class="controls-hint">🖱️ ドラッグで回転 / スクロールでズーム</div>
</div>
</div>
<script>
const animals = [];
function createScene(containerId) {
const container = document.getElementById(containerId);
const width = container.clientWidth;
const height = container.clientHeight;
const scene = new THREE.Scene();
scene.background = null;
const camera = new THREE.PerspectiveCamera(40, width / height, 0.1, 1000);
camera.position.set(6, 4, 10);
camera.lookAt(0, 0, 0);
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(width, height);
renderer.setPixelRatio(window.devicePixelRatio);
container.appendChild(renderer.domElement);
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
scene.add(ambientLight);
const spotLight = new THREE.SpotLight(0xffffff, 0.8);
spotLight.position.set(10, 20, 10);
scene.add(spotLight);
const group = new THREE.Group();
scene.add(group);
// インタラクション
let isDragging = false;
let previousMousePosition = { x: 0, y: 0 };
container.addEventListener('mousedown', () => isDragging = true);
window.addEventListener('mouseup', () => isDragging = false);
container.addEventListener('mousemove', e => {
if (isDragging) {
const deltaMove = {
x: e.offsetX - previousMousePosition.x,
y: e.offsetY - previousMousePosition.y
};
group.rotation.y += deltaMove.x * 0.01;
group.rotation.x += deltaMove.y * 0.01;
}
previousMousePosition = { x: e.offsetX, y: e.offsetY };
});
// ズーム
container.addEventListener('wheel', e => {
e.preventDefault();
camera.position.z += e.deltaY * 0.01;
camera.position.z = Math.max(5, Math.min(20, camera.position.z));
}, { passive: false });
return { scene, camera, renderer, group };
}
// --- 柴犬の構築 ---
function setupShiba() {
const { scene, camera, renderer, group } = createScene('shiba-canvas');
const colors = { main: 0xdc8d45, belly: 0xffffff, nose: 0x333333, eye: 0x111111 };
const shiba = new THREE.Group();
// 胴体
const body = new THREE.Mesh(new THREE.BoxGeometry(2.2, 1.4, 1.4), new THREE.MeshPhongMaterial({color: colors.main}));
shiba.add(body);
// お腹の白
const belly = new THREE.Mesh(new THREE.BoxGeometry(1.8, 0.2, 1.42), new THREE.MeshPhongMaterial({color: colors.belly}));
belly.position.y = -0.65;
shiba.add(belly);
// 頭部グループ
const headGroup = new THREE.Group();
headGroup.position.set(1.4, 0.8, 0);
const head = new THREE.Mesh(new THREE.BoxGeometry(1.2, 1.1, 1.1), new THREE.MeshPhongMaterial({color: colors.main}));
headGroup.add(head);
// 鼻(マズル)
const muzzle = new THREE.Mesh(new THREE.BoxGeometry(0.5, 0.5, 0.6), new THREE.MeshPhongMaterial({color: colors.belly}));
muzzle.position.set(0.6, -0.1, 0);
headGroup.add(muzzle);
const nose = new THREE.Mesh(new THREE.BoxGeometry(0.15, 0.15, 0.2), new THREE.MeshPhongMaterial({color: colors.nose}));
nose.position.set(0.85, 0, 0);
headGroup.add(nose);
// 目
const eyeGeo = new THREE.SphereGeometry(0.08, 8, 8);
const eyeL = new THREE.Mesh(eyeGeo, new THREE.MeshPhongMaterial({color: colors.eye}));
eyeL.position.set(0.6, 0.2, 0.3);
headGroup.add(eyeL);
const eyeR = eyeL.clone();
eyeR.position.z = -0.3;
headGroup.add(eyeR);
// 耳
const earGeo = new THREE.ConeGeometry(0.2, 0.5, 4);
const earL = new THREE.Mesh(earGeo, new THREE.MeshPhongMaterial({color: colors.main}));
earL.position.set(0.2, 0.7, 0.35);
headGroup.add(earL);
const earR = earL.clone();
earR.position.z = -0.35;
headGroup.add(earR);
shiba.add(headGroup);
// 足 (4本)
const legGeo = new THREE.BoxGeometry(0.3, 0.8, 0.3);
const createLeg = (x, z) => {
const leg = new THREE.Mesh(legGeo, new THREE.MeshPhongMaterial({color: colors.main}));
leg.position.set(x, -1, z);
const paw = new THREE.Mesh(new THREE.BoxGeometry(0.35, 0.2, 0.35), new THREE.MeshPhongMaterial({color: colors.belly}));
paw.position.y = -0.4;
leg.add(paw);
return leg;
};
shiba.add(createLeg(0.8, 0.45));
shiba.add(createLeg(0.8, -0.45));
shiba.add(createLeg(-0.8, 0.45));
shiba.add(createLeg(-0.8, -0.45));
// 尻尾 (くるん)
const tail = new THREE.Mesh(new THREE.TorusGeometry(0.3, 0.15, 8, 16, Math.PI), new THREE.MeshPhongMaterial({color: colors.main}));
tail.position.set(-1.1, 0.8, 0);
tail.rotation.y = Math.PI / 2;
shiba.add(tail);
group.add(shiba);
animals.push({
renderer, scene, camera, update: (t) => {
shiba.rotation.z = Math.sin(t * 2) * 0.05;
headGroup.rotation.y = Math.sin(t * 3) * 0.1;
}
});
}
// --- うさぎの構築 ---
function setupRabbit() {
const { scene, camera, renderer, group } = createScene('rabbit-canvas');
const colors = { body: 0xffffff, innerEar: 0xffd1dc, eye: 0xff0000 };
const rabbit = new THREE.Group();
// 胴体 (楕円球)
const body = new THREE.Mesh(new THREE.SphereGeometry(1.1, 20, 20), new THREE.MeshPhongMaterial({color: colors.body}));
body.scale.set(1.3, 1, 1);
rabbit.add(body);
// 頭
const headGroup = new THREE.Group();
headGroup.position.set(1.2, 0.6, 0);
const head = new THREE.Mesh(new THREE.SphereGeometry(0.7, 20, 20), new THREE.MeshPhongMaterial({color: colors.body}));
headGroup.add(head);
// 目
const eyeGeo = new THREE.SphereGeometry(0.08, 8, 8);
const eyeL = new THREE.Mesh(eyeGeo, new THREE.MeshPhongMaterial({color: colors.eye}));
eyeL.position.set(0.5, 0.1, 0.3);
headGroup.add(eyeL);
const eyeR = eyeL.clone();
eyeR.position.z = -0.3;
headGroup.add(eyeR);
// 耳 (長く)
const earGeo = new THREE.CylinderGeometry(0.1, 0.2, 1.4, 12);
const earL = new THREE.Mesh(earGeo, new THREE.MeshPhongMaterial({color: colors.body}));
earL.position.set(0, 1.2, 0.25);
earL.rotation.z = -0.2;
const earInside = new THREE.Mesh(new THREE.PlaneGeometry(0.15, 1), new THREE.MeshBasicMaterial({color: colors.innerEar, side: THREE.DoubleSide}));
earInside.position.set(0.1, 0, 0);
earInside.rotation.y = Math.PI/2;
earL.add(earInside);
headGroup.add(earL);
const earR = earL.clone();
earR.position.z = -0.25;
headGroup.add(earR);
rabbit.add(headGroup);
// 尻尾
const tail = new THREE.Mesh(new THREE.SphereGeometry(0.3, 12, 12), new THREE.MeshPhongMaterial({color: colors.body}));
tail.position.set(-1.2, -0.2, 0);
rabbit.add(tail);
// 足
const footGeo = new THREE.SphereGeometry(0.3, 12, 12);
const footF = new THREE.Mesh(footGeo, new THREE.MeshPhongMaterial({color: colors.body}));
footF.scale.set(1.5, 0.5, 1);
footF.position.set(0.8, -0.9, 0.4);
rabbit.add(footF);
const footFR = footF.clone(); footFR.position.z = -0.4; rabbit.add(footFR);
group.add(rabbit);
animals.push({
renderer, scene, camera, update: (t) => {
const jump = Math.abs(Math.sin(t * 4));
group.position.y = jump * 2;
body.scale.y = 1 - jump * 0.2; // 着地時のつぶれ
earL.rotation.x = Math.sin(t * 8) * 0.1;
earR.rotation.x = -Math.sin(t * 8) * 0.1;
}
});
}
// --- イルカの構築 ---
function setupDolphin() {
const { scene, camera, renderer, group } = createScene('dolphin-canvas');
const colors = { body: 0x4a90e2, belly: 0xddeeff, eye: 0x222222 };
const dolphin = new THREE.Group();
// 胴体 (流線型)
const segments = 5;
for(let i=0; i<segments; i++) {
const s = 1 - (i/segments)*0.6;
const seg = new THREE.Mesh(new THREE.SphereGeometry(0.8 * s, 20, 20), new THREE.MeshPhongMaterial({color: colors.body}));
seg.position.x = i * -0.8 + 1;
seg.scale.y = 0.9;
dolphin.add(seg);
// お腹の白
const bSeg = new THREE.Mesh(new THREE.SphereGeometry(0.7 * s, 16, 16), new THREE.MeshPhongMaterial({color: colors.belly}));
bSeg.position.set(i * -0.8 + 1, -0.3, 0);
dolphin.add(bSeg);
}
// 頭部・口先
const nose = new THREE.Mesh(new THREE.CylinderGeometry(0.2, 0.5, 0.8, 12), new THREE.MeshPhongMaterial({color: colors.body}));
nose.rotation.z = Math.PI/2;
nose.position.set(2, -0.1, 0);
dolphin.add(nose);
// 目
const eyeGeo = new THREE.SphereGeometry(0.07, 8, 8);
const eyeL = new THREE.Mesh(eyeGeo, new THREE.MeshPhongMaterial({color: colors.eye}));
eyeL.position.set(1.4, 0.2, 0.45);
dolphin.add(eyeL);
const eyeR = eyeL.clone();
eyeR.position.z = -0.45;
dolphin.add(eyeR);
// 背びれ
const fin = new THREE.Mesh(new THREE.ConeGeometry(0.4, 0.8, 3), new THREE.MeshPhongMaterial({color: colors.body}));
fin.position.set(-0.2, 0.8, 0);
fin.rotation.x = Math.PI;
dolphin.add(fin);
// 胸びれ
const sideFinGeo = new THREE.BoxGeometry(0.6, 0.1, 0.4);
const sFinL = new THREE.Mesh(sideFinGeo, new THREE.MeshPhongMaterial({color: colors.body}));
sFinL.position.set(0.5, -0.4, 0.7);
sFinL.rotation.y = -0.5;
dolphin.add(sFinL);
const sFinR = sFinL.clone();
sFinR.position.z = -0.7;
sFinR.rotation.y = 0.5;
dolphin.add(sFinR);
// 尾びれ
const tailGroup = new THREE.Group();
tailGroup.position.set(-2.8, 0, 0);
const tailPart = new THREE.Mesh(new THREE.BoxGeometry(0.5, 0.1, 1.5), new THREE.MeshPhongMaterial({color: colors.body}));
tailGroup.add(tailPart);
dolphin.add(tailGroup);
group.add(dolphin);
animals.push({
renderer, scene, camera, update: (t) => {
group.rotation.x = t * 3; // アクロバティック回転
group.position.y = Math.sin(t * 3) * 2;
group.position.x = Math.cos(t * 3) * 1;
tailGroup.rotation.z = Math.sin(t * 10) * 0.5; // 尾びれを振る
}
});
}
function animate() {
requestAnimationFrame(animate);
const time = Date.now() * 0.001;
animals.forEach(animal => {
animal.update(time);
animal.renderer.render(animal.scene, animal.camera);
});
}
window.onload = function() {
setupShiba();
setupRabbit();
setupDolphin();
animate();
window.addEventListener('resize', () => {
const canvasIds = ['shiba-canvas', 'rabbit-canvas', 'dolphin-canvas'];
animals.forEach((animal, index) => {
const container = document.getElementById(canvasIds[index]);
if (!container) return;
const w = container.clientWidth;
const h = container.clientHeight;
animal.camera.aspect = w / h;
animal.camera.updateProjectionMatrix();
animal.renderer.setSize(w, h);
});
});
};
</script>
</body>
</html>
私が好きな動物を集めました。