Last active
May 6, 2023 04:47
-
-
Save Explosion-Scratch/4c787653703cc9b3d418f19fe621d38d to your computer and use it in GitHub Desktop.
Shows a number in the favicon like Discord and Gmail do.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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); | |
}); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment