I’ve a game with a determined frame size:
// The level frame original size
const frameWidth = 1140;
const frameHeight = 518;
I’ve tried different libraries for 2D rendering and I gave up of them either for bug or lack of features and anyway my game is cloud-based, so all the client does is mostly receive world packets, send packets and render the world. I’m betting the browser’s native HTML rendering won’t be a problem.
I want to fit this frame into the window; it must take its most minimal scale ratio that fits into the screen and get centered there:
import ScreenSize from '@/app/util/ScreenSize';
export default function optimalFitRatio(originalSize: ScreenSize, fitToSize: ScreenSize): number {
const horizontalRatio = fitToSize.width / originalSize.width;
const verticalRatio = fitToSize.height / originalSize.height;
return Math.min(horizontalRatio, verticalRatio);
}
Now, the problem is that I see that the position of the world entities vary depending on the window resolution. I wasn’t having this problem when using the mentioned "libraries".
Minimal reproduction:
function optimalFitRatio(originalSize, fitToSize) {
const horizontalRatio = fitToSize.width / originalSize.width;
const verticalRatio = fitToSize.height / originalSize.height;
return Math.min(horizontalRatio, verticalRatio);
}
// The level frame original size
const frameWidth = 1140;
const frameHeight = 518;
class LevelView {
container1 = undefined;
container2 = undefined;
resizeListener = undefined;
attached = false;
scale = 0;
width = 0;
height = 0;
attach() {
this.attached = true;
// Create first container
this.newContainer();
// Resizing
this.resizeListener = this.resizeFrame.bind(this);
window.addEventListener('resize', this.resizeListener);
this.resizeFrame();
}
detach() {
window.removeEventListener('resize', this.resizeListener);
this.container1?.remove();
this.attached = false;
}
renderLevel(world) {
this.container1?.remove();
this.newContainer();
this.orientContainer();
// Sort Z index
// sortEntitiesByZ(world.entities);
for (const entity of world.entities) {
if (entity.type == "Entity") {
this.renderEntity(entity);
}
}
}
renderEntity(entity) {
// Orient the bitmap
const orient = shape => {
shape.style.position = "fixed";
shape.style.left = `${entity.x}px`;
shape.style.top = `${entity.y}px`;
shape.style.transform = `rotate(${entity.rotationRadians}rad)`;
shape.style.transformOrigin = "center";
shape.style.userSelect = "none";
};
// Create the bitmap
const bitmap = document.createElement("img");
bitmap.src = "https://i.imgur.com/Dx200x9.png";
orient(bitmap);
this.container2.appendChild(bitmap);
}
resizeFrame() {
if (!this.attached) {
return;
}
// Fit the level frame original size into the actual resolution
const ratio = optimalFitRatio(
{ width: frameWidth, height: frameHeight },
{ width: window.innerWidth, height: window.innerHeight },
);
// Cache properties
this.width = frameWidth * ratio;
this.height = frameHeight * ratio;
this.scale = ratio;
// Orient
this.orientContainer();
}
orientContainer() {
// Resize the container
const [w, h] = [this.width, this.height];
this.container1.style.width = `${w}px`;
this.container1.style.height = `${h}px`;
// Scale the second container
this.container2.style.transform = `scale(${this.scale})`;
// Translate the container
this.container1.style.left = `${window.innerWidth / 2 - w / 2}px`;
this.container1.style.top = `${window.innerHeight / 2 - h / 2}px`;
}
newContainer() {
const c0 = document.body;
const c1 = document.createElement('div');
c1.style.display = "inline-block";
c1.style.background = "#000";
c1.style.overflow = "hidden";
c1.style.position = "fixed";
c1.style.userSelect = "none";
c0.appendChild(c1);
this.container1 = c1;
const c2 = document.createElement('div');
c1.appendChild(c2);
this.container2 = c2;
}
}
const levelView = new LevelView();
levelView.attach();
levelView.renderLevel({
entities: [{
type: "Entity",
x: 100,
y: 100,
rotationRadians: 0,
}],
});
(Updated: I forgot { type: "Entity", ...p }
above. Now you should see the issue…)
2
Answers
For perfect aspect ratio, add a
<canvas>
element and place your actual canvas on top of it.As for keeping the sizes of elements inside – use percentages.
Something like this:
Yeah, I also think using percentages for the your CSS utility classes would be able to retain the aspect ratio for other screen sizes.