Como obter as coordenadas de um Canvas em HTML5?
Quando utilizamos o HTML5 Canvas, é comum desejarmos obter as coordenadas X e Y do mouse em relação ao elemento para iteragir com o mesmo. Porém é muito comum ocorrer erros e confusões ao tentar capturar estas coordenadas quando o <canvas>
está transformado ou dimensionado através do CSS.
Através desse passo a passo, vamos aprender como capturar as coordenadas do canvas de maneira eficiente!
Obtendo as coordenadas do mouse relativo ao Canvas
O código mais comum para obter as coordenadas X e Y do ponteiro de um mouse em relação ao canvas, é o seguinte:
<canvas id='canvas' width="300" height="100"></canvas>
const canvas = document.querySelector('#canvas');
const context = canvas.getContext('2d');
context.fillStyle = '#000000'
context.fillRect(0, 0, canvas.width, canvas.height);
canvas.addEventListener('mousemove', function (event) {
// captura a posição X e Y do CANVAS
const rect = canvas.getBoundingClientRect();
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
context.fillStyle = '#ffffff'
context.fillRect(x, y, 5, 5)
console.log({x, y})
})
Resultado:
<script> document.addEventListener('DOMContentLoaded', function () { const canvas = document.querySelector('#canvas-normal'); const context = canvas.getContext('2d'); context.fillStyle = '#000000' context.fillRect(0, 0, canvas.width, canvas.height); canvas.addEventListener('mousemove', function (event) { const rect = canvas.getBoundingClientRect(); const x = event.clientX - rect.left; const y = event.clientY - rect.top; context.fillStyle = '#ffffff' context.fillRect(x, y, 5, 5)})
}) </script>
O código acima tem como finalidade desenhar um quadrado de 5x5px quando o mouse é movido sobre o Canvas. Precisamos obter a posição left
e top
do elemento, pois os valores clientX e clientY são relativos à tela e não ao elemento.
Tudo ocorre bem acima, porém, ao utilizar a mesma lógica quando o canvas possui alguma transformação pelo CSS, o comportamento é diferente.
Veja:
<canvas style="width: 100%" id='canvas' width="300" height="100"></canvas>
Resultado:
<script> document.addEventListener('DOMContentLoaded', function () { const canvas = document.querySelector('#canvas-errado'); const context = canvas.getContext('2d'); context.fillStyle = '#000000' context.fillRect(0, 0, canvas.width, canvas.height); canvas.addEventListener('mousemove', function (event) { const rect = canvas.getBoundingClientRect(); const x = event.clientX - rect.left; const y = event.clientY - rect.top; context.fillStyle = '#ffffff' context.fillRect(x, y, 5, 5)})
}); </script>
Como pode ser ver no exemplo anterior, é possivel notar que o cálculo realizado anteriormente não considera o tamanho do canvas
pelo CSS. Isso ocorre porque as coordenadas do canvas são relativos ao tamanho definido diretamente no elemento ou pelo Javascript - em outras palavras, o width
e height
definido diretamente no canvas
.
Obtendo a coordenadas de um Canvas redimensionado pelo CSS
Para corrigir isso, é necessário corrigir o valor das coordenadas X e Y considerando o tamanho computado do Canvas no cliente. Basicamente, vamos dividir as dimensões originais do canvas pelas dimensões computadas no cliente e multiplicar pelas respectivas coordenadas.
Veja:
const rect = canvas.getBoundingClientRect();
const x = (event.clientX - rect.left) * canvas.width / rect.width;
const y = (event.clientY - rect.top) * canvas.height / rect.height;
Como visto, utilizamos os valores width
e height
retornados por canvas.getBoundingClientRect()
, que são as dimensões do canvas
computadas no cliente. Assim, podemos aplicar a proporção sobre as coordenadas X e Y do elemento para efetuar a correção desejada.
O código final e o resultado são estes:
<canvas id="canvas" width="600" style="width: 100%"></canvas>
const canvas = document.querySelector('#canvas');
const context = canvas.getContext('2d');
context.fillStyle = '#202020'
context.fillRect(0, 0, canvas.width, canvas.height);
canvas.addEventListener('mousemove', function (event) {
const rect = canvas.getBoundingClientRect();
const x = (event.clientX - rect.left) * canvas.width / rect.width;
const y = (event.clientY - rect.top) * canvas.height / rect.height;
context.fillStyle = '#ffffff';
context.fillRect(x, y, 5, 5)
})
<script>
document.addEventListener('DOMContentLoaded', function (e) {
const canvas = document.querySelector('#canvas-correto');
const context = canvas.getContext('2d');
context.fillStyle = '#202020'
context.fillRect(0, 0, canvas.width, canvas.height);
canvas.addEventListener('mousemove', function (event) {
const rect = canvas.getBoundingClientRect();
const x = (event.clientX - rect.left) * canvas.width / rect.width;
const y = (event.clientY - rect.top) * canvas.height / rect.height;
context.fillStyle = '#ffffff'
context.fillRect(x, y, 5, 5)
})
});
</script>