| <!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> |
|
|