~kota/slate

a2099a2e2e53debdb401c88743687202d2e4030d — Matthew W Eisenstein 3 months ago 5909286 master
Become a zoomer
2 files changed, 55 insertions(+), 22 deletions(-)

M ui/ts/main.ts
M ui/ts/modules/pieces.ts
M ui/ts/main.ts => ui/ts/main.ts +47 -14
@@ 8,16 8,20 @@ let context = canvas.getContext(
	{alpha: false}
) as CanvasRenderingContext2D;

context.imageSmoothingEnabled = false;

canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

let tableContents: (Piece|Stack)[] = [];

let center = new Vector2(canvas.width / 2, canvas.height / 2);
let offset = new Vector2(0, 0);
let pan = new Vector2(0, 0);
let panOrigin = new Vector2(0, 0);
let zoom = 1;
let zoomOrigin = 0;
let zoomCenter = new Vector2(0, 0);

let grabbed: (null|Piece|Stack) = null;
let panning = false;


@@ 32,7 36,7 @@ main();

let frames = 0;
setInterval(function() {
	console.log(frames);
	console.log("FPS:", frames);
	frames = 0;
}, 1000);



@@ 44,8 48,8 @@ function render() {
	for (let tableItem of tableContents) {
		context.drawImage(
			tableItem.image,
			Math.floor(tableItem.x - pan.x),
			Math.floor(tableItem.y - pan.y),
			Math.floor((tableItem.x * zoom) + pan.x),
			Math.floor((tableItem.y * zoom) + pan.y),
			Math.floor(tableItem.w * zoom),
			Math.floor(tableItem.h * zoom),
		);


@@ 53,22 57,40 @@ function render() {

	if (grabbed) {
		context.strokeStyle = "#cccc44";
		context.stroke(grabbed.boundaryPath(pan.negative()));
		context.stroke(grabbed.boundaryPath(pan, zoom));
	}
}

function changeZoom(factor: number) {
		let oldZoom = zoom;
		zoom *= 1 + factor;

		let zoomRatio = zoom / oldZoom;
		pan.x = (pan.x - zoomCenter.x) * zoomRatio + zoomCenter.x;
		pan.y = (pan.y - zoomCenter.y) * zoomRatio + zoomCenter.y;
}

canvas.addEventListener("contextmenu", function(event) {
	event.preventDefault();
	panOrigin.x = event.offsetX;
	panOrigin.y = event.offsetY;
	panning = true;
});

canvas.addEventListener("mousedown", function(event) {
	if (event.button === 2) {
		panOrigin.x = event.offsetX;
		panOrigin.y = event.offsetY;
		panning = true;
		return;
	}
	if (event.button === 1) {
		zoomOrigin = event.offsetY;
		zoomCenter = new Vector2(event.offsetX, event.offsetY);
		zooming = true;
		return;
	}
	for (let tableItem of tableContents) {
		if (context.isPointInPath(tableItem.boundaryPath(pan.negative()), event.offsetX, event.offsetY)) {
			offset.x = event.offsetX - tableItem.x;
			offset.y = event.offsetY - tableItem.y;
		if (context.isPointInPath(tableItem.boundaryPath(pan, zoom), event.offsetX, event.offsetY)) {
			offset.x = event.offsetX / zoom - tableItem.x;
			offset.y = event.offsetY / zoom - tableItem.y;
			grabbed = tableItem;
		}
	}


@@ 76,20 98,31 @@ canvas.addEventListener("mousedown", function(event) {

canvas.addEventListener("mousemove", function(event: MouseEvent) {
	if (grabbed) {
		grabbed.x = event.offsetX - offset.x;
		grabbed.y = event.offsetY - offset.y;
		grabbed.x = event.offsetX / zoom - offset.x;
		grabbed.y = event.offsetY / zoom - offset.y;
	}
	if (panning) {
		pan.x += panOrigin.x - event.offsetX;
		pan.y += panOrigin.y - event.offsetY;
		pan.x += event.offsetX - panOrigin.x;
		pan.y += event.offsetY - panOrigin.y;
		panOrigin.x = event.offsetX;
		panOrigin.y = event.offsetY;
	}
	if (zooming) {
		changeZoom((zoomOrigin - event.offsetY) / 200);
		zoomOrigin = event.offsetY;
	}
});

canvas.addEventListener("wheel", function(event: WheelEvent) {
	zoomCenter = new Vector2(event.offsetX, event.offsetY);
	let direction = Math.sign(event.deltaY);
	changeZoom(direction * -0.1);
});

canvas.addEventListener("mouseup", function(event: MouseEvent) {
	grabbed = null;
	panning = false;
	zooming = false;
});

function main() {

M ui/ts/modules/pieces.ts => ui/ts/modules/pieces.ts +8 -8
@@ 12,7 12,7 @@ export abstract class GameObject {
	abstract url: string;
	abstract w: number;
	abstract h: number;
	abstract boundaryPath(offset: Vector2): Path2D;
	abstract boundaryPath(offset: Vector2, zoom: number): Path2D;

	// Add this.x and this.y which reference this.position
	get x() {


@@ 43,13 43,13 @@ export class Piece extends GameObject {
	#boundaryHIsSet = Promise.resolve();
	#boundaryWIsSet = Promise.resolve();

	boundaryPath(offset: Vector2): Path2D {
	boundaryPath(offset: Vector2, zoom: number): Path2D {
		let path = new Path2D();
		path.rect(
			this.x + this.boundary.x + offset.x,
			this.y + this.boundary.y + offset.y,
			this.boundary.w,
			this.boundary.h,
			(this.x + this.boundary.x) * zoom + offset.x,
			(this.y + this.boundary.y) * zoom + offset.y,
			this.boundary.w * zoom,
			this.boundary.h * zoom,
		);
		return path;
	}


@@ 169,9 169,9 @@ export class Stack extends GameObject {
	}

	// Pass boundary path to first contents child
	boundaryPath(offset: Vector2): Path2D {
	boundaryPath(offset: Vector2, zoom: number): Path2D {
		if (this.contents[0]) {
			return this.contents[0].boundaryPath(offset);
			return this.contents[0].boundaryPath(offset, zoom);
		} else {
			return new Path2D();
		}