From 03d51937f409a32c5b15a5e9e3b36f4fdb68f32b Mon Sep 17 00:00:00 2001 From: Reinder Nijhoff Date: Tue, 14 Nov 2023 14:21:34 +0100 Subject: [PATCH] Refactor and clean up --- README.md | 47 +++++++++++++++ package.json | 13 ++--- src/lib/MouseListener.ts | 106 +++++++++++++++++----------------- src/lib/RotationController.ts | 8 +-- 4 files changed, 110 insertions(+), 64 deletions(-) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..ba23a56 --- /dev/null +++ b/README.md @@ -0,0 +1,47 @@ +# Panorama Renderer + +The panorama-renderer is a lightweight package that allows you to render equirectangular panoramas using WebGL. + +## Getting started + +### Installing + +Add `@mediamonks/panorama-renderer` to your project: + +```sh +npm i @mediamonks/panorama-renderer +``` +## Basic usage + +Simple panorama rendering on canvas. Make sure the image is loaded before creating the renderer. + +```ts +import { PanoramaRenderer } from '@mediamonks/panorama-renderer'; + +const renderer = PanoramaRenderer(wrapperElement, image, { loop: true }); +``` + +## Building + +In order to build seng-event, ensure that you have [Git](http://git-scm.com/downloads) +and [Node.js](http://nodejs.org/) installed. + +Clone a copy of the repo: +```sh +git clone https://github.com/mediamonks/panorama-renderer.git +``` + +Change to the image-effect-renderer directory: +```sh +cd panorama-renderer +``` + +Install dev dependencies: +```sh +npm i +``` + +Build package: +```sh +npm run build +``` diff --git a/package.json b/package.json index e069df3..3bb3585 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,14 @@ { "name": "@mediamonks/panorama-renderer", "version": "1.0.0", - "description": "The image-effect-renderer is lightweight package that allows you to run WebGL fragment shaders in your website using WebGL. It can be used to apply effects to HTML image or video sources.", + "description": "The panorama-renderer is a lightweight package that allows you to render equirectangular panoramas using WebGL.", "keywords": [ "fragment shader", "webgl", - "image", - "video", - "effect", - "shadertoy", - "glsl", - "shader", - "oneshader" + "panorama", + "360", + "equirectangular", + "equirectangular panorama" ], "repository": "git@github.com:mediamonks/panorama-renderer.git", "author": "Reinder Nijhoff ", diff --git a/src/lib/MouseListener.ts b/src/lib/MouseListener.ts index c8187c3..dda003d 100644 --- a/src/lib/MouseListener.ts +++ b/src/lib/MouseListener.ts @@ -1,25 +1,21 @@ +import type {Vec2} from "./Math.js"; + class MouseButton { public press: boolean = false; // currently pressed public down: boolean = false; // moment of press start - public oldDown: boolean = false; - public hit: boolean = false; public downTime: number = 0; } export default class MouseListener { - private canvas: HTMLCanvasElement; - private mousePos: Float32Array = new Float32Array([0, 0]); - private previousMousePos: Float32Array = new Float32Array([0, 0]); - private mouseVelocity: Float32Array = new Float32Array([0, 0]); - private normalized: Float32Array = new Float32Array([0, 0]); - private mouseClickCallbacks: any[] = []; + private readonly canvas: HTMLCanvasElement; + private mousePos: Vec2 = {x: 0, y: 0}; + private previousMousePos: Vec2 = {x: 0, y: 0}; + private mouseVelocity: Vec2 = {x: 0, y: 0}; + private normalized: Vec2 = {x: 0, y: 0}; + private mouseClickCallbacks: (() => void)[] = []; private buttons: MouseButton[] = []; private resetSpeed: boolean = false; - private touchMoveListener: any; - private endListener: any; - private mouseMoveListener: any; - constructor(canvas: HTMLCanvasElement) { this.canvas = canvas; @@ -27,28 +23,34 @@ export default class MouseListener { this.buttons.push(new MouseButton()); } - this.touchMoveListener = (e: { targetTouches: any[]; }) => this.setMouse(e.targetTouches[0]); - this.mouseMoveListener = (e: any) => this.setMouse(e); - this.endListener = () => { - this.buttons[0].press = false; - }; - - this.canvas.addEventListener('touchstart', this.onMouseStart, false); - this.canvas.addEventListener('touchmove', this.touchMoveListener, false); - this.canvas.addEventListener('touchend', this.endListener, false); - this.canvas.addEventListener('touchcancel', this.endListener, false); - this.canvas.addEventListener('mousedown', this.onMouseStart, false); - this.canvas.addEventListener('mousemove', this.mouseMoveListener, false); - this.canvas.addEventListener('mouseup', this.endListener, false); - this.canvas.addEventListener('mousecancel', this.endListener, false); - this.canvas.addEventListener('mouseout', this.endListener, false); + this.canvas.addEventListener('touchstart', this.onMouseStart.bind(this), false); + this.canvas.addEventListener('touchmove', this.touchMoveListener.bind(this), false); + this.canvas.addEventListener('touchend', this.endListener.bind(this), false); + this.canvas.addEventListener('touchcancel', this.endListener.bind(this), false); + this.canvas.addEventListener('mousedown', this.onMouseStart.bind(this), false); + this.canvas.addEventListener('mousemove', this.mouseMoveListener.bind(this), false); + this.canvas.addEventListener('mouseup', this.endListener.bind(this), false); + this.canvas.addEventListener('mousecancel', this.endListener.bind(this), false); + this.canvas.addEventListener('mouseout', this.endListener.bind(this), false); + } + + private touchMoveListener(e: TouchEvent) { + this.setMouse(e.targetTouches[0]); } - private onMouseStart = (event: any): void => { + private mouseMoveListener(e: MouseEvent) { + this.setMouse(e); + } + + private endListener() { + this.buttons[0].press = false; + } + + private onMouseStart(event: TouchEvent | MouseEvent) { event.preventDefault(); let isTouch = false; - if (window['TouchEvent'] && event instanceof TouchEvent) { + if (event instanceof TouchEvent) { isTouch = true; this.setMouse((event).targetTouches[0]); } else { @@ -58,57 +60,57 @@ export default class MouseListener { this.resetSpeed = true; this.buttons[isTouch ? 0 : event.which - 1].press = true; - for (let i = 0; i < this.mouseClickCallbacks.length; i++) { - this.mouseClickCallbacks[i].call(this); - } - }; + this.mouseClickCallbacks.forEach((callback) => { + callback(); + }); + } - private setMouse(event: any): void { - this.mousePos[0] = event.pageX; - this.mousePos[1] = event.pageY; + private setMouse(event: MouseEvent | Touch): void { + this.mousePos.x = event.pageX; + this.mousePos.y = event.pageY; } - public getNormalizedVelocity(): Float32Array { - return this.mouseVelocity; + public get normalizedVelocity(): Vec2 { + return {...this.mouseVelocity}; } public get mouseDown(): boolean { return this.buttons[0].press; } - public update(): void { - this.normalized[0] = this.mousePos[0] / this.canvas.clientWidth; - this.normalized[1] = this.mousePos[1] / this.canvas.clientHeight; + public click(callback: () => void) { + this.mouseClickCallbacks.push(callback); + } + + public update(dt: number): void { + this.normalized.x = this.mousePos.x / this.canvas.clientWidth; + this.normalized.y = this.mousePos.y / this.canvas.clientHeight; if (this.resetSpeed) { this.resetSpeed = false; - this.mouseVelocity[0] = 0; - this.mouseVelocity[1] = 0; + this.mouseVelocity.x = 0; + this.mouseVelocity.y = 0; } else { - this.mouseVelocity[0] = this.normalized[0] - this.previousMousePos[0]; - this.mouseVelocity[1] = this.normalized[1] - this.previousMousePos[1]; + this.mouseVelocity.x = this.normalized.x - this.previousMousePos.x; + this.mouseVelocity.y = this.normalized.y - this.previousMousePos.y; } - this.previousMousePos[0] = this.normalized[0]; - this.previousMousePos[1] = this.normalized[1]; + this.previousMousePos.x = this.normalized.x; + this.previousMousePos.y = this.normalized.y; // this section makes sure a drag is not used as a click // when the mouse is released after a press longer than 0.25 sec, it is not a click for (let i = 0; i < 3; i++) { const button: MouseButton = this.buttons[i]; - button.hit = false; button.down = false; if (this.buttons[i].press) { if (button.downTime === 0) { button.down = true; } - button.downTime++; + button.downTime += dt; } else { - button.hit = button.downTime < 15 && button.oldDown; button.downTime = 0; } - - button.oldDown = button.press; } } diff --git a/src/lib/RotationController.ts b/src/lib/RotationController.ts index fdc1e19..9f43e96 100644 --- a/src/lib/RotationController.ts +++ b/src/lib/RotationController.ts @@ -73,7 +73,7 @@ export default class RotationController implements IRotationController { } public update(dt: number, rotation: Quat, animated: boolean): Quat { - this.mouseListener.update(); + this.mouseListener.update(dt); // aspect ratio can change const aspect = this.renderer.aspectRatio; @@ -81,14 +81,14 @@ export default class RotationController implements IRotationController { const fovH = Math.atan2(aspect * 0.5, z) * 2; if (this.mouseListener.mouseDown) { - const ms = this.mouseListener.getNormalizedVelocity(); + const ms = this.mouseListener.normalizedVelocity; this.lastUserRotateSpeed.x = lerp( - -ms[0] * fovH * (1 / dt), + -ms.x * fovH * (1 / dt), this.currentRotateSpeed.x, this.options.inertia, ); this.lastUserRotateSpeed.y = lerp( - ms[1] * this.renderer.fov * (1 / dt), + ms.y * this.renderer.fov * (1 / dt), this.currentRotateSpeed.y, this.options.inertia, );