Source: map/tools/ScreenshotButton.js

import { UiElement } from '../../ui/UiElement.js';
import { saveAsFile } from '../../utils/utils.js';
import { URLS } from '../../defaults/URLS.js';

export { ScreenshotButton };

/**
 * Represents a simple button to save the current map as an image.
 * 
 * @memberof vef.map.tools
 */
class ScreenshotButton extends UiElement {

    /**
     * Constructs a new ScreenshotButton instance.
     * 
     * @param {Map} map - The map object.
     * @param {string} [position="top-left"] - The position of the button on the map.
     */
    constructor(map, position) {
        super();

        this.map = map;
        this.position = position || "top-left";
        this.initButton_();

        this.map.addTool(this, this.position);
    }

    /**
     * @private
     * @returns {Promise} A promise that resolves when the brand logo is added to the map.
     */
    addBrandLogo_() {
        return new Promise((resolve) => {
            if (!URLS.BRAND) reject();

            const logo = document.createElement("img");
            logo.src = URLS.BRAND;

            const complete = () => {
                logo.style.width = "200px";
                logo.style.position = "absolute";
                logo.style.bottom = "16px";
                logo.style.right = "16px";
                logo.style.zIndex = "5000";
                this.map.mapContainer_.appendChild(logo);
                resolve(logo);
            };

            if (logo.complete) {
                complete();
            } else {
                logo.onload = () => complete();
                logo.onerror = () => reject();
            }
        });
    }

    /**
     * Initializes the button element.
     * @private
     */
    initButton_() {
        // initialize element
        const element = this.getElement();
        element.classList.add("vef-tool");
        element.title = "Save current map as image";

        const icon = document.createElement("i");
        icon.className = "fas fa-camera";
        element.appendChild(icon);

        let processing = false;
        element.addEventListener("click", () => {
            if (processing) return;
            processing = true;

            // start spinning
            icon.className = "fas fa-spinner fa-spin fa-fw";

            import("html2canvas").then(html2canvas => {
                let logo = null;
                this.addBrandLogo_().then(img => {
                    logo = img;
                }).finally(() => {
                    html2canvas.default(this.map.mapContainer_, {
                        allowTaint: true,
                        useCORS: true
                    }).then(canvas => {
                        canvas.toBlob(blob => {
                            saveAsFile(blob, "viewer.png");
                        }, 'image/png');
                    }).finally(() => {
                        // end spinning
                        processing = false;
                        icon.className = "fas fa-camera";
                        if (logo) logo.remove();
                    });
                })
            });
        });
    }
}