I'm currently developing an image editor with HTML5 Canvas, and I'm having a problem detecting the image coordinates when mousing over the canvas.
I have replicated the problem in a code snipped with a rectangle:

When:
What I want and I don't know how to do it:
(0, 0) of the canvas will become negative. And the displayed (0, 0) on the bottom should correspond with the blue text.1, but after changing to 0.5 or 1.5 should work in the same way.Here below I share the code snipped to see if anyone can help me with that because I'm a little frustrated and I'm sure it's silly. Thanks a lot!
const RECT_SIZE = 200
const ZOOM = 1
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
const svgPoint = svg.createSVGPoint()
const xform = svg.createSVGMatrix()
const canvas = document.querySelector('canvas')
const ctx = canvas.getContext('2d')
const res = document.querySelector('.res')
const pt = transformedPoint(canvas.width / 2, canvas.height / 2)
const X = canvas.width / 2 - RECT_SIZE / 2
const Y = canvas.height / 2 - RECT_SIZE / 2
function transformedPoint(x, y) {
svgPoint.x = x
svgPoint.y = y
return svgPoint.matrixTransform(xform.inverse())
}
function mousemove(e) {
const { left, top } = canvas.getBoundingClientRect()
res.textContent = `X: ${e.clientX - left} - Y: ${e.clientY - top}`
}
// SCALING CANVAS
ctx.translate(pt.x, pt.y)
ctx.scale(ZOOM, ZOOM)
ctx.translate(-pt.x, -pt.y)
// SETTING SOME DEFAULTS
ctx.lineWidth = 1
ctx.strokeStyle = 'green'
ctx.fillStyle = 'blue'
// DRAWING A REACTANGLE
ctx.beginPath()
ctx.strokeRect(X, Y, RECT_SIZE, RECT_SIZE)
ctx.font = "12px Arial";
ctx.fillText("0,0", X - 5, Y-10);
ctx.fillText("200,200", X + RECT_SIZE - 15, Y + RECT_SIZE + 15);
ctx.closePath()
canvas {
border: 1px solid red;
}
<canvas width="400" height="400" onmousemove="mousemove(event)"}></canvas>
<div class="res" />
Changing the line:
res.textContent = `X: ${e.clientX - left} - Y: ${e.clientY - top}`
to
res.textContent = `X: ${e.clientX - left - X} - Y: ${e.clientY - top - Y}`
Is displaying the correct coordinates when ZOOM = 1. However, is not displaying the correct coordinates after change the zoom.
You have to add ZOOM value to the X and Y calculation like in the example below. I used Math.round method as the final X and Y value is a long fraction.
const RECT_SIZE = 200
const ZOOM = 1.4
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
const svgPoint = svg.createSVGPoint()
const xform = svg.createSVGMatrix()
const canvas = document.querySelector('canvas')
const ctx = canvas.getContext('2d')
const res = document.querySelector('.res')
const pt = transformedPoint(canvas.width / 2, canvas.height / 2)
const X = canvas.width / 2 - RECT_SIZE / 2;
const Y = canvas.height / 2 - RECT_SIZE / 2;
canvas.addEventListener('mousemove',(event)=>{
const {top, left} = event.target.getBoundingClientRect();
const zoomX = Math.round((event.clientX - left - (canvas.width / 2 - ((RECT_SIZE * ZOOM) / 2)))/ZOOM);
const zoomY = Math.round((event.clientY - top - (canvas.height / 2 - ((RECT_SIZE * ZOOM) / 2)))/ZOOM);
res.textContent = `X: ${zoomX} - Y: ${zoomY}`;
});
function transformedPoint(x, y) {
svgPoint.x = x
svgPoint.y = y
return svgPoint.matrixTransform(xform.inverse())
}
// SCALING CANVAS
ctx.translate(pt.x, pt.y)
ctx.scale(ZOOM, ZOOM)
ctx.translate(-pt.x, -pt.y)
// SETTING SOME DEFAULTS
ctx.lineWidth = 1
ctx.strokeStyle = 'green'
ctx.fillStyle = 'blue'
// DRAWING A REACTANGLE
ctx.beginPath()
ctx.strokeRect(X, Y, RECT_SIZE, RECT_SIZE)
ctx.font = "12px Arial";
ctx.fillText("0,0", X - 5, Y-10);
ctx.fillText("200,200", X + RECT_SIZE - 15, Y + RECT_SIZE + 15);
ctx.closePath()
canvas {
border: 1px solid red;
}
<canvas width="400" height="400"}></canvas>
<div class="res" />
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With