【Three.js】DOM要素を3D空間に配置する

JavaScript,Three.js

すべてはこの時のために。

はじめに

はい。ついに来ました。この時が。

コレやりたくてJavaScriptの勉強していたと言ってもいいぐらいコレやりたくて。

やりたかったのがコレ

デモサイトはこちら

やりたかったコレをやれました。とりあえずコードを全て載せておきます。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>DOM要素を3D化</title>
  <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
  <script type="text/javascript" src="../libs/three.min.js"></script>
  <script type="text/javascript" src="../libs/renderers/CSS3DRenderer.js"></script>
  <script type="text/javascript" src="../libs/controls/TrackballControls.js"></script>
  <style>
      body {
          margin: 0;
          overflow: hidden;
      }
  </style>
</head>
<body>

  <div id="screen"></div>
  <img id="cat" src="../assets/textures/animals/cat.jpg" alt="猫の写真">
  <img id="dog" src="../assets/textures/animals/dog.jpg" alt="犬の写真">

  <script type="text/javascript">

    window.onload = function() {
      
      // シーン
      var scene = new THREE.Scene();

      // カメラ
      var camera = new THREE.PerspectiveCamera(30, window.innerWidth / window.innerHeight, 1, 10000);
      camera.position.z = 3000;

      // 猫
      var catElem = document.getElementById("cat");
      var catObj = new THREE.CSS3DObject(catElem);
      scene.add(catObj);

      // 犬
      var dogElem = document.getElementById("dog");
      var dogObj = new THREE.CSS3DObject(dogElem);
      dogObj.position.z = -1000;
      scene.add(dogObj);
      
      // レンダラー
      var renderer = new THREE.CSS3DRenderer();
      renderer.setSize(window.innerWidth, window.innerHeight);
      document.getElementById("screen").appendChild(renderer.domElement);

      // カメラのコントロール
      var trackballControls = new THREE.TrackballControls(camera);
      trackballControls.panSpeed = 0.2;
      trackballControls.minDistance = 500;
      trackballControls.maxDistance = 6000;

      // 描画関数
      var clock = new THREE.Clock();
      function render() {
        var delta = clock.getDelta();
        trackballControls.update(delta);
        requestAnimationFrame(render);
        renderer.render(scene, camera);
      }

      // イベントリスナー
      window.addEventListener("resize", onResize);

      function onResize() {
        camera.aspect = window.Width / window.Height;
        camera.updateProjectionMatrix();
        renderer.setSize(window.innerWidth, window.innerHeight);
        render();
      }

      // 描画
      render();
    }
  </script>
</body>
</html>

なかなかシンプルですね。ポイントだけ解説していきます。

<div id="screen"></div>
<img id="cat" src="../assets/textures/animals/cat.jpg" alt="猫の写真">
<img id="dog" src="../assets/textures/animals/dog.jpg" alt="犬の写真">

レンダリング用の<div>と3D空間に配置する<img>を2つ作っておきます。

// 猫
var catElem = document.getElementById("cat");
var catObj = new THREE.CSS3DObject(catElem);
scene.add(catObj);

// 犬
var dogElem = document.getElementById("dog");
var dogObj = new THREE.CSS3DObject(dogElem);
dogObj.position.z = -1000;
scene.add(dogObj);

まず"getElementById"で<img>要素を取得します。で、それを"CSS3DObject"のコンストラクタに渡してあげることで、そのDOM要素を3D空間上のオブジェクトとして扱うことができるようになります。

// レンダラー
var renderer = new THREE.CSS3DRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.getElementById("screen").appendChild(renderer.domElement);

今まではレンダラーに"THREE.WebGLRenderer"を使っていましたが、DOM要素を扱う時は"THREE.CSS3DRenderer"にします。

// カメラのコントロール
var trackballControls = new THREE.TrackballControls(camera);
trackballControls.panSpeed = 0.2;
trackballControls.minDistance = 500;
trackballControls.maxDistance = 6000;

// 描画関数
var clock = new THREE.Clock();
function render() {
  var delta = clock.getDelta();
  trackballControls.update(delta);
  requestAnimationFrame(render);
  renderer.render(scene, camera);
}

今回は"THREE.TrackballControls"を使うことで、視点(カメラ)の位置を操作できるようにしました。"panSpeed"は平行移動する時のスピード。デフォルトのままだとちょっと動きが速い感じがしたので0.2にしました。

ちなみに、スマホでのパンは「指2本で画面触ってスッスッ」です。僕知りませんでした。

で、描画用の関数"render()"内で"trackballControls.update()"を呼んであげると視点の動きも反映してくれるワケですね。引数に渡しているのは、前回"update()"が呼ばれた時からの経過時間です。なんかそういう決まりみたい。経過時間は"THREE.Clock"オブジェクトの"getDelta()"で取得します。

// イベントリスナー
window.addEventListener("resize", onResize);

function onResize() {
  camera.aspect = window.Width / window.Height;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
  render();
}

ウィンドウサイズが変更された時の処理です。カメラのアスペクト比と描画領域を変更後のウィンドウサイズに合わせるように再設定しています。JavaScriptの説明は以上です。

大事なこと言います

<head>タグ内で"viewport"の設定をしています。

<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">

“user-scalable"は画面のズームを許可するかどうか。スマホでブラウザ見てるとき普通にズームできますよね。だからデフォルトは"yes"になっているんだと思います。今回は"no"を指定してズームできないようにしています。

なんで?

今回は"THREE.TrackballControls"を使って視点(カメラ)を移動できるようにしています。回転させたりパンしたり。そしてズームも。

スマホだと、この「カメラのズーム」の操作方法と「ブラウザのズーム」の操作方法が同じせいで挙動がおかしくなります。カメラをズームインしようとするとブラウザもズームインして、犬と猫の写真がどこかへすっ飛んで行ったりして。僕はこれでハマりました。

そんなわけで、視点(カメラ)の移動をお考えでしたら"user-scalable=no"です。

あとがき

できましたねぇ。前に同じような事しようとして、jQueryでtransfomとか必死にいじくり回してたのが懐かしい…。Three.jsなら簡単にできるのね。

今回覚えたことを生かしてポートフォリオサイト作れたらいいなぁと思ってます。今回は以上です。ありがとうございました。

おしゃれ度

★★★☆☆

Posted by ナカタ