C'est donc à la fois simple mais un sujet un peu plus compliqué qu'il n'y paraît.
Tout d'abord, il y a généralement des questions confondues ici
Comment obtenir des coordonnées de souris relatives à un élément
Comment obtenir les coordonnées de la souris pixel pixel pour l'API 2D Canvas ou WebGL
donc, réponses
Comment obtenir des coordonnées de souris relatives à un élément
Que l'élément soit ou non un canevas obtenant les coordonnées relatives de la souris de l'élément est le même pour tous les éléments.
Il y a 2 réponses simples à la question "Comment obtenir des coordonnées de souris relatives au canevas"
Réponse simple # 1 utilisez offsetXetoffsetY
canvas.addEventListner('mousemove', (e) => {
const x = e.offsetX;
const y = e.offsetY;
});
Cette réponse fonctionne dans Chrome, Firefox et Safari. Contrairement à toutes les autres valeurs d'événement offsetXet offsetYprenez en compte les transformations CSS.
Le plus gros problème avec offsetXet à offsetYpartir de 2019/05, ils n'existent pas sur les événements tactiles et ne peuvent donc pas être utilisés avec iOS Safari. Ils existent sur les événements de pointeur qui existent dans Chrome et Firefox, mais pas dans Safari, mais apparemment Safari y travaille .
Un autre problème est que les événements doivent être sur la toile elle-même. Si vous les placez sur un autre élément ou sur la fenêtre, vous ne pourrez plus choisir le canevas comme point de référence.
Réponse simple n ° 2 utilisation clientX, clientYetcanvas.getBoundingClientRect
Si vous ne vous souciez pas des transformations CSS, la réponse la plus simple suivante consiste à appeler canvas. getBoundingClientRect()et à soustraire la gauche de clientXet topde la même manière clientYque dans
canvas.addEventListener('mousemove', (e) => {
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
});
Cela fonctionnera tant qu'il n'y aura pas de transformations CSS. Il fonctionne également avec les événements tactiles et fonctionnera donc avec Safari iOS
canvas.addEventListener('touchmove', (e) => {
const rect = canvas. getBoundingClientRect();
const x = e.touches[0].clientX - rect.left;
const y = e.touches[0].clientY - rect.top;
});
Comment obtenir les coordonnées de la souris pixel pixel pour l'API 2D Canvas
Pour cela, nous devons prendre les valeurs ci-dessus et convertir la taille du canevas affichée en nombre de pixels dans le canevas lui-même
avec canvas.getBoundingClientRectet clientXetclientY
canvas.addEventListener('mousemove', (e) => {
const rect = canvas.getBoundingClientRect();
const elementRelativeX = e.clientX - rect.left;
const elementRelativeY = e.clientY - rect.top;
const canvasRelativeX = elementRelativeX * canvas.width / rect.width;
const canvasRelativeY = elementRelativeY * canvas.height / rect.height;
});
ou avec offsetXetoffsetY
canvas.addEventListener('mousemove', (e) => {
const elementRelativeX = e.offsetX;
const elementRelativeX = e.offsetY;
const canvasRelativeX = elementRelativeX * canvas.width / canvas.clientWidth;
const canvasRelativeY = elementRelativeX * canvas.height / canvas.clientHeight;
});
Remarque: Dans tous les cas, n'ajoutez pas de rembourrage ou de bordures au canevas. Cela compliquerait considérablement le code. Au lieu de cela, vous voulez qu'une bordure ou un rembourrage entoure le canevas dans un autre élément et ajoute le rembourrage et / ou la bordure à l'élément extérieur.
Exemple de travail utilisant event.offsetX,event.offsetY
[...document.querySelectorAll('canvas')].forEach((canvas) => {
const ctx = canvas.getContext('2d');
ctx.canvas.width = ctx.canvas.clientWidth;
ctx.canvas.height = ctx.canvas.clientHeight;
let count = 0;
function draw(e, radius = 1) {
const pos = {
x: e.offsetX * canvas.width / canvas.clientWidth,
y: e.offsetY * canvas.height / canvas.clientHeight,
};
document.querySelector('#debug').textContent = count;
ctx.beginPath();
ctx.arc(pos.x, pos.y, radius, 0, Math.PI * 2);
ctx.fillStyle = hsl((count++ % 100) / 100, 1, 0.5);
ctx.fill();
}
function preventDefault(e) {
e.preventDefault();
}
if (window.PointerEvent) {
canvas.addEventListener('pointermove', (e) => {
draw(e, Math.max(Math.max(e.width, e.height) / 2, 1));
});
canvas.addEventListener('touchstart', preventDefault, {passive: false});
canvas.addEventListener('touchmove', preventDefault, {passive: false});
} else {
canvas.addEventListener('mousemove', draw);
canvas.addEventListener('mousedown', preventDefault);
}
});
function hsl(h, s, l) {
return `hsl(${h * 360 | 0},${s * 100 | 0}%,${l * 100 | 0}%)`;
}
.scene {
width: 200px;
height: 200px;
perspective: 600px;
}
.cube {
width: 100%;
height: 100%;
position: relative;
transform-style: preserve-3d;
animation-duration: 16s;
animation-name: rotate;
animation-iteration-count: infinite;
animation-timing-function: linear;
}
@keyframes rotate {
from { transform: translateZ(-100px) rotateX( 0deg) rotateY( 0deg); }
to { transform: translateZ(-100px) rotateX(360deg) rotateY(720deg); }
}
.cube__face {
position: absolute;
width: 200px;
height: 200px;
display: block;
}
.cube__face--front { background: rgba(255, 0, 0, 0.2); transform: rotateY( 0deg) translateZ(100px); }
.cube__face--right { background: rgba(0, 255, 0, 0.2); transform: rotateY( 90deg) translateZ(100px); }
.cube__face--back { background: rgba(0, 0, 255, 0.2); transform: rotateY(180deg) translateZ(100px); }
.cube__face--left { background: rgba(255, 255, 0, 0.2); transform: rotateY(-90deg) translateZ(100px); }
.cube__face--top { background: rgba(0, 255, 255, 0.2); transform: rotateX( 90deg) translateZ(100px); }
.cube__face--bottom { background: rgba(255, 0, 255, 0.2); transform: rotateX(-90deg) translateZ(100px); }
<div class="scene">
<div class="cube">
<canvas class="cube__face cube__face--front"></canvas>
<canvas class="cube__face cube__face--back"></canvas>
<canvas class="cube__face cube__face--right"></canvas>
<canvas class="cube__face cube__face--left"></canvas>
<canvas class="cube__face cube__face--top"></canvas>
<canvas class="cube__face cube__face--bottom"></canvas>
</div>
</div>
<pre id="debug"></pre>
Exemple de travail utilisant canvas.getBoundingClientRectet event.clientXetevent.clientY
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
ctx.canvas.width = ctx.canvas.clientWidth;
ctx.canvas.height = ctx.canvas.clientHeight;
let count = 0;
function draw(e, radius = 1) {
const rect = canvas.getBoundingClientRect();
const pos = {
x: (e.clientX - rect.left) * canvas.width / canvas.clientWidth,
y: (e.clientY - rect.top) * canvas.height / canvas.clientHeight,
};
ctx.beginPath();
ctx.arc(pos.x, pos.y, radius, 0, Math.PI * 2);
ctx.fillStyle = hsl((count++ % 100) / 100, 1, 0.5);
ctx.fill();
}
function preventDefault(e) {
e.preventDefault();
}
if (window.PointerEvent) {
canvas.addEventListener('pointermove', (e) => {
draw(e, Math.max(Math.max(e.width, e.height) / 2, 1));
});
canvas.addEventListener('touchstart', preventDefault, {passive: false});
canvas.addEventListener('touchmove', preventDefault, {passive: false});
} else {
canvas.addEventListener('mousemove', draw);
canvas.addEventListener('mousedown', preventDefault);
}
function hsl(h, s, l) {
return `hsl(${h * 360 | 0},${s * 100 | 0}%,${l * 100 | 0}%)`;
}
canvas { background: #FED; }
<canvas width="400" height="100" style="width: 300px; height: 200px"></canvas>
<div>canvas deliberately has differnt CSS size vs drawingbuffer size</div>