I have a html5 canvas element that takes size of the parent div in JSX.
<canvas
ref={canvasRef}
width={canvasParentRef.current?.offsetWidth}
height={canvasParentRef.current?.offsetHeight}
className={style.canvas}
/>
In this canvas I always have a rectangle on the center position, and possibly some other rectangles which position is based on the coordinates of the center rectangle.
I have a function to calculate position of the main rectangle that is centered on canvas.
const centerRectCoords = () => {
const {width, height} = rect;
const canvas = canvasRef.current;
const startX = canvas.width / 2 - width / 2;
const startY = canvas.height / 2 - height / 2;
return {
startX,
startY,
endX: startX + width,
endY: startY + height,
};
};
But I have a problem, everything looks too blurry on higher resolution screens.
To prevent that, I have found some solution on MDN to scale canvas up. For that I have a following function:
const scaleCanvas = (
canvas: HTMLCanvasElement,
ctx: CanvasRenderingContext2D
): void => {
const canvasParent = canvasParentRef.current;
if (!canvasParent) return;
const { devicePixelRatio } = window;
const dimensions = canvasParent.getBoundingClientRect();
canvas.width = dimensions.width * devicePixelRatio;
canvas.height = dimensions.height * devicePixelRatio;
ctx.scale(devicePixelRatio, devicePixelRatio);
canvas.style.width = `${dimensions.width}px`;
canvas.style.height = `${dimensions.height}px`;
};
This function is called in useEffect
on mount.
But now whenever I generate these rectangles on higher resolution screen, they are not centered, because width and height on canvas is now bigger than style.width and style.height. I thought to maybe use style.width and style.height in centerRectCoords
function calculation, but those values are undefined because canvas is not visible in my app on load, it is another tab.
How can I make everything scale nicely on bigger and smallers screens, with correct sizing and without it being blurry?
2
Answers
It’s impossible to imagine what you’re doing there and you didn’t even post an image!
But since you mention that things screw up based on screen size, then maybe what you need is CSS Media Queries:
Screen media queries are CUSTOM element sizes and behavior, based on the user’s screen size.
Example of how screen media queries are used:
ie: Smaller screens do this…
ie: Bigger screens do that…
https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_media_queries/Using_media_queries
The problem is that since you do scale your context, the size of your canvas doesn’t map to your context’s visible area. If we say that you’re on a 2x monitor, then if the canvas is 500px wide, on the context you’d need to draw a
250
(magic unit) wide rectangle for it covers the whole bitmap.So what you need is to convert the coordinates and sizes from the outer world, into the ones in your context.
One quite cumbersome way, but which will work in most situations is to get the context’s current transformation as a
DOMMatrix
, invert it and then transform your coordinates based on this inverted matrix:But for this exact case, it might be a lot simpler and clearer to store the
contextWidth
andcontextHeight
properties directly in yourresize
handler:I’m not really into JSX, so I’ll let you figure out the best way to store these variables so it’s accessible to your script.