Shows a number in the favicon like Discord and Gmail do.

PHOTO EMBED

Thu Dec 16 2021 22:56:50 GMT+0000 (Coordinated Universal Time)

Saved by @Explosion #javascript #canvas

/**
 * Generates a data URL of the current favicon with a number added on top.
 * @param {Object} options
 * @param {String} [options.type = "image/png"] The mime type of the image to return.
 * @param {String} [options.text = ""] The text to display on the favicon, if left blank will simply show a dot on the favicon.
 * @param {String} [options.background = "white"] A CSS color for the background of the notification badge.
 * @param {String} [options.color = "white"] A CSS color for the color of the text on the notification badge.
 * @param {Number} [options.size = 10] The size of the notification badge. The badge generated will be size * 2 pixels in width and height, then added on top of the current favicon.
 * @param {String} [options.pos = "bottom-right"] The position of the badge, either "bottom-right", "top-right", "bottom-left" or "top-left"
 * @param {String} [options.font = "Monospace"] The font to use
 * @param {String} [options.iconUrl] The URL of the base icon, if not provided will be the current favicon.
 * @returns {Promise.<string>} Returns a promise that resolves into the data URL of the icon generated.
 * @example
 *   getIcon({
 *     text: "1",
 *     pos: "top-right",
 *   }).then((data_url) => {
 *     document.querySelector("link[rel='icon']").href = data_url;
 *   });
 */
async function getIcon({
    type = "image/png",
    text = "",
    background = "white",
    color = "black",
    size = 10,
    pos = "bottom-right",
    font = "Monospace",
    iconUrl
}) {
    const icon = iconUrl || document.querySelector("link[rel='icon']")?.href;
    let data = await getData(icon);
    let canvas = document.createElement("canvas");
    let ctx = canvas.getContext("2d");
    let img = await loadedImg(data);
    let notif_img = await loadedImg(getNotifData(text, size));
    canvas.width = img.width;
    canvas.height = img.height;
    ctx.drawImage(img, 0, 0);
    let x = 0;
    let y = 0;
    let sections = pos.trim().toLowerCase().split("-");
    sections[0] === "bottom" && (y = canvas.height - size * 2);
    sections[1] == "right" && (x = canvas.width - size * 2);
    ctx.drawImage(notif_img, x, y);
    return canvas.toDataURL(type);

    function loadedImg(src) {
        return new Promise((res) => {
            let img = new Image();
            img.src = src;
            img.onload = () => res(img);
        });
    }

    function getNotifData(text, size) {
        const _canvas = document.createElement("canvas");
        _canvas.width = size * 2;
        _canvas.height = size * 2;
        let c = _canvas.getContext("2d");
        const inset = size;
        c.beginPath();
        c.fillStyle = background;
        c.arc(inset, inset, size, 0, 2 * Math.PI);
        c.fill();
        c.font = `${size * 1.5}px ${font}`;
        c.fillStyle = color;
        c.textBaseline = "top";
        c.fillText(text, inset / 2, inset / 2);
        return _canvas.toDataURL("image/png");
    }

    function getData(url) {
        return new Promise(async (res, reject) => {
            let blob = await fetch(url).then((r) => r.blob());
            let dataUrl = await new Promise((resolve) => {
                let reader = new FileReader();
                reader.onload = () => resolve(reader.result);
                reader.readAsDataURL(blob);
            });
            res(dataUrl);
        });
    }
}
content_copyCOPY

https://gist.github.com/Explosion-Scratch/4c787653703cc9b3d418f19fe621d38d