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

Experience a 3D Carousel in Your Browser! [Visual Gallery Powered by Three.js]

Japanese

Creating a 3D Carousel (Using Three.js [r145]) Pattern 1

Learn how to display a 3D carousel in the browser!
By utilizing Three.js, you can achieve a dynamic expression that is quite different from a typical 2D carousel.
In this article, we explain in detail how to create a 3D carousel along with actual sample code.

See 3D Carousel (Pattern 2) here

Source Code

<!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:front, counterclockwise

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=[];

  // Create scene
  scene = new THREE.Scene();

  // Create and set camera
  var width  = document.getElementById("can").getAttribute("width");
  var height = document.getElementById("can").getAttribute("height");
  // field of view, aspect ratio, near, far
  camera = new THREE.PerspectiveCamera( 60, width/height, 1, 10000 );

  // Place renderer on DOM
  renderer = new THREE.WebGLRenderer({'canvas':document.getElementById('can') , antialias: true});// enable antialiasing
  renderer.setSize( width, height );
  renderer.shadowMap.enabled = true;// enable shadows
  renderer.shadowMapSoft = true;
  renderer.shadowMap.type = THREE.PCFShadowMap;

  document.getElementById('can').style.width="100%";
  document.getElementById('can').style.height="auto";


  // ■ Set background color
  //renderer.setClearColor(0x88ccff, 1);
  renderer.setClearColor(0x444444, 1);

  // Light ------------------------------------------------
  // Ambient light (light applied to all objects from all directions). Without this, unlit areas are completely black.
  var AmbientLight=new THREE.AmbientLight(0xffffff,0.2);
  scene.add( AmbientLight );


  // Load images
  images=await Promise.all(imageFiles.map(loadImage));
  // Load textures
  for(let i=0;i<imageFiles.length;i++){
    texture[i]=new THREE.TextureLoader().load(imageFiles[i]);
    material[i]=new THREE.MeshLambertMaterial({
      color: 0xffffff, // color
      map:texture[i],
      side:THREE.DoubleSide, //THREE.DoubleSide, THREE.FrontSide, THREE.BackSide
    });
  }

  // Create meshes
  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
  camera.position.set(0,0,4);
  //camera.lookAt(new THREE.Vector3(0, 0, 0));
  
  // Spotlight            color, intensity, distance, angle, penumbra[0-1], decay[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;// enable shadows
  spot.shadow.mapSize.width = 4096;	// high quality shadows
  spot.shadow.mapSize.height = 4096;	// high quality shadows
  spot.shadow.radius=8;
  scene.add(spot);
  scene.add(spot.target);

  // Create background mesh
  var backMaterial=new THREE.MeshLambertMaterial({
      color: 0xffffff, // color
      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);

  // Add events
  renderer.domElement.addEventListener('mouseup',onMouseDown,false);
  renderer.domElement.addEventListener('touchend',onMouseDown,false);

  renderLoop();
});

function renderLoop () {
  // Execute scenario
  timer++;
  if(timer>=acts[act].time){
    timer=0;
    act++;
    if(act>=acts.length){
      act=0;
    }
  }
  if(acts[act].act=="w"){
    // just wait
  }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();
  // Get element position
  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);

  // Create ray by passing start point and direction vector
  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>

Back to the list of 3D content with JavaScript