Home
JavaScript
【Three.js】マウスにライトを追従させる方法

【Three.js】マウスにライトを追従させる方法

【Three.js】マウスにライトを追従させる方法

どうも!かけちまるです!

Three.jsでマウスの動きに合わせてライトを追従させる方法を解説します。

WebGL座標は、window座標とx,y軸の原点が違うので座標変換する必要があるのです。

この記事では、

  • ・座標変換する方法
  • ・マウスイベントでのアニメーション

がわかります。

今回のテンプレート

次のコードを元に追従するライトを加えていきます。

ディレクトリ構成
開く&閉じるコピー
index.html css/ -style.css js/ -main.js
index.html
開く&閉じるコピー
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" type="text/css" href="./css/style.css"> <script async src="https://unpkg.com/es-module-shims@1.5.8/dist/es-module-shims.js"></script> <script type="importmap"> { "imports": { "three": "https://unpkg.com/three@0.142.0/build/three.module.js" } } </script> <script src="./js/main.js" type="module"></script> </head> <body></body> </html>
style.css
開く&閉じるコピー
* { margin: 0; padding: 0; box-sizing: border-box; }
main.js
開く&閉じるコピー
import * as THREE from 'three'; /** * シーン・カメラ・レンダラーを用意する */ const fov = 50; const fovRad = (fov / 2) * (Math.PI / 180); // 視野角をラジアンに変換 let distance = (window.innerHeight / 2) / Math.tan(fovRad); // カメラ距離を求める const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera( fov, window.innerWidth / window.innerHeight, 0.1, 1000 ); camera.position.z = distance; const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); renderer.setPixelRatio(window.devicePixelRatio); document.body.appendChild(renderer.domElement); /** * ジオメトリ+マテリアル=メッシュを用意する */ const torusKnotGeometry = new THREE.TorusKnotGeometry( 100, 30, 100, 16 ); const lambertMaterial = new THREE.MeshLambertMaterial({ color: 0x3da8e6, }); const torusKnotMesh = new THREE.Mesh(torusKnotGeometry, lambertMaterial); scene.add(torusKnotMesh); /** * ライトを追加する */ let pointLight = new THREE.PointLight(0xffffff, 1, 0, 1.0); pointLight.position.set(0, 0, 200); scene.add(pointLight); // アニメーション const clock = new THREE.Clock(); function animate() { renderer.render(scene, camera); let getElapsedTime = clock.getElapsedTime(); torusKnotMesh.position.y += Math.sin(getElapsedTime * 1.5); requestAnimationFrame(animate); } animate(); // ブラウザのリサイズに対応させる window.addEventListener('resize', onWindowResize); function onWindowResize() { renderer.setSize(window.innerWidth, window.innerHeight); camera.aspect = window.innerWidth / window.innerHeight; distance = (window.innerHeight / 2) / Math.tan(fovRad); camera.position.z = distance; camera.updateProjectionMatrix(); }

ここまで次のようになっていればオッケーです。

マウスにライトを追従させる方法

マウスにライトを追従させるにはmousemoveイベントを使います。
ですが、その前にWebGLとwindow座標を合わせる必要があります。
この記事では、window座標をWebGL座標に合わせる方向で考えます。

WebGLとwindow座標の考え方

Three.jsを何度か触ったことがある人なら知っていることだと思いますが、

次の画像のようにWebGL座標では中央を原点としてx,yが決まります。
一方、window座標では左上を原点としてx,yが決まりますね。

WebGLとwindow座標の原点簿図

なので、window座標の原点を中心に持ってくる必要があるのです。

window座標の原点を中心に持ってくるには、

  • WebGLのx座標 = windowのx座標 – ブラウザの幅 / 2
  • WebGLのy座標 = -windowのy座標 + ブラウザの高さ / 2

で求めることができます。

具体的な数値に当てはめると次の画像のようになります。

WebGLとwindow座標の具体例

ライトの追従を実装

マウスを追従するには12行目以降を追加します。

window座標をWebGL座標に変換します。(18、19行目)
変換した座標の値をライトの位置として指定します。(22、23行目)

マウスが動くたびにonMouseMove関数を実行します。(13行目)

main.js
開く&閉じるコピー
・・・ // ブラウザのリサイズに対応させる window.addEventListener('resize', onWindowResize); function onWindowResize() { renderer.setSize(window.innerWidth, window.innerHeight); camera.aspect = window.innerWidth / window.innerHeight; distance = (window.innerHeight / 2) / Math.tan(fovRad); camera.position.z = distance; camera.updateProjectionMatrix(); } // ライトをマウスに追従させる window.addEventListener('mousemove', (event) => { onMouseMove(event.clientX, event.clientY); }); function onMouseMove(x, y) { const mouseX = x - (window.innerWidth / 2); // 原点を中心に持ってくる const mouseY = -y + (window.innerHeight / 2); // 軸を反転して原点を中心に持ってくる // ライトのxy座標をマウス位置にする pointLight.position.x = mouseX; pointLight.position.y = mouseY; }

こんな感じになったらOKです。

まとめ

マウスホバーでのアニメーションは使える場面が多いと思いますのでマスターしておくと応用が効くのではないかと思います。

おわり

かけちまる
かけちまる
Webエンジニアをしています。
HTML/CSS/JavaScript/jQuery/PHPができます。
WEB制作を中心に日々学びになったこと、興味が沸いたことについて初心者の方でもわかりやすいようにアウトプットしていくブログです。

関連記事

JSでユーザが使っているブラウザを判定する方法【userAgent】

JSでユーザが使っているブラウザを判定する方法【userAgent】

videoタグの再生や停止などをJSでコントロールする方法

videoタグの再生や停止などをJSでコントロールする方法

for文内でsetTimeoutを使うときの変数のスコープとクロージャ

for文内でsetTimeoutを使うときの変数のスコープとクロージャ

コピペで完了!CSSグラデーション(linear-gradient)まとめ

コピペで完了!CSSグラデーション(linear-gradient)まとめ

JavaScriptでサイトのスクロールを無効にする方法

JavaScriptでサイトのスクロールを無効にする方法

【Contact Form 7】ログインユーザーのためにselectのデフォルト値を設定する

【Contact Form 7】ログインユーザーのためにselectのデフォルト値を設定する