mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-09 10:34:17 +00:00
249 lines
7.4 KiB
JavaScript
249 lines
7.4 KiB
JavaScript
import store from 'store';
|
|
import { app } from 'config';
|
|
import verbosity from 'core/libs/verbosity'
|
|
import ErrorHandler from 'core/libs/errorhandler'
|
|
|
|
const { appTheme_desiredContrast, storage_theme } = app
|
|
|
|
export const updateRootStyles = (styles) => {
|
|
const rootContainer = document.getElementById(runtime.mountElementId ?? "root")
|
|
if (rootContainer) {
|
|
if (typeof (styles) !== "undefined" && Array.isArray(styles)) {
|
|
try {
|
|
__legacy__objectToArray(styles).forEach((e) => {
|
|
rootContainer.style[e.key] = e.value
|
|
})
|
|
} catch (error) {
|
|
verbosity([error])
|
|
return false
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
|
|
export const appendStyles = (style) => {
|
|
|
|
}
|
|
|
|
export const theme = {
|
|
get: (key) => {
|
|
const raw = store.get(storage_theme)
|
|
if (!raw) return false
|
|
let container = []
|
|
try {
|
|
raw.forEach((e) => { container[e.key] = e.value })
|
|
} catch (error) {
|
|
return ErrorHandler({ msg: error, code: 120 })
|
|
}
|
|
return container
|
|
},
|
|
set: (data) => {
|
|
if (!data || data.length > 2) return false
|
|
try {
|
|
let mix = []
|
|
const obj = Object.entries(data)
|
|
obj.forEach((e) => {
|
|
mix.push({ key: e[0], value: e[1] })
|
|
})
|
|
return store.set(storage_theme, mix)
|
|
} catch (error) {
|
|
console.log(error)
|
|
return false
|
|
}
|
|
},
|
|
raw: () => {
|
|
return store.get(storage_theme)
|
|
}
|
|
}
|
|
|
|
export function get_style_rule_value(selector, style) {
|
|
const selector_lowercase = selector.toLowerCase();
|
|
const selector_parsed = selector_lowercase.substr(0, 1) === '.' ? selector_lowercase.substr(1) : '.' + selector_lowercase;
|
|
|
|
for (let i = 0; i < document.styleSheets.length; i++) {
|
|
let styleSheet = document.styleSheets[i];
|
|
let rules = styleSheet.cssRules ? styleSheet.cssRules : styleSheet.rules;
|
|
|
|
for (var j = 0; j < rules.length; j++) {
|
|
if (rules[j].selectorText) {
|
|
var check = rules[j].selectorText.toLowerCase();
|
|
switch (check) {
|
|
case selector_lowercase:
|
|
case selector_parsed: return rules[j].style[style];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
export function getOptimalOpacityFromIMG(payload, callback) {
|
|
const { textColor, overlayColor, img } = payload;
|
|
|
|
verbosity([payload])
|
|
let canvas = document.createElement('canvas');
|
|
let image = new Image();
|
|
|
|
image.src = img
|
|
image.setAttribute('crossOrigin', '');
|
|
image.onload = () => {
|
|
const imagePixelColors = getImagePixelColorsUsingCanvas(canvas, image);
|
|
if (imagePixelColors) {
|
|
const worstContrastColorInImage = getWorstContrastColorInImage(textColor, imagePixelColors);
|
|
const optimalOpacity = findOptimalOverlayOpacity(textColor, overlayColor, worstContrastColorInImage, appTheme_desiredContrast);
|
|
return callback(optimalOpacity)
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
export function getImagePixelColorsUsingCanvas(canvas, image) {
|
|
let imagePixelColors = null;
|
|
const ctx = canvas.getContext('2d');
|
|
|
|
canvas.height = getCanvasHeightToMatchImageProportions(canvas, image);
|
|
|
|
const sourceImageCoordinates = [0, 0, image.width, image.height];
|
|
const destinationCanvasCoordinates = [0, 0, canvas.width, canvas.height];
|
|
|
|
ctx.drawImage(
|
|
image,
|
|
...sourceImageCoordinates,
|
|
...destinationCanvasCoordinates
|
|
);
|
|
|
|
// Remember getImageData only works for same-origin or cross-origin-enabled images.
|
|
// See https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image for more info.
|
|
try {
|
|
imagePixelColors = ctx.getImageData(...destinationCanvasCoordinates);
|
|
} catch (error) {
|
|
return ErrorHandler({ msg: error, code: 120 })
|
|
}
|
|
|
|
if (imagePixelColors) {
|
|
return imagePixelColors
|
|
}
|
|
}
|
|
|
|
export function getCanvasHeightToMatchImageProportions(canvas, image) {
|
|
return (image.height / image.width) * canvas.width;
|
|
}
|
|
|
|
export function getWorstContrastColorInImage(textColor, imagePixelColors) {
|
|
let worstContrastColorInImage;
|
|
let worstContrast = Infinity;
|
|
|
|
for (let i = 0; i < imagePixelColors.data.length; i += 4) {
|
|
let pixelColor = {
|
|
r: imagePixelColors.data[i],
|
|
g: imagePixelColors.data[i + 1],
|
|
b: imagePixelColors.data[i + 2],
|
|
};
|
|
|
|
let contrast = getContrast(textColor, pixelColor);
|
|
if (contrast < worstContrast) {
|
|
worstContrast = contrast;
|
|
worstContrastColorInImage = pixelColor;
|
|
}
|
|
}
|
|
return worstContrastColorInImage;
|
|
}
|
|
|
|
export function getContrast(color1, color2) {
|
|
const color1_luminance = getLuminance(color1);
|
|
const color2_luminance = getLuminance(color2);
|
|
|
|
const lighterColorLuminance = Math.max(color1_luminance, color2_luminance);
|
|
const darkerColorLuminance = Math.min(color1_luminance, color2_luminance);
|
|
|
|
const contrast = (lighterColorLuminance + 0.05) / (darkerColorLuminance + 0.05);
|
|
return contrast;
|
|
}
|
|
|
|
export function getLuminance({ r, g, b }) {
|
|
return (0.2126 * getLinearRGB(r) + 0.7152 * getLinearRGB(g) + 0.0722 * getLinearRGB(b));
|
|
}
|
|
|
|
export function getLinearRGB(primaryColor_8bit) {
|
|
// First convert from 8-bit rbg (0-255) to standard RGB (0-1)
|
|
const primaryColor_sRGB = convert_8bit_RGB_to_standard_RGB(primaryColor_8bit);
|
|
|
|
// Then convert from sRGB to linear RGB so we can use it to calculate luminance
|
|
const primaryColor_RGB_linear = convert_standard_RGB_to_linear_RGB(primaryColor_sRGB);
|
|
|
|
return primaryColor_RGB_linear;
|
|
}
|
|
|
|
export function convert_8bit_RGB_to_standard_RGB(primaryColor_8bit) {
|
|
return primaryColor_8bit / 255;
|
|
}
|
|
|
|
export function convert_standard_RGB_to_linear_RGB(primaryColor_sRGB) {
|
|
const primaryColor_linear = primaryColor_sRGB < 0.03928 ?
|
|
primaryColor_sRGB / 12.92 :
|
|
Math.pow((primaryColor_sRGB + 0.055) / 1.055, 2.4);
|
|
return primaryColor_linear;
|
|
}
|
|
|
|
export function getTextContrastWithImagePlusOverlay({ textColor, overlayColor, imagePixelColor, overlayOpacity }) {
|
|
const colorOfImagePixelPlusOverlay = mixColors(imagePixelColor, overlayColor, overlayOpacity);
|
|
const contrast = getContrast(textColor, colorOfImagePixelPlusOverlay);
|
|
return contrast;
|
|
}
|
|
|
|
export function mixColors(baseColor, overlayColor, overlayOpacity) {
|
|
const mixedColor = {
|
|
r: baseColor.r + (overlayColor.r - baseColor.r) * overlayOpacity,
|
|
g: baseColor.g + (overlayColor.g - baseColor.g) * overlayOpacity,
|
|
b: baseColor.b + (overlayColor.b - baseColor.b) * overlayOpacity,
|
|
}
|
|
return mixedColor;
|
|
}
|
|
|
|
export function findOptimalOverlayOpacity(textColor, overlayColor, worstContrastColorInImage, appTheme_desiredContrast) {
|
|
const opacityGuessRange = {
|
|
lowerBound: 0,
|
|
midpoint: 0.5,
|
|
upperBound: 1,
|
|
};
|
|
|
|
let numberOfGuesses = 0;
|
|
const maxGuesses = 8;
|
|
const opacityLimit = 0.99;
|
|
|
|
while (numberOfGuesses < maxGuesses) {
|
|
numberOfGuesses++;
|
|
const currentGuess = opacityGuessRange.midpoint;
|
|
|
|
const contrastOfGuess = getTextContrastWithImagePlusOverlay({
|
|
textColor,
|
|
overlayColor,
|
|
imagePixelColor: worstContrastColorInImage,
|
|
overlayOpacity: currentGuess,
|
|
});
|
|
|
|
const isGuessTooLow = contrastOfGuess < appTheme_desiredContrast;
|
|
const isGuessTooHigh = contrastOfGuess > appTheme_desiredContrast;
|
|
|
|
if (isGuessTooLow) {
|
|
opacityGuessRange.lowerBound = currentGuess;
|
|
}
|
|
else if (isGuessTooHigh) {
|
|
opacityGuessRange.upperBound = currentGuess;
|
|
}
|
|
|
|
const newMidpoint = ((opacityGuessRange.upperBound - opacityGuessRange.lowerBound) / 2) + opacityGuessRange.lowerBound;
|
|
opacityGuessRange.midpoint = newMidpoint;
|
|
}
|
|
|
|
const optimalOpacity = opacityGuessRange.midpoint;
|
|
|
|
if (optimalOpacity > opacityLimit) {
|
|
return opacityLimit;
|
|
}
|
|
|
|
return optimalOpacity;
|
|
}
|
|
|