Source: packages/core/src/view/ViewSystem.ts

import { Rectangle } from '@pixi/math';
import { settings } from '@pixi/settings';
import type { ExtensionMetadata } from '@pixi/extensions';
import { extensions, ExtensionType } from '@pixi/extensions';
import type { IRenderer } from '../IRenderer';
import type { ISystem } from '../system/ISystem';

/**
 * Options passed to the ViewSystem
 * @memberof PIXI
 */
export interface ViewOptions
{
    /** The width of the screen. */
    width: number
    /** The height of the screen. */
    height: number
    /** The canvas to use as a view, optional. */
    view?: HTMLCanvasElement;
    /** Resizes renderer view in CSS pixels to allow for resolutions other than 1. */
    autoDensity?: boolean
    /** The resolution / device pixel ratio of the renderer. */
    resolution?: number
}

/**
 * The view system manages the main canvas that is attached to the DOM.
 * This main role is to deal with how the holding the view reference and dealing with how it is resized.
 * @memberof PIXI
 */
export class ViewSystem implements ISystem
{
    /** @ignore */
    static extension: ExtensionMetadata = {
        type: [
            ExtensionType.RendererSystem,
            ExtensionType.CanvasRendererSystem
        ],
        name: '_view',
    };

    private renderer: IRenderer;

    /**
     * The resolution / device pixel ratio of the renderer.
     * @member {number}
     * @default PIXI.settings.RESOLUTION
     */
    public resolution: number;

    /**
     * Measurements of the screen. (0, 0, screenWidth, screenHeight).
     *
     * Its safe to use as filterArea or hitArea for the whole stage.
     * @member {PIXI.Rectangle}
     */
    public screen: Rectangle;

    /**
     * The canvas element that everything is drawn to.
     * @member {HTMLCanvasElement}
     */
    public element: HTMLCanvasElement;

    /**
     * Whether CSS dimensions of canvas view should be resized to screen dimensions automatically.
     * @member {boolean}
     */
    public autoDensity: boolean;

    constructor(renderer: IRenderer)
    {
        this.renderer = renderer;
    }

    /**
     * initiates the view system
     * @param {PIXI.ViewOptions} options - the options for the view
     */
    init(options: ViewOptions): void
    {
        this.screen = new Rectangle(0, 0, options.width, options.height);

        this.element = options.view || settings.ADAPTER.createCanvas();

        this.resolution = options.resolution || settings.RESOLUTION;

        this.autoDensity = !!options.autoDensity;
    }

    /**
     * Resizes the screen and canvas to the specified dimensions.
     * @param desiredScreenWidth - The new width of the screen.
     * @param desiredScreenHeight - The new height of the screen.
     */
    resizeView(desiredScreenWidth: number, desiredScreenHeight: number): void
    {
        this.element.width = Math.round(desiredScreenWidth * this.resolution);
        this.element.height = Math.round(desiredScreenHeight * this.resolution);

        const screenWidth = this.element.width / this.resolution;
        const screenHeight = this.element.height / this.resolution;

        this.screen.width = screenWidth;
        this.screen.height = screenHeight;

        if (this.autoDensity)
        {
            this.element.style.width = `${screenWidth}px`;
            this.element.style.height = `${screenHeight}px`;
        }

        /**
         * Fired after view has been resized.
         * @event PIXI.Renderer#resize
         * @param {number} screenWidth - The new width of the screen.
         * @param {number} screenHeight - The new height of the screen.
         */
        this.renderer.emit('resize', screenWidth, screenHeight);
        this.renderer.runners.resize.emit(this.screen.width, this.screen.height);
    }

    /**
     * Destroys this System and optionally removes the canvas from the dom.
     * @param {boolean} [removeView=false] - Whether to remove the canvas from the DOM.
     */
    destroy(removeView: boolean): void
    {
        // ka boom!
        if (removeView && this.element.parentNode)
        {
            this.element.parentNode.removeChild(this.element);
        }

        this.renderer = null;
        this.element = null;
        this.screen = null;
    }
}

extensions.add(ViewSystem);