Added untested hsla background mapping.
Font color mapping now uses color format the styling currently uses.
This commit is contained in:
parent
4dc34ef2f9
commit
7ac924f934
@ -1,4 +1,4 @@
|
||||
import { parseColor, rgbaToHexRgba } from "../support/colors.js";
|
||||
import { hslaToCssHsla, hslaToRgba, parseColorToHsla, parseColorToRgba, rgbaToCssRgba, rgbaToHexRgba, rgbaToHsla } from "../support/colors.js";
|
||||
import VisUpdateRouter from "../visualization/VisUpdateRouter.js";
|
||||
import { numerical } from "./numeric.js";
|
||||
|
||||
@ -10,7 +10,7 @@ import { numerical } from "./numeric.js";
|
||||
*
|
||||
* @param {object} conf The configuration of the mapping.
|
||||
* @param {HTMLElement} conf.element The element whose text's red value should be mapped.
|
||||
* @param {number} conf.color Where r for red, g, for green, b for blue, and a for alpha.
|
||||
* @param {string} conf.color Where r for red, g, for green, b for blue, and a for alpha.
|
||||
* @param {number} conf.lowerBin The lower bound of the bins to be mapped.
|
||||
* @param {VisUpdateRouter} conf.visUpdateRouter The visualizer update manager associated with the audio playback you would like the mapping with.
|
||||
* @param {Function} conf.interpolator The interpolation function to use.
|
||||
@ -26,12 +26,20 @@ export function backgroundColorRgba({ element, color, lowerBin, visUpdateRouter,
|
||||
if (color < 0) throw new Error("Invalid color parameter provided.");
|
||||
const getter = () => {
|
||||
if (!element.style.backgroundColor) element.style.backgroundColor = "rgba(0, 0, 0, 255)";
|
||||
return parseColor(element.style.backgroundColor)[color];
|
||||
return parseColorToRgba(element.style.backgroundColor)[color];
|
||||
};
|
||||
const setter = (value) => {
|
||||
const changed = parseColor(element.style.backgroundColor);
|
||||
const changed = parseColorToRgba(element.style.backgroundColor);
|
||||
changed[color] = value;
|
||||
element.style.backgroundColor = rgbaToHexRgba(changed);
|
||||
if (element.style.backgroundColor.startsWith("rgb")) {
|
||||
element.style.backgroundColor = rgbaToCssRgba(changed);
|
||||
} else if (element.style.backgroundColor.startsWith("hsl")) {
|
||||
element.style.backgroundColor = hslaToCssHsla(rgbaToHsla(changed));
|
||||
} else if (element.style.backgroundColor.startsWith("#")) {
|
||||
element.style.backgroundColor = rgbaToHexRgba(changed);
|
||||
} else {
|
||||
element.style.backgroundColor = rgbaToHexRgba(changed);
|
||||
}
|
||||
};
|
||||
upperVal = Math.min(Math.max(0, upperVal), 255);
|
||||
lowerVal = Math.min(Math.max(0, lowerVal), upperVal);
|
||||
@ -49,12 +57,71 @@ export function backgroundColorRgba({ element, color, lowerBin, visUpdateRouter,
|
||||
return numerical(conf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a color component of an element background color to a certain range of frequency bins.
|
||||
*
|
||||
* @param {object} conf The configuration of the mapping.
|
||||
* @param {HTMLElement} conf.element The element whose text's red value should be mapped.
|
||||
* @param {number} conf.select Where h for hue, s, for saturation, l for lightness, and a for alpha.
|
||||
* @param {number} conf.lowerBin The lower bound of the bins to be mapped.
|
||||
* @param {VisUpdateRouter} conf.visUpdateRouter The visualizer update manager associated with the audio playback you would like the mapping with.
|
||||
* @param {Function} conf.interpolator The interpolation function to use.
|
||||
* @param {number} [conf.upperBin=undefined] The upper bound of the bins to be mapped. If left undefined, then only the bin defined by the min value is used.
|
||||
* @param {boolean} [conf.reversed=true] If true, then the quieter, the greater the red value.
|
||||
* @param {number} [conf.lowerVal=0] The lower boundary of possible values for the color component (0 to upperVal inclusive).
|
||||
* @param {number} [conf.upperVal=0] The upper boundary of possible values for the color component (0 to 360 if conf.color is "h", 1 if conf.color is "s" or "l", and 255 if conf.color is "a").
|
||||
* @returns {{lower: number, upper: number, listener: VisUpdateRouter~visualizerRangedUpdateListener}} The ranged listener that was added.
|
||||
*/
|
||||
export function backgroundColorHsla({ element, select, lowerBin, visUpdateRouter, interpolator, upperBin = undefined, reversed = false, lowerVal = 0, upperVal = undefined }) {
|
||||
const rgbaStr = "hsla";
|
||||
select = rgbaStr.indexOf(select);
|
||||
if (select < 0) throw new Error("Invalid color parameter provided.");
|
||||
|
||||
const getter = () => {
|
||||
if (!element.style.backgroundColor) element.style.backgroundColor = "hsl(0, 50%, 50%, 255)";
|
||||
return parseColorToHsla(element.style.backgroundColor)[select];
|
||||
};
|
||||
const setter = (value) => {
|
||||
const changed = parseColorToHsla(element.style.backgroundColor);
|
||||
changed[select] = value;
|
||||
if (element.style.backgroundColor.startsWith("hsl")) {
|
||||
element.style.backgroundColor = hslaToCssHsla(changed);
|
||||
} else if (element.style.backgroundColor.startsWith("rgb")) {
|
||||
element.style.backgroundColor = rgbaToCssRgba(hslaToRgba(changed));
|
||||
} else if (element.style.backgroundColor.startsWith("#")) {
|
||||
element.style.backgroundColor = rgbaToHexRgba(hslaToRgba(changed));
|
||||
} else {
|
||||
element.style.backgroundColor = rgbaToHexRgba(hslaToRgba(changed)); // If we don't recognize the currently used color function, then just use hex.
|
||||
}
|
||||
};
|
||||
let upperBound = 360;
|
||||
if (select === "s" || select === "l") {
|
||||
upperBound = 1;
|
||||
} else if (select === "a") {
|
||||
upperBound = 255;
|
||||
}
|
||||
upperVal = Math.min(Math.max(0, upperVal), upperBound);
|
||||
lowerVal = Math.min(Math.max(0, lowerVal), upperVal);
|
||||
const conf = {
|
||||
minVal: lowerVal,
|
||||
maxVal: upperVal,
|
||||
lowerBin: lowerBin,
|
||||
upperBin: upperBin,
|
||||
getter: getter,
|
||||
setter: setter,
|
||||
interpolator: interpolator,
|
||||
visUpdateRouter: visUpdateRouter,
|
||||
reversed: reversed
|
||||
};
|
||||
return numerical(conf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a color component of the text color to a certain range of frequency bins.
|
||||
*
|
||||
* @param {object} conf The configuration of the mapping.
|
||||
* @param {HTMLElement} conf.element The element whose text's red value should be mapped.
|
||||
* @param {number} conf.color Where r for red, g, for green, b for blue, and a for alpha.
|
||||
* @param {string} conf.color Where r for red, g, for green, b for blue, and a for alpha.
|
||||
* @param {number} conf.lowerBin The lower bound of the bins to be mapped.
|
||||
* @param {VisUpdateRouter} conf.visUpdateRouter The visualizer update manager associated with the audio playback you would like the mapping with.
|
||||
* @param {Function} conf.interpolator The interpolation function to use.
|
||||
@ -70,12 +137,20 @@ export function fontColorRgba({ element, color, lowerBin, visUpdateRouter, inter
|
||||
if (color < 0) throw new Error("Invalid color parameter provided.");
|
||||
const getter = () => {
|
||||
if (!element.style.color) element.style.color = "rgba(0, 0, 0, 255)";
|
||||
return parseColor(element.style.color)[color];
|
||||
return parseColorToRgba(element.style.color)[color];
|
||||
};
|
||||
const setter = (value) => {
|
||||
const changed = parseColor(element.style.color);
|
||||
const changed = parseColorToRgba(element.style.color);
|
||||
changed[color] = value;
|
||||
element.style.color = rgbaToHexRgba(changed);
|
||||
if (element.style.color.startsWith("rgb")) {
|
||||
element.style.color = rgbaToCssRgba(changed);
|
||||
} else if (element.style.color.startsWith("hsl")) {
|
||||
element.style.color = hslaToCssHsla(rgbaToHsla(changed));
|
||||
} else if (element.style.color.startsWith("#")) {
|
||||
element.style.color = rgbaToHexRgba(changed);
|
||||
} else {
|
||||
element.style.color = rgbaToHexRgba(changed);
|
||||
}
|
||||
};
|
||||
upperVal = Math.min(Math.max(0, upperVal), 255);
|
||||
lowerVal = Math.min(Math.max(0, lowerVal), upperVal);
|
||||
|
@ -45,7 +45,7 @@ export function rgbHexToRgba(hex) {
|
||||
*/
|
||||
export function cssRgbaToRgba(rgba) {
|
||||
rgba = rgba.replaceAll(" ", "");
|
||||
const cssRgbaRegex = /rgba\((\d+),(\d+),(\d+),(\d+)\)/;
|
||||
const cssRgbaRegex = /rgba\((\d+)[, ] ?(\d+)[, ] ?(\d+)[, ] ?(\d+)\)/;
|
||||
try {
|
||||
const matches = rgba.match(cssRgbaRegex);
|
||||
return [parseInt(matches[1]), parseInt(matches[2]), parseInt(matches[3]), parseInt(matches[4])];
|
||||
@ -61,8 +61,7 @@ export function cssRgbaToRgba(rgba) {
|
||||
* @returns {number[]} the rgba components.
|
||||
*/
|
||||
export function cssRgbToRgba(rgb) {
|
||||
rgb = rgb.replaceAll(" ", "");
|
||||
const cssRgbRegex = /rgb\((\d+),(\d+),(\d+)\)/;
|
||||
const cssRgbRegex = /rgb\((\d+)[, ] ?(\d+)[, ] ?(\d+)\)/;
|
||||
try {
|
||||
const matches = rgb.match(cssRgbRegex);
|
||||
return [parseInt(matches[1]), parseInt(matches[2]), parseInt(matches[3]), 255];
|
||||
@ -84,14 +83,14 @@ export function rgbaToHexRgba(rgba) {
|
||||
|
||||
|
||||
/**
|
||||
* Converts a css rgb, rgba, hex, or rgba hex to a rgba array.
|
||||
* Converts a css rgb, rgba, hex, hsl(a), or rgba hex to a rgba array.
|
||||
*
|
||||
* For hex, we assume there is no alpha channel unless the hex value is not minimized.
|
||||
*
|
||||
* @param {string} color The string that contains the color information.
|
||||
* @returns {number[]} an array that contains the r, g, b and a components.
|
||||
*/
|
||||
export function parseColor(color) {
|
||||
export function parseColorToRgba(color) {
|
||||
if (color.startsWith("rgba(")) {
|
||||
return cssRgbaToRgba(color);
|
||||
} else if (color.startsWith("rgb(")) {
|
||||
@ -102,6 +101,124 @@ export function parseColor(color) {
|
||||
} else {
|
||||
return rgbHexToRgba(color);
|
||||
}
|
||||
} else if (color.startsWith("hsl(")) {
|
||||
return hslaToRgba(cssHslaToHsla(color));
|
||||
}
|
||||
throw new Error("Could not parse to an rgba value.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a css rgb, rgba, hex, hsl(a), or rgba hex to an hsla array.
|
||||
*
|
||||
* @param {string} color The string that contains the color information.
|
||||
* @returns {number[]} An array that contains the h, s, l and alpha channel components.
|
||||
*/
|
||||
export function parseColorToHsla(color) {
|
||||
if (color.startsWith("rgba(")) {
|
||||
return rgbaToHsla(cssRgbaToRgba(color));
|
||||
} else if (color.startsWith("rgb(")) {
|
||||
return rgbaToHsla(cssRgbToRgba(color));
|
||||
} else if (color.startsWith("#")) {
|
||||
if (color.length === 9) {
|
||||
return rgbaToHsla(rgbaHexToRgba(color));
|
||||
} else {
|
||||
return rgbaToHsla(rgbHexToRgba(color));
|
||||
}
|
||||
} else if (color.startsWith("hsl(")) {
|
||||
return cssHslaToHsla(color);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a given rgb value to hsla. Alpha value is simply passed through.
|
||||
*
|
||||
* @param {number[]} rgba An array that contains numbers representing the r, g, b, and a values respectively.
|
||||
* @returns {number[]} An array that contains the h, s, l, and a values.
|
||||
*/
|
||||
export function rgbaToHsla(rgba) { // Used GeeksForGeeks implementation
|
||||
let r = rgba[0] / 255;
|
||||
let g = rgba[1] / 255;
|
||||
let b = rgba[2] / 255;
|
||||
|
||||
const cmax = Math.max(r, g, b);
|
||||
const cmin = Math.min(r, g, b);
|
||||
const delta = cmax - cmin;
|
||||
|
||||
let h = 0;
|
||||
if (delta === 0) {
|
||||
h = 0;
|
||||
} else if (cmax === r) {
|
||||
h = 60 * (((g - b) / delta) % 6);
|
||||
} else if (cmax === g) {
|
||||
h = 60 * (2 + (b - r) / delta);
|
||||
} else if (cmax === b) {
|
||||
h = 60 * (4 + (r - g) / delta);
|
||||
}
|
||||
|
||||
let l = (cmax + cmin) / 2;
|
||||
|
||||
let s = 0;
|
||||
if (delta === 0) {
|
||||
s = 0;
|
||||
} else {
|
||||
s = delta / (1 - Math.abs(2 * l - 1));
|
||||
}
|
||||
|
||||
return [h, s, l, rgba[3]];
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a given hsv value to rgb. The alpha channel value is just passed through.
|
||||
*
|
||||
* @param {number[]} hsla The HSL and alpha component array.
|
||||
* @returns {number[]} An array comprised of r, g, b and a values converted from the given HSL.
|
||||
*/
|
||||
export function hslaToRgba(hsla) {
|
||||
const h = hsla[0];
|
||||
const s = hsla[1];
|
||||
const l = hsla[2];
|
||||
|
||||
const fn = (n) => {
|
||||
const k = ((n + (h / 30)) % 12);
|
||||
const a = s * Math.min(l, 1 - l);
|
||||
return l - a * Math.max(-1, Math.min(k - 3, 9 - k, 1));
|
||||
};
|
||||
|
||||
return [Math.round(fn(0) * 255), Math.round(fn(8) * 255), Math.round(fn(4) * 255), hsla[3]];
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a CSS color property coded in hsl.
|
||||
*
|
||||
* @param {string} cssHsla The style property value in HSL(A).
|
||||
* @returns {number[]} An array that contains the parsed H, S, L and alpha channel.
|
||||
*/
|
||||
export function cssHslaToHsla(cssHsla) {
|
||||
const cssHslaRegex = /hsl\((\d+)[, ] ?(\d+)%[, ] ?(\d+)% ?[, /]? ?(\d+)?\)/;
|
||||
try {
|
||||
const matches = cssHsla.match(cssHslaRegex);
|
||||
return [parseInt(matches[1]), parseInt(matches[2]) / 100, parseInt(matches[3]) / 100, isNaN(matches[4]) ? 255 : parseInt(matches[4])];
|
||||
} catch (error) {
|
||||
throw new Error("Could not parse the given HSL(a) value: " + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an array containing the H, S, L and alpha values to a css hsl function call.
|
||||
*
|
||||
* @param {number[]} hsla An array containing the H, S, L and alpha channel values.
|
||||
* @returns {string} The css hsl function call.
|
||||
*/
|
||||
export function hslaToCssHsla(hsla) {
|
||||
return "hsl(" + hsla[0] + ", " + (hsla[1] * 100) + "%, " + (hsla[2] * 100) + "%, " + hsla[3];
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an array containing the R, G, B and alpha values to a css rgba function call.
|
||||
*
|
||||
* @param {number[]} rgba An array whose elements are the rgba components to encode into a css rgba function call.
|
||||
* @returns {string} The css rgba function call.
|
||||
*/
|
||||
export function rgbaToCssRgba(rgba) {
|
||||
return "rgba(" + Math.round(rgba[0]) + ", " + Math.round(rgba[1]) + ", " + Math.round(rgba[2]) + ", " + Math.round(rgba[3]) + ")";
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
/* eslint-disable no-undef */
|
||||
import { expect } from "chai";
|
||||
import { describe } from "mocha";
|
||||
import { cssRgbaToRgba, cssRgbToRgba, rgbaHexToRgba, parseColor, rgbaToHexRgba, rgbHexToRgba } from "../src/support/colors.js";
|
||||
import { cssRgbaToRgba, cssRgbToRgba, rgbaHexToRgba, parseColorToRgba, rgbaToHexRgba, rgbHexToRgba, rgbaToHsla, hslaToRgba, cssHslaToHsla, hslaToCssHsla } from "../src/support/colors.js";
|
||||
|
||||
describe("Color utilities", function () {
|
||||
describe("the hex to rgba conversion function", function () {
|
||||
@ -83,6 +83,11 @@ describe("Color utilities", function () {
|
||||
const rgba = cssRgbToRgba("rgb(1,1,1)");
|
||||
expect(rgba).to.have.ordered.members([1, 1, 1, 255]);
|
||||
});
|
||||
|
||||
it("returns 1, 1 and 1 for r, g and b given \"rgb(1, 1, 1)\" (contains spaces)", function () {
|
||||
const rgba = cssRgbToRgba("rgb(1, 1, 1)");
|
||||
expect(rgba).to.have.ordered.members([1, 1, 1, 255]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("The function to convert r, g, b, and a represented as an array to hexadecimals", function () {
|
||||
@ -106,22 +111,72 @@ describe("Color utilities", function () {
|
||||
|
||||
describe("The function that automatically converts a string containing an rgb(a) value to an rgba array.", function () {
|
||||
it("returns 1, 1, 1 and 1 for r, g, b and a given \"rgba(1,1,1,1)\"", function () {
|
||||
const rgba = parseColor("rgba(1,1,1,1)");
|
||||
const rgba = parseColorToRgba("rgba(1,1,1,1)");
|
||||
expect(rgba).to.have.ordered.members([1, 1, 1, 1]);
|
||||
});
|
||||
it("returns 0, 1, 0 and 1 for r, g, b and a given \"#00010001\"", function () {
|
||||
const rgb = parseColor("#00010001");
|
||||
const rgb = parseColorToRgba("#00010001");
|
||||
expect(rgb).to.have.ordered.members([0, 1, 0, 1]);
|
||||
});
|
||||
it("returns 1, 0, 1 and 255 for r, g, b and a given \"#10001\"", function () {
|
||||
const rgb = parseColor("#10001");
|
||||
const rgb = parseColorToRgba("#10001");
|
||||
expect(rgb).to.have.ordered.members([1, 0, 1, 255]);
|
||||
});
|
||||
it("returns 1, 1, 1 and 255 for r, g, b and a given \"rgb(1,1,1)\"", function () {
|
||||
const rgba = parseColor("rgb(1,1,1)");
|
||||
const rgba = parseColorToRgba("rgb(1,1,1)");
|
||||
expect(rgba).to.have.ordered.members([1, 1, 1, 255]);
|
||||
});
|
||||
it("returns 1, 1, 1 and 1 for r, g, b and a given \"rgba(1, 1, 1, 1)\" (contains spaces)", function () {
|
||||
const rgba = parseColorToRgba("rgba(1, 1, 1, 1)");
|
||||
expect(rgba).to.have.ordered.members([1, 1, 1, 1]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("The function that converts a rgba array to hsl and a values.", function () {
|
||||
it("returns (approx.) 193, 100% and 50% for H, S, and V components given [0, 200, 255, 255]", function () {
|
||||
const [h, s, v, a] = rgbaToHsla([0, 200, 255, 255]);
|
||||
expect(h).to.be.closeTo(193, 0.1);
|
||||
expect(s).to.equal(1);
|
||||
expect(v).to.equal(0.5);
|
||||
expect(a).to.equal(255);
|
||||
});
|
||||
it("returns (approx.) 120, 100% and 70% for H, S, and V components given [102, 255, 102, 255]", function () {
|
||||
const [h, s, v, a] = rgbaToHsla([102, 255, 102, 255]);
|
||||
expect(h).to.be.closeTo(120, 0.1);
|
||||
expect(s).to.be.closeTo(1, 0.0001);
|
||||
expect(v).to.equal(0.7);
|
||||
expect(a).to.equal(255);
|
||||
});
|
||||
});
|
||||
|
||||
describe("The function that converts a hsva array to rgba values.", function () {
|
||||
it("Returns (approx.) 0, 200, 255 and 255 for r, g, b and a given 193, 100%, 50% and 255.", function () {
|
||||
const [r, g, b, a] = hslaToRgba([193, 1, 0.5, 255]);
|
||||
expect(r).to.be.closeTo(0, 1);
|
||||
expect(g).to.be.closeTo(200, 1);
|
||||
expect(b).to.be.closeTo(255, 1);
|
||||
expect(a).to.equal(255);
|
||||
});
|
||||
it("Returns 61, 71, 77 and 255 for r, g, b and a given 200, 20%, 30% and 255.", function () {
|
||||
const [r, g, b, a] = hslaToRgba([203, 0.116, 0.271, 255]);
|
||||
expect(r).to.be.closeTo(61, 0.2);
|
||||
expect(g).to.be.closeTo(71, 0.2);
|
||||
expect(b).to.be.closeTo(77, 0.2);
|
||||
expect(a).to.equal(255);
|
||||
});
|
||||
});
|
||||
|
||||
describe("The function that converts an css hsl function call to an array containing the individual h, s, l and a components.", function () {
|
||||
it("Returns [1, 0.01, 0.01, 1] given \"hsl(1, 1%, 1%, 1)\"", function () {
|
||||
const hsla = cssHslaToHsla("hsl(1, 1%, 1%, 1)");
|
||||
expect(hsla).to.have.ordered.members([1, 0.01, 0.01, 1]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("The function that converts an array representing hsla values to a css hsl function call.", function () {
|
||||
it("Returns \"hsl(1, 1%, 1%, 1)\" given [1, 0.01, 0.01, 1].", function () {
|
||||
const cssHsla = hslaToCssHsla([1, 0.01, 0.01, 1]);
|
||||
expect(cssHsla).to.equal("hsl(1, 1%, 1%, 1");
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user