Aller au contenu

Suivre la souris

Transformer la position du pointeur en coordonnées de -1 à 1, puis s’en servir pour déplacer un objet dans la scène.

main.js
import * as THREE from "three";

const scene = new THREE.Scene();

const camera = new THREE.PerspectiveCamera(
  45,
  window.innerWidth / window.innerHeight,
  0.1,
  100,
);
camera.position.z = 5;

const renderer = new THREE.WebGLRenderer({
  antialias: true,
});
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

const cube = new THREE.Mesh(
  new THREE.BoxGeometry(1, 1, 1),
  new THREE.MeshNormalMaterial(),
);
scene.add(cube);

const mouse = {
  x: 0,
  y: 0,
};

window.addEventListener("pointermove", (event) => {
  mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
  mouse.y = (event.clientY / window.innerHeight) * 2 - 1;

  console.log(mouse);
});

renderer.setAnimationLoop((time) => {
  cube.position.x = mouse.x * 2;
  cube.position.y = -mouse.y * 2;

  cube.rotation.x = time / 1000;
  cube.rotation.y = time / 1500;

  renderer.render(scene, camera);
});

window.addEventListener("resize", () => {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();

  renderer.setSize(window.innerWidth, window.innerHeight);
});

Le principe

On part de la doc « Première scène » : même scène, même caméra, même cube. On ajoute deux choses :

  1. un écouteur pointermove qui range la position du pointeur dans un objet mouse ;
  2. dans la boucle d’animation, une ligne qui place le cube selon mouse.

pointermove marche à la souris, au stylet et au doigt : c’est le bon réflexe plutôt que mousemove.

Ramener la position entre -1 et 1

event.clientX donne la position du pointeur en pixels, depuis le bord gauche. Ce nombre dépend de la taille de l’écran : on le ramène entre -1 (bord gauche) et 1 (bord droit), pour que le même code marche partout.

main.js
const mouse = {
  x: 0,
  y: 0,
};

window.addEventListener("pointermove", (event) => {
  mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
  mouse.y = (event.clientY / window.innerHeight) * 2 - 1;

  console.log(mouse);
});

Un exemple sur un écran de 1000 pixels de large, pointeur à 750 pixels :

  1. 750 / 1000 donne 0.75 : la position entre 0 et 1 ;
  2. 0.75 * 2 donne 1.5 : entre 0 et 2 ;
  3. 1.5 - 1 donne 0.5 : entre -1 et 1.

Le console.log affiche les valeurs à chaque mouvement : ouvre la console pour vérifier qu’elles restent bien entre -1 et 1, puis supprime-le.

Déplacer le cube

main.js
renderer.setAnimationLoop((time) => {
  cube.position.x = mouse.x * 2;
  cube.position.y = -mouse.y * 2;

  cube.rotation.x = time / 1000;
  cube.rotation.y = time / 1500;

  renderer.render(scene, camera);
});

Le * 2 choisit l’amplitude : le cube va de -2 à 2 unités. Le signe moins sur y compte : à l’écran, clientY grandit vers le bas, alors que dans la scène 3D l’axe y monte. Sans lui, le cube part à l’envers.

On lit mouse dans la boucle d’animation, pas dans l’écouteur : l’écouteur se contente de noter la position, la boucle décide quoi en faire à chaque image.

Pour aller plus loin

Pour un mouvement plus doux, le cube peut rattraper le pointeur petit à petit au lieu de sauter dessus. À chaque image, il parcourt 10 % de la distance qui lui reste :

main.js
cube.position.x += (mouse.x * 2 - cube.position.x) * 0.1;
cube.position.y += (-mouse.y * 2 - cube.position.y) * 0.1;

Essaie aussi de faire tourner la caméra plutôt que le cube, ou de changer la couleur du fond selon mouse.x.

Mise à jour : ven. 30 oct. à 17:05