トップへ(mam-mam.net/)

Javascriptで3Dカルーセルの実装 其の2 ~Webサイトで3Dコンテンツ(Three.js)

検索:

3Dカルーセルの作成(Three.js[r145]を使用)

ソースコード

<!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 rrr=10,//半径10
    rrate=Math.PI/3,//回転角
    rrrate=1/rrate*Math.PI/2;
var moveImg=0,act=0,timer=0,moveStep={x:0,y:0,z:0,r:0},mesh=[],
  acts=[
    {"act":"w","time":120},
    {"act":"m","time":30},
  ];

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( 40, 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(0x000000, 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=Math.random()*20-10;
    mesh[i].position.y=Math.random()*20-10;
    mesh[i].position.z=-i*rrr;
    mesh[i].rotation.z=Math.random()*Math.PI*2;
    
    mesh[i].castShadow=true;
    scene.add(mesh[i]);
  }

  
  //スポットライト            色,     強さ,距離(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);

  //イベントの追加
  renderer.domElement.addEventListener('mouseup',onMouseDown,false);
  renderer.domElement.addEventListener('touchend',onMouseDown,false);

  //カメラ位置
  camera.position.set(mesh[0].position.x,mesh[0].position.y,mesh[0].position.z+rrr/2);
  camera.rotation.z=mesh[0].rotation.z;

  spot.position.set(mesh[0].position.x,mesh[0].position.y,mesh[0].position.z+rrr/2);
  spot.target.position.set(mesh[0].position.x,mesh[0].position.y,mesh[0].position.z);

  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=="m"){
    if(timer==0){
      let fm=mesh[moveImg];
      moveImg++;
      if(moveImg>=mesh.length){moveImg=0;}
      let tm=mesh[moveImg];
      moveStep.x=(fm.position.x-tm.position.x)/acts[act].time;
      moveStep.y=(fm.position.y-tm.position.y)/acts[act].time;
      moveStep.z=(fm.position.z-tm.position.z)/acts[act].time;
      moveStep.r=(fm.rotation.z-tm.rotation.z)/acts[act].time;
    }
    camera.position.x-=moveStep.x;
    camera.position.y-=moveStep.y;
    camera.position.z-=moveStep.z;
    camera.rotation.z-=moveStep.r;
    
    spot.position.x-=moveStep.x;
    spot.position.y-=moveStep.y;
    spot.position.z-=moveStep.z;
    spot.target.position.x-=moveStep.x;
    spot.target.position.y-=moveStep.y;
    spot.target.position.z-=moveStep.z;
  }



  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>