Shows a number in the favicon like Discord and Gmail do.
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); }); } }
Comments