ImageCanvas.ts

/**
 * Usefull class for working with JS canvas object.
 * @class
 * @param {number} [width] - Canvas width. Default 256.
 * @param {number} [height] - Canvas height. Default 256.
 */
class ImageCanvas {

    /**
     * Canvas object.
     * @protected
     * @type {HTMLCanvasElement}
     */
    protected _canvas: HTMLCanvasElement;

    /**
     * Canvas context.
     * @protected
     * @type {CanvasRenderingContext2D}
     */
    protected _context: CanvasRenderingContext2D;

    constructor(width: number = 256, height: number = 256) {
        this._canvas = document.createElement("canvas");
        this._canvas.width = width;
        this._canvas.height = height;

        this._context = this._canvas.getContext("2d", {
            willReadFrequently: true
        })!;
    }

    /**
     * Returns canvas object.
     * @public
     * @returns {HTMLCanvasElement}
     */
    public getCanvas(): HTMLCanvasElement {
        return this._canvas;
    }

    /**
     * Returns canvas context pointer.
     * @public
     * @returns {CanvasRenderingContext2D}
     */
    public getContext(): CanvasRenderingContext2D {
        return this._context;
    }

    /**
     * Fills canvas RGBA with zeroes.
     * @public
     */
    public fillEmpty() {
        let imgd = this._context.getImageData(0, 0, this._canvas.width, this._canvas.height);
        let pixels = imgd.data;
        for (let i = 0, n = pixels.length; i < n; i += 4) {
            pixels[i] = pixels[i + 1] = pixels[i + 2] = pixels[i + 3] = 0;
        }
        this._context.putImageData(imgd, 0, 0);
    }

    /**
     * Fills canvas RGBA with color.
     * @public
     * @param {string} color - CSS string color.
     */
    public fill(color: string) {
        this._context.fillStyle = color
        this._context.fill()
    }

    /**
     * Gets canvas pixels RGBA data.
     * @public
     * @returns {Uint8ClampedArray}
     */
    public getData(): Uint8ClampedArray {
        let imgd = this._context.getImageData(0, 0, this._canvas.width, this._canvas.height);
        return imgd.data;
    }

    /**
     * Fill the canvas by color.
     * @public
     * @param {string} color - CSS string color.
     */
    public fillColor(color: string) {
        this._context.fillStyle = color;
        this._context.fillRect(0, 0, this._canvas.width, this._canvas.height);
    }

    /**
     * Sets RGBA pixel data.
     * @public
     * @param {Array.<number>} data - Array RGBA data.
     */
    public setData(data: ArrayLike<number>) {
        let imageData = this._context.createImageData(this._canvas.width, this._canvas.height);
        imageData.data.set(data);
        this._context.putImageData(imageData, 0, 0);
    }

    /**
     * Resize canvas.
     * @public
     * @param {number} width - Width.
     * @param {number} height - Height.
     */
    public resize(width: number, height: number) {
        this._canvas.width = width;
        this._canvas.height = height;
        this._context = this._canvas.getContext("2d") as CanvasRenderingContext2D;
    }

    /**
     * Draw an image on the canvas.
     * @public
     * @param {Image} img - Draw image.
     * @param {number} [x] - Left top image corner X coordinate on the canvas.
     * @param {number} [y] - Left top image corner Y coordinate on the canvas.
     * @param {number} [width] - Image width slice. Image width is default.
     * @param {number} [height] - Image height slice. Image height is default.
     */
    public drawImage(img: HTMLImageElement, x: number, y: number, width: number, height: number) {
        this._context.drawImage(img, x || 0, y || 0, width || img.width, height || img.height);
    }

    /**
     * Converts canvas to JS image object.
     * @public
     * @returns {Image}
     */
    public getImage(): HTMLImageElement {
        let img = new Image();
        img.width = this.getWidth();
        img.height = this.getHeight();
        img.src = this._canvas.toDataURL("image/png");
        return img;
    }

    /**
     * Get measured text width.
     * @public
     * @param {string} text - Measured text.
     * @returns {number}
     */
    public getTextWidth(text: string): number {
        let metrics = this._context.measureText(text);
        return Math.round(metrics.width);
    }

    /**
     * Draw a text on the canvas.
     * @public
     * @param {string} text - Text.
     * @param {number} [x] - Canvas X - coordinate. 0 - default.
     * @param {number} [y] - Canvas Y - coordinate. 0 - default.
     * @param {string} [font] - Font style. 'normal 14px Verdana' - is default.
     * @param {string} [color] - Css font color.
     */
    public drawText(text: string, x: number = 0, y: number = 14, font: string = "normal 14px Verdana", color: string = "black") {
        this._context.fillStyle = color;
        this._context.font = font;
        this._context.fillText(text, x, y);
    }

    /**
     * Gets canvas width.
     * @public
     * @returns {number}
     */
    public getWidth(): number {
        return this._canvas.width;
    }

    /**
     * Gets canvas height.
     * @public
     * @returns {number}
     */
    public getHeight(): number {
        return this._canvas.height;
    }

    /**
     * Load image to canvas.
     * @public
     * @param {string} url - Image url.
     * @param {Function} [callback] - Image onload callback.
     */
    public load(url: string, callback: Function) {
        let img = new Image();
        let that = this;
        img.onload = function () {
            that.resize(img.width, img.height);
            that._context.drawImage(img, 0, 0, img.width, img.height);
            callback && callback(img);
        };
        img.src = url;
    }

    /**
     * Open canvas image in the new window.
     * @public
     */
    public openImage() {
        let img = this.getImage();
        let dataUrl = img.src;

        let windowContent = "<!DOCTYPE html>";
        windowContent += "<html>";
        windowContent += "<head><title>Print</title></head>";
        windowContent += "<body>";
        windowContent += '<img src="' + dataUrl + '">';
        windowContent += "</body>";
        windowContent += "</html>";

        let printWin = window.open(
            "",
            "",
            "width=" + img.width + "px ,height=" + img.height + "px"
        );

        if (printWin) {
            printWin.document.open();
            printWin.document.write(windowContent);
            printWin.document.close();
            printWin.focus();
        }
    }

    public destroy() {
        this._canvas.width = 1;
        this._canvas.height = 1;
        //@ts-ignore
        this._canvas = null;
        //@ts-ignore
        this._context = null;
    }
}

export {ImageCanvas};