Three.jsの基礎（マウスイベント）

# はじめに
こんにちは！Three.jsについて学び始めた初心者です。
Three.jsは、ブラウザ上で3Dモデルの描画や操作等を可能にするJavaScriptライブラリです。
本記事では、Three.jsの基礎の1つとして、マウスイベントの基本的な使い方を記載します。

# 単純な立方体を描画するソースコード
以降の内容については、下記のコードをベースに使用しています（立方体を表示するだけの簡単なコード）。


```html
<!-- test.html -->
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Three.js test</title>
    <style>
        body { margin: 0; }
        canvas { display: block; }
    </style>
</head>
<body>
    <script src="https://threejs.org/build/three.js"></script>
    <script src="threejstest.js"></script>
</body>
</html>
```
```javascript
//threejstest.js
let scene, camera, renderer, cube;
let raycaster, mouse;

function init() {
    // シーン、カメラ、レンダラーの初期化
    scene = new THREE.Scene();
    camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
    renderer = new THREE.WebGLRenderer();
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);

    // 立方体の追加
    let geometry = new THREE.BoxGeometry();
    let material = new THREE.MeshBasicMaterial({ color: 0xffffff });
    cube = new THREE.Mesh(geometry, material);
    scene.add(cube);

    // カメラの位置を設定
    camera.position.z = 5;
}

function animate() {
    requestAnimationFrame(animate);
    renderer.render(scene, camera);
}

init();
animate();
```
暗闇に浮かぶ白い箱
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3625785/8239aca3-cc49-eaeb-d2e7-7f5265f77b32.png)


# マウスイベントを使ってみる

Three.jsでマウスイベントを使う際には、マウスの位置を取得したり、マウスとオブジェクトの重なりを判定したりする必要があります。後者はレイキャスティングと呼ばれる機能で、簡単に実装できるようにクラスが用意されています。
これらのベースとなる部分はほぼ定型のコードを使うようなので、ここでは立ち入って説明はしないことにします。
（レイキャスティングについては下記にグラフィカルな例がたくさんあり、分かりやすいと思います）
https://threejs.org/docs/#api/en/core/Raycaster

## クリックしたとき
上記のベースとなるコードでは白い四角形が表示されるだけでしたが、下記のコードではクリックすると色が青に変わる機能を追加しています（追記部分）。
```javascript
//threejstest.js
let scene, camera, renderer, cube;
let raycaster, mouse;

function init() {
    scene = new THREE.Scene();
    camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
    renderer = new THREE.WebGLRenderer();
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);

    let geometry = new THREE.BoxGeometry();
    let material = new THREE.MeshBasicMaterial({ color: 0xffffff });
    cube = new THREE.Mesh(geometry, material);
    scene.add(cube);

    camera.position.z = 5;

    // レイキャスターの初期化(追記部分)
    raycaster = new THREE.Raycaster();

    //マウスベクトルの初期化(追記部分)
    mouse = new THREE.Vector2();

    // イベントリスナーの追加(追記部分)
    document.addEventListener('click', onMouseEvent, false);
}

// イベントリスナーに対応する処理(追記部分)
function onMouseEvent(event) {
    event.preventDefault();

    // 座標を正規化する呪文
    mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
    mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

    // レイキャスティングでマウスと重なるオブジェクトを取得
    raycaster.setFromCamera(mouse, camera);
    const intersects = raycaster.intersectObjects(scene.children);

    // 取得したオブジェクトの色を青色に変える
    for (let i = 0; i < intersects.length; i++) {
        intersects[i].object.material.color.set(0x0000ff);
    }
}

function animate() {
    requestAnimationFrame(animate);
    renderer.render(scene, camera);
}

init();
animate();
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3625785/a2e90fff-6cc3-4b1f-1d28-2cec5350fe5c.png)


## マウスボタンを押したとき
クリック以外のマウスイベントに反応させたいときは、```document.addEventListener```の引数を変更すれば対応できます。以降には該当部分のみコードを記載します。


```javascript
    //マウスボタンを押すと（離さなくても）反応する
    document.addEventListener('mousedown', onMouseEvent, false);
```

## マウスボタンを離したとき

```javascript
    //マウスボタンを押した状態から離したときに反応する
    document.addEventListener('mouseup', onMouseEvent, false);
```


## ダブルクリックしたとき

```javascript
    //ダブルクリックすると反応する
    document.addEventListener('dblclick', onMouseEvent, false);
```

## ホイールをスクロールしたとき

```javascript
    //ホイールをスクロールすると反応する
    document.addEventListener('wheel', onMouseEvent, false);
```

## マウスを動かしたとき

```javascript
    //マウスを動かしたとき反応する
    document.addEventListener('mousemove', onMouseEvent, false);
```