3Dカルーセルの作成(Three.js[r145]を使用)
ブラウザ上で3Dカルーセルを表示する方法を解説します!
Three.jsを活用することで、通常の2Dカルーセルとは一味違ったダイナミックな表現が可能です。
本記事では、実際のサンプルコードとともに、3Dカルーセルの作り方を詳しく解説します。
ソースコード
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script src="./three_r145/three.min.js"></script> <script> //X:→ Y:↑ Z:手前、反時計回り var camera,spot; var imageFiles=[ './imgs/fc01.png', './imgs/fc02.png', './imgs/fc03.png', './imgs/fc04.png', './imgs/fc05.png', './imgs/fc06.png', ]; var imageLinkURL=[ 'https://mam-mam.net/javascript/three_js_material.html', 'https://mam-mam.net/javascript/three_js_geometry.html', 'https://mam-mam.net/javascript/three_js_baseball.html', 'https://mam-mam.net/javascript/css_ul_li.html', 'https://mam-mam.net/javascript/css_nav_horizontal_menu.html', 'https://mam-mam.net/javascript/css_toggle_switch.html', ]; var moveImg=0,act=0,timer=0,moveStep=0,mesh=[], acts=[ {"act":"w","time":120}, {"act":"o","time":10}, {"act":"m","time":30}, {"act":"i","time":10} ]; async function loadImage(src){ return new Promise(function(resolve,reject){ let img=new Image(); img.onload=function(){resolve(img);} img.onerror=function(e){reject(e);} img.src=src; }); } window.addEventListener( 'DOMContentLoaded', async function(){ var images=[]; var material=[]; var texture=[]; //シーンを作成 scene = new THREE.Scene(); //カメラを作って設定 var width = document.getElementById("can").getAttribute("width"); var height = document.getElementById("can").getAttribute("height"); // 視野角,アスペクト比,近距離,遠距離 camera = new THREE.PerspectiveCamera( 60, width/height, 1, 10000 ); //レンダラーをDOM上に設置する renderer = new THREE.WebGLRenderer({'canvas':document.getElementById('can') , antialias: true});//アンチエイリアスをtureにする renderer.setSize( width, height ); renderer.shadowMap.enabled = true;//影可能 renderer.shadowMapSoft = true; renderer.shadowMap.type = THREE.PCFShadowMap; document.getElementById('can').style.width="100%"; document.getElementById('can').style.height="auto"; //■背景色の設定 //renderer.setClearColor(0x88ccff, 1); renderer.setClearColor(0x444444, 1); //光源------------------------------------------------ //環境光(すべての物体にすべての方向から与える光)これがないと光の当たらない部分は真っ黒になる var AmbientLight=new THREE.AmbientLight(0xffffff,0.2); scene.add( AmbientLight ); //画像のロード images=await Promise.all(imageFiles.map(loadImage)); //テクスチャのロード for(let i=0;i<imageFiles.length;i++){ texture[i]=new THREE.TextureLoader().load(imageFiles[i]); material[i]=new THREE.MeshLambertMaterial({ color: 0xffffff, //色 map:texture[i], side:THREE.DoubleSide, //THREE.DoubleSide, THREE.FrontSide, THREE.BackSide }); } //メッシュの作成 for(let i=0;i<texture.length;i++){ let w=10;//w:h=ww:hh h=w*hh/ww let h=w/images[i].naturalWidth*images[i].naturalHeight; var plane=new THREE.PlaneGeometry(w, h, 1, 1); mesh[i]=new THREE.Mesh(plane,material[i]); mesh[i].position.x=i*11; mesh[i].castShadow=true; scene.add(mesh[i]); } //カメラ位置 camera.position.set(0,0,4); //camera.lookAt(new THREE.Vector3(0, 0, 0)); //スポットライト 色, 強さ,距離(distance),角度(angle),半影[0-1],減衰[1,1.5] spot=new THREE.SpotLight(0xffffff, 1.5, 24,Math.PI*0.35, 0.8, 1.2); spot.position.set(0,4,8); spot.target.position.set(0,-4,0); spot.castShadow=true;//影を表示可能に spot.shadow.mapSize.width = 4096; //影をきれいにする spot.shadow.mapSize.height = 4096; //影をきれいにする spot.shadow.radius=8; scene.add(spot); scene.add(spot.target); //背景メッシュの作成 var backMaterial=new THREE.MeshLambertMaterial({ color: 0xffffff, //色 side:THREE.FrontSide, //THREE.DoubleSide, THREE.FrontSide, THREE.BackSide }); var backPlane=new THREE.PlaneGeometry(imageFiles.length*11+20, 20, 1, 1); var backMesh=new THREE.Mesh(backPlane,backMaterial); backMesh.position.z=-3; backMesh.position.x=imageFiles.length*11/2-10; backMesh.receiveShadow=true; scene.add(backMesh); //イベントの追加 renderer.domElement.addEventListener('mouseup',onMouseDown,false); renderer.domElement.addEventListener('touchend',onMouseDown,false); renderLoop(); }); function renderLoop () { //シナリオの実行 timer++; if(timer>=acts[act].time){ timer=0; act++; if(act>=acts.length){ act=0; } } if(acts[act].act=="w"){ //待つだけ }else if(acts[act].act=="o"){ camera.position.z+=0.5; spot.position.z+=0.5; }else if(acts[act].act=="i"){ camera.position.z-=0.5; spot.position.z-=0.5; }else if(acts[act].act=="m"){ if(timer==0){ let fromX=mesh[moveImg].position.x; moveImg++; if(moveImg>=mesh.length){moveImg=0;} let toX=mesh[moveImg].position.x; moveStep=(toX-fromX)/acts[act].time; } camera.position.x+=moveStep; spot.position.x+=moveStep; spot.target.position.x+=moveStep; } renderer.render( scene, camera ); setTimeout(renderLoop,33); } function onMouseDown(event){ event.preventDefault(); // 要素の位置を取得 var clientRect = this.getBoundingClientRect() ; var positionX = clientRect.left; var positionY = clientRect.top; let st=window.getComputedStyle(document.getElementById("can")); let www=parseInt(st.width); let hhh=parseInt(st.height); let xxx,yyy; if(event.clientX){ xxx= ((event.clientX-positionX) / www) * 2 - 1; yyy= -((event.clientY-positionY) / hhh) * 2 + 1; }else{ xxx = ((event.changedTouches[0].pageX-positionX) / www) * 2 - 1; yyy = -((event.changedTouches[0].pageY-positionY) / hhh) * 2 + 1; } var pos=new THREE.Vector3(xxx,yyy,-10); pos.unproject(camera); // 始点、向きベクトルを渡してレイを作成 var ray = new THREE.Raycaster(camera.position, pos.sub(camera.position).normalize()); var intersects=ray.intersectObjects(scene.children); if(intersects.length > 0){ for(i=0;i<mesh.length;i++){ for(j=0;j<intersects.length;j++){ if(mesh[i]==intersects[j].object){ if(imageLinkURL[i]!=""){ window.open(imageLinkURL[i],'_blank'); } } } } } } </script> </head> <body> <canvas id="can" style="width: 100%; height: auto; max-width:calc(100vw - 32px);max-height:calc(100vw - 32px);cursor:pointer;" width="1000" height="300"></canvas> </body> </html>