mirror of
https://codeberg.org/demostf/frontend.git
synced 2026-06-03 18:24:12 +02:00
103 lines
3.1 KiB
TypeScript
103 lines
3.1 KiB
TypeScript
import {Viewport, Point} from './Viewport';
|
|
|
|
export interface CenteredPanZoomOptions {
|
|
screenWidth: number;
|
|
screenHeight: number;
|
|
scale?: number;
|
|
contentSize: {
|
|
width: number;
|
|
height: number;
|
|
}
|
|
}
|
|
|
|
export class CenteredPanZoom {
|
|
screen: Viewport;
|
|
viewport: Viewport;
|
|
scale: number;
|
|
contentSize: {
|
|
width: number;
|
|
height: number;
|
|
};
|
|
|
|
constructor(options: CenteredPanZoomOptions) {
|
|
const scale = options.scale || (options.screenWidth / options.contentSize.width);
|
|
this.screen = new Viewport({
|
|
x: 0,
|
|
y: 0,
|
|
width: options.screenWidth,
|
|
height: options.screenHeight
|
|
});
|
|
this.viewport = new Viewport({
|
|
x: (options.screenWidth - (options.contentSize.width * scale)) / 2,
|
|
y: ( options.screenHeight - (options.contentSize.height * scale)) / 2,
|
|
width: options.screenWidth,
|
|
height: options.screenHeight
|
|
});
|
|
this.scale = scale;
|
|
this.contentSize = options.contentSize;
|
|
}
|
|
|
|
setSize(width, height) {
|
|
this.screen.width = width;
|
|
this.screen.height = height;
|
|
this.viewport.width = width * this.scale;
|
|
this.viewport.height = height * this.scale;
|
|
this.constrainPan(true);
|
|
}
|
|
|
|
setContentSize(width, height) {
|
|
this.contentSize = {width, height};
|
|
this.constrainPan(true);
|
|
}
|
|
|
|
pan(screenX, screenY) {
|
|
this.viewport.x = this.viewport.x + screenX;
|
|
this.viewport.y = this.viewport.y + screenY;
|
|
this.constrainPan();
|
|
}
|
|
|
|
constrainPan(strict = false) {
|
|
const minX = (strict) ? 0 : this.screen.width * 0.5;
|
|
const minY = (strict) ? 0 : this.screen.height * 0.5;
|
|
this.viewport.x = Math.min(minX, this.viewport.x);
|
|
this.viewport.y = Math.min(minY * 0.5, this.viewport.y);
|
|
const maxY = (this.screen.height - (this.contentSize.height * this.scale)) - (strict ? 0 : this.screen.height * 0.5);
|
|
const maxX = (this.screen.width - (this.contentSize.width * this.scale)) - (strict ? 0 : this.screen.width * 0.5);
|
|
this.viewport.y = Math.max(maxY, this.viewport.y);
|
|
this.viewport.x = Math.max(maxX, this.viewport.x);
|
|
if (maxY > ((strict) ? 0 : this.screen.height * 0.5)) {
|
|
this.viewport.y = Math.floor(maxY / 2);
|
|
}
|
|
if (maxX > ((strict) ? 0 : this.screen.width * 0.5)) {
|
|
this.viewport.x = Math.floor(maxX / 2);
|
|
}
|
|
}
|
|
|
|
panFrom(screenStart: Point, screenEnd: Point) {
|
|
this.pan(screenEnd.x - screenStart.x, screenEnd.y - screenStart.y);
|
|
}
|
|
|
|
//find zoom point in pre-zoom viewport
|
|
//make that point the same in the post-zoom viewport
|
|
zoom(scale: number, screenCenter: Point) {
|
|
const v1 = Viewport.convert(screenCenter, {
|
|
from: this.screen,
|
|
to: this.viewport
|
|
});
|
|
this.viewport.x = this.viewport.x * (scale / this.scale);
|
|
this.viewport.y = this.viewport.y * (scale / this.scale);
|
|
this.viewport.width = this.screen.width * scale;
|
|
this.viewport.height = this.screen.height * scale;
|
|
const minScale = Math.min(this.screen.width / this.contentSize.width, this.screen.height / this.contentSize.height);
|
|
scale = Math.max(minScale, scale);
|
|
this.scale = scale;
|
|
|
|
const v2 = Viewport.convert(screenCenter, {
|
|
from: this.screen,
|
|
to: this.viewport
|
|
});
|
|
const deltaX = v2.x - v1.x;
|
|
const deltaY = v2.y - v1.y;
|
|
this.pan(deltaX * scale, deltaY * scale);
|
|
}
|
|
}
|