105 lines
2.6 KiB
JavaScript
105 lines
2.6 KiB
JavaScript
const Mode = {
|
|
Light: 1,
|
|
Dark: 2,
|
|
};
|
|
|
|
/**
|
|
* Change the svg color theme based on the mode
|
|
* @param {Mode} mode
|
|
*/
|
|
const svgChangeTheme = (mode) => {
|
|
for (const item of document.getElementsByTagName("img")) {
|
|
if (!item.src.startsWith("data:image/svg+xml;base64,")) {
|
|
// Exclude image who aren't SVG and base64 encoded
|
|
continue;
|
|
}
|
|
|
|
/**
|
|
* Convert to grayscale
|
|
* @param {{r: number, g: number, b: number}} color
|
|
* @returns Number between 0 and 255
|
|
*/
|
|
const colorToGrayscale = (color) => {
|
|
return 0.3 * color.r + 0.59 * color.g + 0.11 * color.b;
|
|
};
|
|
|
|
/**
|
|
* Extract color using canvas2d
|
|
* @param {HTMLImageElement} image Image source
|
|
* @returns Colors represeting the image
|
|
*/
|
|
const extractColors = (image) => {
|
|
const canvas = document.createElement("canvas");
|
|
canvas.width = image.naturalWidth;
|
|
canvas.height = image.naturalHeight;
|
|
const ctx = canvas.getContext("2d");
|
|
ctx.drawImage(image, 0, 0);
|
|
|
|
const imageData = ctx.getImageData(
|
|
0,
|
|
0,
|
|
Math.max(1, canvas.width),
|
|
Math.max(1, canvas.height)
|
|
);
|
|
const pixelData = imageData.data;
|
|
|
|
const colors = [];
|
|
for (let i = 0; i < pixelData.length; i += 4) {
|
|
if (pixelData[i + 3] > 0) {
|
|
colors.push({
|
|
r: pixelData[i],
|
|
g: pixelData[i + 1],
|
|
b: pixelData[i + 2],
|
|
});
|
|
}
|
|
}
|
|
|
|
return colors;
|
|
};
|
|
|
|
// Extract colors
|
|
const colors = extractColors(item);
|
|
|
|
// Calculate the average grayscale value
|
|
const grayscaleValues = colors.map(colorToGrayscale);
|
|
const totalGrayscale = grayscaleValues.reduce((acc, val) => acc + val, 0);
|
|
const averageGrayscale = totalGrayscale / grayscaleValues.length;
|
|
|
|
const treshold = 128;
|
|
|
|
const style = "filter: ";
|
|
const dim = "brightness(0.8) contrast(1.2)";
|
|
|
|
if (averageGrayscale < treshold && mode === Mode.Dark) {
|
|
item.style = style + dim + " invert(1);";
|
|
continue;
|
|
}
|
|
|
|
if (averageGrayscale > treshold && mode === Mode.Light) {
|
|
item.style = style + "invert(1);";
|
|
continue;
|
|
}
|
|
|
|
if (mode === Mode.Dark) {
|
|
item.style = style + `${dim};`;
|
|
continue;
|
|
}
|
|
|
|
item.style = "";
|
|
}
|
|
};
|
|
|
|
window.addEventListener("load", () => {
|
|
// Fix SVG images
|
|
svgChangeTheme(
|
|
window.matchMedia("(prefers-color-scheme: dark)").matches
|
|
? Mode.Dark
|
|
: Mode.Light
|
|
);
|
|
});
|
|
|
|
window
|
|
.matchMedia("(prefers-color-scheme: dark)")
|
|
.addEventListener("change", (event) =>
|
|
svgChangeTheme(event.matches ? Mode.Dark : Mode.Light)
|
|
);
|