|
<!DOCTYPE html> |
|
<html lang="ja"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<title>画像モザイク&3Dアニメーション</title> |
|
<style> |
|
body { |
|
display: flex; |
|
flex-direction: column; |
|
align-items: center; |
|
background-color: #222; |
|
color: #fff; |
|
font-family: Arial, sans-serif; |
|
} |
|
canvas { |
|
display: block; |
|
margin-top: 20px; |
|
} |
|
#scene-container { |
|
width: 80vw; |
|
height: 80vh; |
|
} |
|
#uploadForm { |
|
margin-top: 20px; |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
<h1>画像のモザイク処理と3Dアニメーション</h1> |
|
<form id="uploadForm"> |
|
<input type="file" id="imageInput" accept="image/*"> |
|
<button type="button" onclick="processImage()">画像を処理</button> |
|
</form> |
|
<canvas id="canvas" style="display: none;"></canvas> |
|
<div id="scene-container"></div> |
|
|
|
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> |
|
<script> |
|
const canvas = document.getElementById('canvas'); |
|
const ctx = canvas.getContext('2d'); |
|
let scene, camera, renderer; |
|
const cubeSize = 5; |
|
let cubes = []; |
|
|
|
function processImage() { |
|
const fileInput = document.getElementById('imageInput'); |
|
const file = fileInput.files[0]; |
|
if (!file) return; |
|
|
|
const img = new Image(); |
|
img.onload = () => { |
|
const scale = 0.1; |
|
canvas.width = img.width * scale; |
|
canvas.height = img.height * scale; |
|
ctx.drawImage(img, 0, 0, canvas.width, canvas.height); |
|
|
|
create3DScene(); |
|
}; |
|
img.src = URL.createObjectURL(file); |
|
} |
|
|
|
function create3DScene() { |
|
|
|
scene = new THREE.Scene(); |
|
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); |
|
camera.position.z = 300; |
|
|
|
renderer = new THREE.WebGLRenderer(); |
|
renderer.setSize(window.innerWidth, window.innerHeight); |
|
document.getElementById('scene-container').appendChild(renderer.domElement); |
|
|
|
createCubesFromImage(); |
|
|
|
animate(); |
|
} |
|
|
|
function createCubesFromImage() { |
|
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height).data; |
|
|
|
for (let y = 0; y < canvas.height; y++) { |
|
for (let x = 0; x < canvas.width; x++) { |
|
const index = (y * canvas.width + x) * 4; |
|
const r = imageData[index]; |
|
const g = imageData[index + 1]; |
|
const b = imageData[index + 2]; |
|
const color = new THREE.Color(rgb(${r},${g},${b})); |
|
|
|
const geometry = new THREE.BoxGeometry(cubeSize, cubeSize, cubeSize); |
|
const material = new THREE.MeshBasicMaterial({ color: color }); |
|
const cube = new THREE.Mesh(geometry, material); |
|
|
|
|
|
cube.position.set( |
|
(x - canvas.width / 2) * cubeSize * 1.2, |
|
(canvas.height / 2 - y) * cubeSize * 1.2, |
|
Math.random() * 200 - 100 |
|
); |
|
scene.add(cube); |
|
cubes.push(cube); |
|
} |
|
} |
|
} |
|
|
|
function animate() { |
|
requestAnimationFrame(animate); |
|
|
|
|
|
cubes.forEach((cube, index) => { |
|
const targetX = ((index % canvas.width) - canvas.width / 2) * cubeSize * 1.2; |
|
const targetY = ((canvas.height / 2) - Math.floor(index / canvas.width)) * cubeSize * 1.2; |
|
const targetZ = 0; |
|
|
|
cube.position.x += (targetX - cube.position.x) * 0.05; |
|
cube.position.y += (targetY - cube.position.y) * 0.05; |
|
cube.position.z += (targetZ - cube.position.z) * 0.05; |
|
}); |
|
|
|
renderer.render(scene, camera); |
|
} |
|
</script> |
|
</body> |
|
</html> |