|
<!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; |
|
margin: 0; |
|
padding: 0; |
|
|
|
} |
|
canvas { |
|
display: block; |
|
margin-top: 20px; |
|
} |
|
#scene-container { |
|
width: 100vw; |
|
height: 100vh; |
|
margin: 0; |
|
padding: 0; |
|
overflow: hidden; |
|
} |
|
#uploadForm { |
|
margin-top: 20px; |
|
} |
|
#settings { |
|
margin-top: 20px; |
|
display: flex; |
|
flex-direction: column; |
|
align-items: center; |
|
} |
|
#settings label { |
|
margin: 5px; |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
<h1>画像のモザイク処理と3Dアニメーション</h1> |
|
<form id="uploadForm"> |
|
<input type="file" id="imageInput" accept="image/*"> |
|
<button type="button" onclick="processImage()">画像を処理</button><br> |
|
<button type="button" onclick="useExampleImage()">例の画像(Gの文字)を使用</button> |
|
</form> |
|
<div id="settings"> |
|
<label> |
|
シーン背景色: <input type="color" id="bgColorPicker" value="#222222" onchange="updateSceneBackgroundColor()"> |
|
</label> |
|
<label> |
|
カメラ X: <input type="range" id="cameraX" min="-500" max="500" value="-100" oninput="updateCameraPosition()"> |
|
</label> |
|
<label> |
|
カメラ Y: <input type="range" id="cameraY" min="-500" max="500" value="-100" oninput="updateCameraPosition()"> |
|
</label> |
|
<label> |
|
カメラ Z: <input type="range" id="cameraZ" min="-1000" max="1000" value="300" oninput="updateCameraPosition()"> |
|
</label> |
|
</div> |
|
<button id="replayButton" style="margin-top: 10px; display: none;" onclick="replayAnimation()">もう一度再生</button> |
|
<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 = 8; |
|
let cubes = []; |
|
let animationFrameId; |
|
|
|
function processImage(imageSrc = null) { |
|
const fileInput = document.getElementById('imageInput'); |
|
const file = imageSrc ? null : fileInput.files[0]; |
|
|
|
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(); |
|
document.getElementById('replayButton').style.display = 'block'; |
|
}; |
|
img.src = imageSrc || URL.createObjectURL(file); |
|
} |
|
|
|
function useExampleImage() { |
|
const exampleImagePath = 'ex.png'; |
|
processImage(exampleImagePath); |
|
} |
|
|
|
function create3DScene() { |
|
if (renderer) { |
|
|
|
cancelAnimationFrame(animationFrameId); |
|
cubes.forEach(cube => scene.remove(cube)); |
|
cubes = []; |
|
renderer.dispose(); |
|
} |
|
|
|
|
|
scene = new THREE.Scene(); |
|
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); |
|
updateCameraPosition(); |
|
|
|
renderer = new THREE.WebGLRenderer(); |
|
renderer.setSize(document.getElementById('scene-container').clientWidth, document.getElementById('scene-container').clientHeight); |
|
document.getElementById('scene-container').innerHTML = ''; |
|
document.getElementById('scene-container').appendChild(renderer.domElement); |
|
|
|
updateSceneBackgroundColor(); |
|
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 a = imageData[index + 3]; |
|
|
|
const color = new THREE.Color(`rgb(${r},${g},${b})`); |
|
const geometry = new THREE.BoxGeometry(cubeSize, cubeSize, cubeSize); |
|
const material = new THREE.MeshBasicMaterial({ |
|
color: color, |
|
transparent: true, |
|
opacity: a / 255 |
|
}); |
|
|
|
const cube = new THREE.Mesh(geometry, material); |
|
|
|
cube.position.set( |
|
(x - canvas.width / 2) * cubeSize * 1.2 + (Math.random()-0.5) * 2200 - 100, |
|
(canvas.height / 2 - y) * cubeSize * 1.2 + (Math.random()-0.5) * 2200 - 100, |
|
(Math.random()-0.5) * 2200 - 100 |
|
); |
|
scene.add(cube); |
|
cubes.push(cube); |
|
} |
|
} |
|
} |
|
|
|
function animate() { |
|
animationFrameId = 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); |
|
} |
|
|
|
function replayAnimation() { |
|
create3DScene(); |
|
} |
|
|
|
function updateSceneBackgroundColor() { |
|
const color = document.getElementById('bgColorPicker').value; |
|
scene.background = new THREE.Color(color); |
|
} |
|
|
|
function updateCameraPosition() { |
|
const x = parseFloat(document.getElementById('cameraX').value); |
|
const y = parseFloat(document.getElementById('cameraY').value); |
|
const z = parseFloat(document.getElementById('cameraZ').value); |
|
camera.position.set(x, y, z); |
|
camera.lookAt(new THREE.Vector3(0, 0, 0)); |
|
} |
|
</script> |
|
</body> |
|
</html> |
|
|