📦

Three.jsで3Dの立方体が埋め込まれたButtonを作る

2022/03/23に公開

完成したもの

作り方

Viteでプロジェクト作成。

npm init vite@latest my-vite-3dbutton --template vanilla-ts

Three.jsとその型定義ファイルをインストール。

npm install --save three @types/three

index.htmlファイルを開き、buttonとCanvasを追加。

index.html
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <link rel="icon" type="image/svg+xml" href="favicon.svg" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Vite App</title>
</head>

<body>
  <div id="app">
    <button>
      <canvas />
    </button>
  </div>
  <script type="module" src="/src/main.ts"></script>
</body>

</html>

main.tsファイルを開き、1行目以外を消して書き換える。

main.ts
import './style.css'

import * as THREE from 'three';

function init() {
  // Button取得, Buttonの縦幅・横幅をwidth,height変数に代入
  const button = document.querySelector('button') as HTMLButtonElement;
  const width = button.clientWidth;
  const height = button.clientHeight;

  // Three.js側の処理
  const renderer = new THREE.WebGLRenderer({
    canvas: document.querySelector('canvas') as HTMLCanvasElement
  });
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(width, height);

  // シーン作成
  const scene = new THREE.Scene();

  // カメラ作成
  const camera = new THREE.PerspectiveCamera(45, width / height);
  camera.position.set(0, 0, 1000);

  // 立方体の3Dモデルを作ってシーンに追加
  const geometry = new THREE.BoxGeometry(400, 400, 400);
  const material = new THREE.MeshNormalMaterial();
  const box = new THREE.Mesh(geometry, material);
  scene.add(box);

  // レンダリングする用のTick関数
  function Tick() {
    requestAnimationFrame(Tick);

    renderer.render(scene, camera);
  }

  Tick();
}

window.onload = init;

こんな感じでボタンの中に3Dモデルの立方体が表示される。

発展

これだけだとあまり面白くないので、中の立方体を回転させてみる。

まずは回転される角度を入れる変数を準備。

let rotaion: number = 0.005;

Tick関数を実行するたびにboxを回転させていく。

// レンダリングする用のTick関数
  function Tick() {
    requestAnimationFrame(Tick);

    box.rotation.y += rotaion;
    renderer.render(scene, camera);
  }

さらにボタンにカーソルを当てると回転速度が変わるようにしてみる。
ついでにボタンの大きさも変わるようにCSSを変えてみる。

  button.onmouseenter = () => {
    rotaion = 0.01;
  }

  button.onmouseleave = () => {
    rotaion = 0.005;
  }
style.css
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}

button {
  transition-property: transform;
  transition-duration: 0.5s;
}

button:hover {
  transform: scale(1.1);
}

Discussion