2021-05-04 11:29:37 +02:00
|
|
|
interface Size {
|
|
|
|
width: number;
|
|
|
|
height: number;
|
|
|
|
}
|
|
|
|
|
|
|
|
export class HdpiManager {
|
|
|
|
private _zoomModifier: number = 1;
|
|
|
|
|
2021-05-04 14:08:40 +02:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
* @param minRecommendedGamePixelsNumber The minimum number of pixels we want to display "by default" to the user
|
|
|
|
* @param absoluteMinPixelNumber The very minimum of game pixels to display. Below, we forbid zooming more
|
|
|
|
*/
|
2021-05-17 15:49:18 +02:00
|
|
|
public constructor(private minRecommendedGamePixelsNumber: number, private absoluteMinPixelNumber: number) {}
|
2021-05-04 11:29:37 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the optimal size in "game pixels" based on the screen size in "real pixels".
|
|
|
|
*
|
|
|
|
* Note: the function is returning the optimal size in "game pixels" in the "game" property,
|
|
|
|
* but also recommends resizing the "real" pixel screen size of the canvas.
|
|
|
|
* The proposed new real size is a few pixels bigger than the real size available (if the size is not a multiple of the pixel size) and should overflow.
|
|
|
|
*
|
|
|
|
* @param realPixelScreenSize
|
|
|
|
*/
|
2021-09-06 14:27:54 +02:00
|
|
|
public getOptimalGameSize(realPixelScreenSize: Size): { game: Size; real: Size } {
|
2021-05-04 11:29:37 +02:00
|
|
|
const realPixelNumber = realPixelScreenSize.width * realPixelScreenSize.height;
|
|
|
|
// If the screen has not a definition small enough to match the minimum number of pixels we want to display,
|
|
|
|
// let's make the canvas the size of the screen (in real pixels)
|
2021-05-04 14:08:40 +02:00
|
|
|
if (realPixelNumber <= this.minRecommendedGamePixelsNumber) {
|
2021-05-04 11:29:37 +02:00
|
|
|
return {
|
|
|
|
game: realPixelScreenSize,
|
2021-09-06 14:27:54 +02:00
|
|
|
real: realPixelScreenSize,
|
2021-05-04 11:29:37 +02:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2021-05-17 15:49:18 +02:00
|
|
|
const optimalZoomLevel = this.getOptimalZoomLevel(realPixelNumber);
|
2021-05-04 11:29:37 +02:00
|
|
|
|
2021-05-04 12:09:00 +02:00
|
|
|
// Has the canvas more pixels than the screen? This is forbidden
|
2021-05-17 15:49:18 +02:00
|
|
|
if (optimalZoomLevel * this._zoomModifier < 1) {
|
2021-05-04 12:09:00 +02:00
|
|
|
// Let's reset the zoom modifier (WARNING this is a SIDE EFFECT in a getter)
|
2021-05-17 15:49:18 +02:00
|
|
|
this._zoomModifier = 1 / optimalZoomLevel;
|
2021-05-04 12:09:00 +02:00
|
|
|
|
|
|
|
return {
|
|
|
|
game: {
|
|
|
|
width: realPixelScreenSize.width,
|
|
|
|
height: realPixelScreenSize.height,
|
|
|
|
},
|
|
|
|
real: {
|
|
|
|
width: realPixelScreenSize.width,
|
|
|
|
height: realPixelScreenSize.height,
|
2021-09-06 14:27:54 +02:00
|
|
|
},
|
|
|
|
};
|
2021-05-04 12:09:00 +02:00
|
|
|
}
|
|
|
|
|
2021-05-17 15:49:18 +02:00
|
|
|
const gameWidth = Math.ceil(realPixelScreenSize.width / optimalZoomLevel / this._zoomModifier);
|
|
|
|
const gameHeight = Math.ceil(realPixelScreenSize.height / optimalZoomLevel / this._zoomModifier);
|
2021-05-04 14:08:40 +02:00
|
|
|
|
|
|
|
// Let's ensure we display a minimum of pixels, even if crazily zoomed in.
|
|
|
|
if (gameWidth * gameHeight < this.absoluteMinPixelNumber) {
|
2021-09-06 14:27:54 +02:00
|
|
|
const minGameHeight = Math.sqrt(
|
|
|
|
(this.absoluteMinPixelNumber * realPixelScreenSize.height) / realPixelScreenSize.width
|
|
|
|
);
|
|
|
|
const minGameWidth = Math.sqrt(
|
|
|
|
(this.absoluteMinPixelNumber * realPixelScreenSize.width) / realPixelScreenSize.height
|
|
|
|
);
|
2021-05-04 14:08:40 +02:00
|
|
|
|
|
|
|
// Let's reset the zoom modifier (WARNING this is a SIDE EFFECT in a getter)
|
2021-05-17 15:49:18 +02:00
|
|
|
this._zoomModifier = realPixelScreenSize.width / minGameWidth / optimalZoomLevel;
|
2021-05-04 14:08:40 +02:00
|
|
|
|
|
|
|
return {
|
|
|
|
game: {
|
|
|
|
width: minGameWidth,
|
|
|
|
height: minGameHeight,
|
|
|
|
},
|
|
|
|
real: {
|
|
|
|
width: realPixelScreenSize.width,
|
|
|
|
height: realPixelScreenSize.height,
|
2021-09-06 14:27:54 +02:00
|
|
|
},
|
|
|
|
};
|
2021-05-04 14:08:40 +02:00
|
|
|
}
|
|
|
|
|
2021-05-04 11:29:37 +02:00
|
|
|
return {
|
|
|
|
game: {
|
2021-05-04 14:08:40 +02:00
|
|
|
width: gameWidth,
|
|
|
|
height: gameHeight,
|
2021-05-04 11:29:37 +02:00
|
|
|
},
|
|
|
|
real: {
|
2021-05-17 15:49:18 +02:00
|
|
|
width: Math.ceil(realPixelScreenSize.width / optimalZoomLevel) * optimalZoomLevel,
|
|
|
|
height: Math.ceil(realPixelScreenSize.height / optimalZoomLevel) * optimalZoomLevel,
|
2021-09-06 14:27:54 +02:00
|
|
|
},
|
|
|
|
};
|
2021-05-04 11:29:37 +02:00
|
|
|
}
|
|
|
|
|
2021-05-17 15:49:18 +02:00
|
|
|
/**
|
|
|
|
* We only accept integer but we make an exception for 1.5
|
|
|
|
*/
|
2021-12-07 12:48:08 +01:00
|
|
|
public getOptimalZoomLevel(realPixelNumber: number): number {
|
2021-05-17 15:49:18 +02:00
|
|
|
const result = Math.sqrt(realPixelNumber / this.minRecommendedGamePixelsNumber);
|
|
|
|
if (1.5 <= result && result < 2) {
|
2021-09-06 14:27:54 +02:00
|
|
|
return 1.5;
|
2021-05-17 15:49:18 +02:00
|
|
|
} else {
|
|
|
|
return Math.floor(result);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-04 11:29:37 +02:00
|
|
|
public get zoomModifier(): number {
|
|
|
|
return this._zoomModifier;
|
|
|
|
}
|
|
|
|
|
|
|
|
public set zoomModifier(zoomModifier: number) {
|
|
|
|
this._zoomModifier = zoomModifier;
|
|
|
|
}
|
|
|
|
}
|