Jest to więc zarówno prosty, ale nieco bardziej skomplikowany temat, niż się wydaje.
Po pierwsze, najczęściej są tu pytania skonfliktowane
Jak uzyskać względne współrzędne myszy elementu
Jak uzyskać współrzędne myszy Canvas Canvas dla API 2D Canvas lub WebGL
więc odpowiedzi
Jak uzyskać względne współrzędne myszy elementu
Niezależnie od tego, czy element jest płótnem otrzymującym element, względne współrzędne myszy są takie same dla wszystkich elementów.
Istnieją 2 proste odpowiedzi na pytanie „Jak uzyskać względne współrzędne myszy względem obszaru roboczego”
Prosta odpowiedź nr 1 użycie offsetX
ioffsetY
canvas.addEventListner('mousemove', (e) => {
const x = e.offsetX;
const y = e.offsetY;
});
Ta odpowiedź działa w Chrome, Firefox i Safari. W przeciwieństwie do wszystkich innych wartości zdarzeń offsetX
i offsetY
uwzględniają transformacje CSS.
Największym problemem offsetX
i offsetY
jest od 2019/05, że nie istnieją one na zdarzeniach dotykowych, więc nie można ich używać z iOS Safari. Istnieją one w Zdarzeniach Wskaźnikowych, które istnieją w Chrome i Firefox, ale nie w Safari, chociaż najwyraźniej Safari na tym działa .
Inną kwestią jest to, że wydarzenia muszą znajdować się na samym płótnie. Jeśli umieścisz je na jakimś innym elemencie lub oknie, nie będziesz mógł później wybrać płótna jako punktu odniesienia.
Prosta odpowiedź nr 2 clientX
, clientY
icanvas.getBoundingClientRect
Jeśli nie obchodzi Cię transformacja CSS, następną najprostszą odpowiedzią jest wywołanie canvas. getBoundingClientRect()
i odjęcie lewej od clientX
i top
od clientY
jak w
canvas.addEventListener('mousemove', (e) => {
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
});
Będzie to działać, dopóki nie będzie transformacji CSS. Działa również ze zdarzeniami dotykowymi, więc będzie działać z 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;
});
Jak uzyskać współrzędne myszy Canvas Canvas dla interfejsu API 2D Canvas
W tym celu musimy wziąć wartości, które otrzymaliśmy powyżej, i przekonwertować z rozmiaru wyświetlanego płótna na liczbę pikseli w samym płótnie
z canvas.getBoundingClientRect
i clientX
aclientY
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;
});
lub z offsetX
ioffsetY
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;
});
Uwaga: we wszystkich przypadkach nie dodawaj do kanwy wypełnienia ani obramowań. Może to znacznie skomplikować kod. Zamiast tego chcesz, aby obramowanie lub wypełnienie otaczały płótno w jakimś innym elemencie i dodaj wypełnienie i / lub granicę do elementu zewnętrznego.
Przykład roboczy przy użyciu 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>
Przykład pracuje używając canvas.getBoundingClientRect
i event.clientX
aevent.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>