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 offsetX
etoffsetY
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 offsetX
et offsetY
prenez en compte les transformations CSS.
Le plus gros problème avec offsetX
et à offsetY
partir 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
, clientY
etcanvas.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 clientX
et top
de la même manière clientY
que 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.getBoundingClientRect
et clientX
etclientY
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 offsetX
etoffsetY
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.getBoundingClientRect
et event.clientX
etevent.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>