123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944 |
- /*
- * MARTINS.js
- * GPU-accelerated Augmented Reality for the web
- * Copyright (C) 2022-2024 Alexandre Martins <alemartf(at)gmail.com>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- *
- * viewport.ts
- * Viewport
- */
-
- import Speedy from 'speedy-vision';
- import { SpeedySize } from 'speedy-vision/types/core/speedy-size';
- import { SpeedyPromise } from 'speedy-vision/types/core/speedy-promise';
- import { Nullable } from '../utils/utils';
- import { Resolution } from './resolution';
- import { Utils } from '../utils/utils';
- import { IllegalArgumentError, IllegalOperationError, NotSupportedError, AccessDeniedError } from '../utils/errors';
- import { HUD, HUDContainer } from './hud';
- import { AREvent, AREventTarget, AREventListener } from '../utils/ar-events';
-
-
-
- /** Viewport container */
- export type ViewportContainer = HTMLDivElement;
-
- /** We admit that the size of the drawing buffer of the background canvas of the viewport may change over time */
- type ViewportSizeGetter = () => SpeedySize;
-
- /** All possible event types emitted by a Viewport */
- type ViewportEventType = 'resize';
-
- /** An event emitted by a Viewport */
- class ViewportEvent extends AREvent<ViewportEventType> { }
-
- /** Viewport event target */
- class ViewportEventTarget extends AREventTarget<ViewportEventType> { }
-
- /** Viewport style (immersive mode) */
- type ViewportStyle = 'best-fit' | 'stretch' | 'inline';
-
-
-
- /**
- * Viewport interface
- */
- export interface Viewport extends ViewportEventTarget
- {
- /** Resolution of the virtual scene */
- readonly resolution: Resolution;
-
- /** Viewport container */
- readonly container: ViewportContainer;
-
- /** Viewport style */
- style: ViewportStyle;
-
- /** HUD */
- readonly hud: HUD;
-
- /** Fullscreen mode */
- readonly fullscreen: boolean;
-
- /** Canvas on which the virtual scene will be drawn */
- readonly canvas: HTMLCanvasElement;
-
- /** Size of the drawing buffer of the foreground canvas */
- readonly virtualSize: SpeedySize;
-
- /** Request fullscreen mode */
- requestFullscreen(): SpeedyPromise<void>;
-
- /** Exit fullscreen mode */
- exitFullscreen(): SpeedyPromise<void>;
-
- /** Is the fullscreen mode available? */
- isFullscreenAvailable(): boolean;
-
- /** Canvas on which the physical scene will be drawn @internal */
- readonly _backgroundCanvas: HTMLCanvasElement;
-
- /** Size of the drawing buffer of the background canvas @internal */
- readonly _realSize: SpeedySize;
-
- /** Initialize the viewport @internal */
- _init(): void;
-
- /** Release the viewport @internal */
- _release(): void;
- }
-
- /**
- * Viewport constructor settings
- */
- export interface ViewportSettings
- {
- /** Viewport container */
- container: Nullable<ViewportContainer>;
-
- /** HUD container (must be a direct child of container) */
- hudContainer?: Nullable<HUDContainer>;
-
- /** Resolution of the canvas on which the virtual scene will be drawn */
- resolution?: Resolution;
-
- /** Viewport style */
- style?: ViewportStyle;
-
- /** An existing <canvas> on which the virtual scene will be drawn */
- canvas?: Nullable<HTMLCanvasElement>;
- }
-
- /** Default viewport constructor settings */
- const DEFAULT_VIEWPORT_SETTINGS: Readonly<Required<ViewportSettings>> = {
- container: null,
- hudContainer: null,
- resolution: 'lg',
- style: 'best-fit',
- canvas: null,
- };
-
- /** Z-index of the viewport container */
- const CONTAINER_ZINDEX = 1000000000;
-
- /** Base z-index of the children of the viewport container */
- const BASE_ZINDEX = 0;
-
- /** Z-index of the background canvas */
- const BACKGROUND_ZINDEX = BASE_ZINDEX + 0;
-
- /** Z-index of the foreground canvas */
- const FOREGROUND_ZINDEX = BASE_ZINDEX + 1;
-
- /** Z-index of the HUD */
- const HUD_ZINDEX = BASE_ZINDEX + 2;
-
- /** Default viewport width, in pixels */
- const DEFAULT_VIEWPORT_WIDTH = 300;
-
- /** Default viewport height, in pixels */
- const DEFAULT_VIEWPORT_HEIGHT = 150;
-
-
-
- /**
- * Viewport
- */
- export class BaseViewport extends ViewportEventTarget implements Viewport
- {
- /** Viewport resolution (controls the size of the drawing buffer of the foreground canvas) */
- private readonly _resolution: Resolution;
-
- /** Viewport container */
- protected readonly _container: ViewportContainer;
-
- /** An overlay displayed in front of the augmented scene */
- protected readonly _hud: HUD;
-
- /** Viewport style */
- protected _style: ViewportStyle;
-
- /** Internal canvas used to render the physical scene */
- private readonly __backgroundCanvas: HTMLCanvasElement;
-
- /** A canvas used to render the virtual scene */
- protected readonly _foregroundCanvas: HTMLCanvasElement;
-
- /** Original parent of the foreground canvas, if it's imported from somewhere */
- private readonly _parentOfImportedForegroundCanvas: Nullable<Node>;
-
-
-
- /**
- * Constructor
- * @param viewportSettings
- */
- constructor(viewportSettings: ViewportSettings)
- {
- super();
-
- const settings = Object.assign({}, DEFAULT_VIEWPORT_SETTINGS, viewportSettings);
- const size = Speedy.Size(DEFAULT_VIEWPORT_WIDTH, DEFAULT_VIEWPORT_HEIGHT);
-
- // validate settings
- if(settings.container == null)
- throw new IllegalArgumentError('Unspecified viewport container');
- else if(!(settings.container instanceof HTMLElement))
- throw new IllegalArgumentError('Invalid viewport container');
-
- // initialize attributes
- this._resolution = settings.resolution;
- this._container = settings.container;
- this._hud = new HUD(settings.container, settings.hudContainer);
-
- // make this more elegant?
- // need to initialize this._style and validate settings.style
- this._style = DEFAULT_VIEWPORT_SETTINGS.style;
- this.style = settings.style;
-
- // create the background canvas
- this.__backgroundCanvas = this._createBackgroundCanvas(this._container, size);
-
- // create the foreground canvas
- if(settings.canvas == null) {
- this._foregroundCanvas = this._createForegroundCanvas(this._container, size);
- this._parentOfImportedForegroundCanvas = null;
- }
- else {
- this._foregroundCanvas = settings.canvas;
- this._parentOfImportedForegroundCanvas = settings.canvas.parentNode;
- }
- }
-
- /**
- * Make a request to the user agent so that the viewport container is
- * displayed in fullscreen mode. The container must be a compatible element[1]
- * and the user must interact with the page in order to comply with browser
- * policies[2]. In case of error, the returned promise is rejected.
- * [1] https://developer.mozilla.org/en-US/docs/Web/API/Element/requestFullscreen#compatible_elements
- * [2] https://developer.mozilla.org/en-US/docs/Web/API/Element/requestFullscreen#security
- */
- requestFullscreen(): SpeedyPromise<void>
- {
- const container = this._container;
-
- // fallback for older WebKit versions
- if(container.requestFullscreen === undefined) {
- if((container as any).webkitRequestFullscreen === undefined)
- return Speedy.Promise.reject(new NotSupportedError());
- else if(!(document as any).webkitFullscreenEnabled)
- return Speedy.Promise.reject(new AccessDeniedError());
-
- // webkitRequestFullscreen() does not return a value
- (container as any).webkitRequestFullscreen();
-
- return new Speedy.Promise<void>((resolve, reject) => {
- setTimeout(() => {
- if(container === (document as any).webkitFullscreenElement)
- resolve();
- else
- reject(new TypeError());
- }, 100);
- });
- }
-
- // check if the fullscreen mode is available
- if(!document.fullscreenEnabled)
- return Speedy.Promise.reject(new AccessDeniedError());
-
- // request fullscreen
- return new Speedy.Promise<void>((resolve, reject) => {
- container.requestFullscreen({
- navigationUI: 'hide'
- }).then(resolve, reject);
- });
- }
-
- /**
- * Exit fullscreen mode
- */
- exitFullscreen(): SpeedyPromise<void>
- {
- // fallback for older WebKit versions
- if(document.exitFullscreen === undefined) {
- const doc = document as any;
-
- if(doc.webkitExitFullscreen === undefined)
- return Speedy.Promise.reject(new NotSupportedError());
- else if(doc.webkitFullscreenElement === null)
- return Speedy.Promise.reject(new IllegalOperationError('Not in fullscreen mode'));
-
- // webkitExitFullscreen() does not return a value
- doc.webkitExitFullscreen();
-
- return new Speedy.Promise<void>((resolve, reject) => {
- setTimeout(() => {
- if(doc.webkitFullscreenElement === null)
- resolve();
- else
- reject(new TypeError());
- }, 100);
- });
- }
-
- // exit fullscreen
- return new Speedy.Promise<void>((resolve, reject) => {
- document.exitFullscreen().then(resolve, reject);
- });
- }
-
- /** Is the fullscreen mode available? */
- isFullscreenAvailable(): boolean
- {
- return document.fullscreenEnabled ||
- !!((document as any).webkitFullscreenEnabled);
- }
-
- /**
- * True if the viewport is being displayed in fullscreen mode
- */
- get fullscreen(): boolean
- {
- if(document.fullscreenElement !== undefined)
- return document.fullscreenElement === this._container;
- else if((document as any).webkitFullscreenElement !== undefined)
- return (document as any).webkitFullscreenElement === this._container;
- else
- return false;
- }
-
- /**
- * Viewport container
- */
- get container(): ViewportContainer
- {
- return this._container;
- }
-
- /**
- * Viewport style
- */
- get style(): ViewportStyle
- {
- return this._style;
- }
-
- /**
- * Set viewport style
- */
- set style(value: ViewportStyle)
- {
- if(value != 'best-fit' && value != 'stretch' && value != 'inline')
- throw new IllegalArgumentError('Invalid viewport style: ' + value);
-
- const changed = (value != this._style);
- this._style = value;
-
- if(changed) {
- const event = new ViewportEvent('resize');
- this.dispatchEvent(event);
- }
- }
-
- /**
- * HUD
- */
- get hud(): HUD
- {
- return this._hud;
- }
-
- /**
- * Resolution of the virtual scene
- */
- get resolution(): Resolution
- {
- return this._resolution;
- }
-
- /**
- * Size in pixels of the drawing buffer of the canvas
- * on which the virtual scene will be drawn
- */
- get virtualSize(): SpeedySize
- {
- const aspectRatio = this._backgroundCanvas.width / this._backgroundCanvas.height;
- return Utils.resolution(this._resolution, aspectRatio);
- }
-
- /**
- * The canvas on which the virtual scene will be drawn
- */
- get canvas(): HTMLCanvasElement
- {
- return this._foregroundCanvas;
- }
-
- /**
- * The canvas on which the physical scene will be drawn
- * @internal
- */
- get _backgroundCanvas(): HTMLCanvasElement
- {
- return this.__backgroundCanvas;
- }
-
- /**
- * Size of the drawing buffer of the background canvas, in pixels
- * @internal
- */
- get _realSize(): SpeedySize
- {
- throw new IllegalOperationError();
- }
-
- /**
- * Initialize the viewport (when the session starts)
- * @internal
- */
- _init(): void
- {
- // import foreground canvas
- if(this._parentOfImportedForegroundCanvas != null) {
- const size = Speedy.Size(DEFAULT_VIEWPORT_WIDTH, DEFAULT_VIEWPORT_HEIGHT);
- this._importForegroundCanvas(this._foregroundCanvas, this._container, size);
- }
-
- // setup CSS
- this._container.style.touchAction = 'none';
- this._container.style.backgroundColor = 'black';
- this._container.style.zIndex = String(CONTAINER_ZINDEX);
-
- // initialize the HUD
- this._hud._init(HUD_ZINDEX);
- this._hud.visible = true;
- }
-
- /**
- * Release the viewport (when the session starts)
- * @internal
- */
- _release(): void
- {
- // release the HUD
- this._hud._release();
-
- // reset the CSS
- this._container.style.touchAction = 'auto';
-
- // restore imported canvas
- if(this._parentOfImportedForegroundCanvas != null)
- this._restoreImportedForegroundCanvas();
- }
-
- /**
- * Create a canvas and attach it to another HTML element
- * @param parent parent container
- * @param size size of the drawing buffer
- * @returns a new canvas as a child of parent
- */
- private _createCanvas(parent: HTMLElement, size: SpeedySize): HTMLCanvasElement
- {
- const canvas = document.createElement('canvas') as HTMLCanvasElement;
-
- canvas.width = size.width;
- canvas.height = size.height;
- parent.appendChild(canvas);
-
- return canvas;
- }
-
- /**
- * Create the background canvas
- * @param parent parent container
- * @param size size of the drawing buffer
- * @returns a new canvas as a child of parent
- */
- private _createBackgroundCanvas(parent: ViewportContainer, size: SpeedySize): HTMLCanvasElement
- {
- const canvas = this._createCanvas(parent, size);
- return this._styleCanvas(canvas, BACKGROUND_ZINDEX);
- }
-
- /**
- * Create the foreground canvas
- * @param parent parent container
- * @param size size of the drawing buffer
- * @returns a new canvas as a child of parent
- */
- private _createForegroundCanvas(parent: ViewportContainer, size: SpeedySize): HTMLCanvasElement
- {
- const canvas = this._createCanvas(parent, size);
- return this._styleCanvas(canvas, FOREGROUND_ZINDEX);
- }
-
- /**
- * Import an existing foreground canvas to the viewport
- * @param canvas existing canvas
- * @param parent parent container
- * @param size size of the drawing buffer
- * @returns the input canvas
- */
- private _importForegroundCanvas(canvas: HTMLCanvasElement, parent: ViewportContainer, size: SpeedySize): HTMLCanvasElement
- {
- if(!(canvas instanceof HTMLCanvasElement))
- throw new IllegalArgumentError('Not a canvas: ' + canvas);
-
- // borrow the canvas; add it as a child of the viewport container
- canvas.remove();
- parent.appendChild(canvas);
-
- canvas.width = size.width;
- canvas.height = size.height;
-
- canvas.dataset.cssText = canvas.style.cssText; // save CSS
- canvas.style.cssText = ''; // clear CSS
- this._styleCanvas(canvas, FOREGROUND_ZINDEX);
-
- return canvas;
- }
-
- /**
- * Restore a previously imported foreground canvas to its original parent
- */
- private _restoreImportedForegroundCanvas(): void
- {
- // not an imported canvas; nothing to do
- if(this._parentOfImportedForegroundCanvas == null)
- throw new IllegalOperationError();
-
- const canvas = this._foregroundCanvas;
- canvas.style.cssText = canvas.dataset.cssText || ''; // restore CSS
- canvas.remove();
- this._parentOfImportedForegroundCanvas.appendChild(canvas);
- }
-
- /**
- * Add suitable CSS rules to a canvas
- * @param canvas
- * @param canvasType
- * @returns canvas
- */
- private _styleCanvas(canvas: HTMLCanvasElement, zIndex: number): HTMLCanvasElement
- {
- canvas.style.position = 'absolute';
- canvas.style.left = '0px';
- canvas.style.top = '0px';
- canvas.style.width = '100%';
- canvas.style.height = '100%';
- canvas.style.zIndex = String(zIndex);
-
- return canvas;
- }
- }
-
- /**
- * Viewport decorator
- */
- abstract class ViewportDecorator extends ViewportEventTarget implements Viewport
- {
- /** The decorated viewport */
- private _base: Viewport;
-
- /** Size getter (the size of the viewport may change over time) */
- private _getSize: ViewportSizeGetter;
-
-
-
- /**
- * Constructor
- * @param base to be decorated
- * @param getSize size getter
- */
- constructor(base: Viewport, getSize: ViewportSizeGetter)
- {
- super();
-
- this._base = base;
- this._getSize = getSize;
- }
-
- /**
- * Viewport container
- */
- get container(): ViewportContainer
- {
- return this._base.container;
- }
-
- /**
- * Viewport style
- */
- get style(): ViewportStyle
- {
- return this._base.style;
- }
-
- /**
- * Set viewport style
- */
- set style(value: ViewportStyle)
- {
- this._base.style = value;
- }
-
- /**
- * HUD
- */
- get hud(): HUD
- {
- return this._base.hud;
- }
-
- /**
- * Fullscreen mode
- */
- get fullscreen(): boolean
- {
- return this._base.fullscreen;
- }
-
- /**
- * Resolution of the virtual scene
- */
- get resolution(): Resolution
- {
- return this._base.resolution;
- }
-
- /**
- * Size in pixels of the drawing buffer of the canvas
- * on which the virtual scene will be drawn
- */
- get virtualSize(): SpeedySize
- {
- return this._base.virtualSize;
- }
-
- /**
- * The canvas on which the virtual scene will be drawn
- */
- get canvas(): HTMLCanvasElement
- {
- return this._base.canvas;
- }
-
- /**
- * Request fullscreen mode
- */
- requestFullscreen(): SpeedyPromise<void>
- {
- return this._base.requestFullscreen();
- }
-
- /**
- * Exit fullscreen mode
- */
- exitFullscreen(): SpeedyPromise<void>
- {
- return this._base.exitFullscreen();
- }
-
- /**
- * Is the fullscreen mode available?
- */
- isFullscreenAvailable(): boolean
- {
- return this._base.isFullscreenAvailable();
- }
-
- /**
- * Background canvas
- * @internal
- */
- get _backgroundCanvas(): HTMLCanvasElement
- {
- return this._base._backgroundCanvas;
- }
-
- /**
- * Size of the drawing buffer of the background canvas, in pixels
- * @internal
- */
- get _realSize(): SpeedySize
- {
- return this._getSize();
- }
-
- /**
- * Initialize the viewport
- * @internal
- */
- _init(): void
- {
- this._base._init();
- }
-
- /**
- * Release the viewport
- * @internal
- */
- _release(): void
- {
- this._base._release();
- }
-
- /**
- * Add event listener
- * @param type event type
- * @param callback
- */
- addEventListener(type: ViewportEventType, callback: AREventListener): void
- {
- this._base.addEventListener(type, callback);
- }
-
- /**
- * Remove event listener
- * @param type event type
- * @param callback
- */
- removeEventListener(type: ViewportEventType, callback: AREventListener): void
- {
- this._base.removeEventListener(type, callback);
- }
-
- /**
- * Synchronously trigger an event
- * @param event
- * @returns same value as a standard event target
- * @internal
- */
- dispatchEvent(event: ViewportEvent): boolean
- {
- return this._base.dispatchEvent(event);
- }
- }
-
- /**
- * A viewport that watches for page resizes
- */
- abstract class ResizableViewport extends ViewportDecorator
- {
- /** is this viewport subject to being resized? */
- private _active: boolean;
-
-
-
- /**
- * Constructor
- * @param base to be decorated
- * @param getSize size getter
- */
- constructor(base: BaseViewport, getSize: ViewportSizeGetter)
- {
- super(base, getSize);
- this._active = false;
- this.addEventListener('resize', this._onResize.bind(this));
- }
-
- /**
- * Initialize the viewport
- * @internal
- */
- _init(): void
- {
- super._init();
- this._active = true;
-
- // Configure the resize listener. We want the viewport
- // to adjust itself if the phone/screen is resized or
- // changes orientation
- let timeout: Nullable<ReturnType<typeof setTimeout>> = null;
- const onWindowResize = () => {
- if(!this._active) {
- window.removeEventListener('resize', onWindowResize);
- return;
- }
-
- if(timeout !== null)
- clearTimeout(timeout);
-
- timeout = setTimeout(() => {
- timeout = null;
- this._resize();
- }, 50);
- };
- window.addEventListener('resize', onWindowResize);
-
- // handle changes of orientation
- // (is this needed? we already listen to resize events)
- if(screen.orientation !== undefined)
- screen.orientation.addEventListener('change', this._resize.bind(this));
- else
- window.addEventListener('orientationchange', this._resize.bind(this)); // deprecated
-
- // trigger a resize to setup the sizes / the CSS
- this._resize();
- }
-
- /**
- * Release the viewport
- * @internal
- */
- _release(): void
- {
- if(screen.orientation !== undefined)
- screen.orientation.removeEventListener('change', this._resize);
- else
- window.removeEventListener('orientationchange', this._resize); // deprecated
-
- this._active = false;
- super._release();
- }
-
- /**
- * Trigger a resize event
- */
- private _resize(): void
- {
- const event = new ViewportEvent('resize');
- this.dispatchEvent(event);
- }
-
- /**
- * Function to be called when the viewport is resized
- */
- protected _onResize(): void
- {
- // Resize the drawing buffer of the foreground canvas, so that it
- // matches the desired resolution, as well as the aspect ratio of the
- // background canvas
- const foregroundCanvas = this.canvas;
- const virtualSize = this.virtualSize;
- foregroundCanvas.width = virtualSize.width;
- foregroundCanvas.height = virtualSize.height;
-
- // Resize the drawing buffer of the background canvas
- const backgroundCanvas = this._backgroundCanvas;
- const realSize = this._realSize;
- backgroundCanvas.width = realSize.width;
- backgroundCanvas.height = realSize.height;
- }
- }
-
- /**
- * Immersive viewport: it occupies the entire page
- */
- export class ImmersiveViewport extends ResizableViewport
- {
- /**
- * Release the viewport
- * @internal
- */
- _release(): void
- {
- this.canvas.remove();
- this._backgroundCanvas.remove();
- this.hud.visible = false;
- this.container.style.cssText = ''; // reset CSS
-
- super._release();
- }
-
- /**
- * Resize the immersive viewport, so that it occupies the entire page.
- * We respect the aspect ratio of the source media
- */
- protected _onResize(): void
- {
- super._onResize();
-
- const container = this.container;
- container.style.position = 'fixed';
-
- if(this.style == 'best-fit') {
- // cover the page while maintaining the aspect ratio
- let viewportWidth = 0, viewportHeight = 0;
- const windowAspectRatio = window.innerWidth / window.innerHeight;
- const viewportAspectRatio = this._realSize.width / this._realSize.height;
-
- if(viewportAspectRatio <= windowAspectRatio) {
- viewportHeight = window.innerHeight;
- viewportWidth = (viewportHeight * viewportAspectRatio) | 0;
- }
- else {
- viewportWidth = window.innerWidth;
- viewportHeight = (viewportWidth / viewportAspectRatio) | 0;
- }
-
- container.style.left = `calc(50% - ${(viewportWidth+1) >>> 1}px)`;
- container.style.top = `calc(50% - ${(viewportHeight+1) >>> 1}px)`;
- container.style.width = viewportWidth + 'px';
- container.style.height = viewportHeight + 'px';
- }
- else if(this.style == 'stretch') {
- // stretch to cover the entire page
- container.style.left = '0px';
- container.style.top = '0px';
- container.style.width = window.innerWidth + 'px';
- container.style.height = window.innerHeight + 'px';
- }
- else
- throw new IllegalOperationError('Invalid immersive viewport style: ' + this.style);
- }
- }
-
- /**
- * Inline viewport: it follows the typical flow of a web page
- */
- export class InlineViewport extends ResizableViewport
- {
- /**
- * Initialize the viewport
- * @internal
- */
- _init(): void
- {
- super._init();
- this.style = 'inline';
- }
-
- /**
- * Release the viewport
- * @internal
- */
- _release(): void
- {
- this.container.style.cssText = ''; // reset CSS
- super._release();
- }
-
- /**
- * Resize the inline viewport
- * (we still take orientation changes into account)
- */
- protected _onResize(): void
- {
- super._onResize();
-
- const container = this.container;
- container.style.position = 'relative';
-
- if(this.style == 'inline') {
- container.style.left = '0px';
- container.style.top = '0px';
- container.style.width = this.virtualSize.width + 'px';
- container.style.height = this.virtualSize.height + 'px';
- }
- else
- throw new IllegalOperationError('Invalid inline viewport style: ' + this.style);
- }
- }
|