Gestion des pointeurs
Les Pointer Events permettent de gérer la souris, le tactile et le stylet avec les mêmes événements. Un pointeur représente une source d'interaction : sur un écran tactile, plusieurs doigts peuvent donc être suivis indépendamment.
Les événements
Les trois événements principaux sont :
pointerdown: le début d'un contact ou l'appui d'un bouton de souris.pointermove: un déplacement du pointeur.pointerup: la fin du contact ou le relâchement.
Avec une souris, pointermove est aussi émis sans bouton appuyé. Pour suivre un geste, il faut donc mémoriser les contacts commencés avec pointerdown et ne traiter que leurs déplacements.
Pour un canvas interactif, ajoutez la propriété CSS touch-action :
canvas {
touch-action: none;
}
Elle indique que les gestes sur cet élément ne doivent pas être utilisés pour faire défiler ou zoomer la page.
Position et identifiant
Les propriétés event.clientX et event.clientY donnent la position du pointeur dans le viewport. Si le canvas occupe toute la fenêtre depuis son origine, elles correspondent directement à la position sur le canvas.
La propriété event.pointerId identifie le pointeur pendant l'interaction. Les événements d'un même contact portent le même identifiant. Il permet de retrouver quel doigt a bougé ou s'est relevé, sans dépendre de l'ordre des événements.
Pour un joystick limité à un seul doigt, mémorisez cet identifiant au premier contact et ignorez les autres jusqu'à son relâchement.
Direction d'un geste
Pour obtenir la direction d'un déplacement, mémorisez la position initiale du contact. La différence avec la position actuelle donne un vecteur :
const dx = event.clientX - startX;
const dy = event.clientY - startY;
La fonction Math.atan2(dy, dx) donne son angle en radians, entre -Math.PI et Math.PI. Contrairement à une division suivie de Math.atan, elle tient compte des signes des deux composantes pour identifier la direction.
Dans les coordonnées de l'écran, les X augmentent vers la droite et les Y vers le bas. On obtient donc les repères suivants :
| Direction du geste | Angle |
|---|---|
| Droite | 0 |
| Bas | Math.PI / 2 |
| Gauche | Math.PI ou -Math.PI |
| Haut | -Math.PI / 2 |
Si dx et dy sont nuls, le geste n'a pas de direction. Vous pouvez représenter cet état par false. Attention au test utilisé ensuite : l'angle 0 est valide, même s'il est considéré comme faux dans une condition JavaScript. Un test direction !== false permet de le distinguer de l'absence de direction.
Comme pour le clavier, les événements peuvent simplement mettre à jour un état. La boucle d'animation consulte ensuite la direction mémorisée pour appliquer un mouvement tant que le doigt reste décalé de son point de départ.
Gestion multi touch
Pour suivre plusieurs doigts, une Map permet d'associer chaque pointerId à ses informations. Voici un exemple qui mémorise les positions des contacts en cours :
const pointers = new Map();
canvas.addEventListener('pointerdown', (event) => {
pointers.set(event.pointerId, {
x: event.clientX,
y: event.clientY,
});
});
window.addEventListener('pointermove', (event) => {
const pointer = pointers.get(event.pointerId);
if (!pointer) return;
pointer.x = event.clientX;
pointer.y = event.clientY;
});
window.addEventListener('pointerup', (event) => {
pointers.delete(event.pointerId);
});
Le contact commence sur le canvas. Les déplacements et les relâchements sont écoutés sur window pour les recevoir aussi lorsque la souris quitte le canvas tout en restant dans la fenêtre.
Le navigateur peut aussi interrompre un contact avec pointercancel. Pour gérer cette interruption, vous pouvez effectuer le même nettoyage qu'au relâchement :
window.addEventListener('pointercancel', (event) => {
pointers.delete(event.pointerId);
});