1

I have a functionallity which allow my users to choose a theme color via range selector. The code is a mix of an answer to a previous question and my own. This is working great but it displays above the selector a text tag showing this enter image description here, the value in degrees changes as the selector changes position. It's fine for me as a developer, but for the users it's a nonesense value, so I did comment that in the code. But I was thinking, why not to show the color names instead because it will be useful to users. So I started on my efforts but got stuck (once more).

This is my working code:

    // Theme Color Chooser

    //const currentThemec = localStorage.getItem('themeColor');
    const value = document.getElementById('value');
    const body = document.getElementById('usrhue');

    /*if (currentThemec) {
    const themeColor = currentThemec;
    body.style.filter = currentThemec;
    value.textContent = currentThemec;
    }*/

    document.getElementById('hue-rotate').oninput = function() {
    const filter = 'hue-rotate(xdeg)'.replace('x', this.value);
    value.textContent = filter;
    body.style.filter = filter;

    //localStorage.setItem( 'themeColor', body.style.filter );
    }
body {
  color: #ff0000;
  background: #f2f2f2;
  padding: 20px;
}
a {
  color: #0033cc;
}
p {
}
h1 {
  color: #0033cc;
}
svg {
  fill: #0000cc;
}
<!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="utf-8">
    </head>
    <body id="usrhue">

    <h3>Theme Color</h1>
    <pre><h3><code id="value">hue-rotate(0deg)</code></h3></pre>
    <input id="hue-rotate" step="45" type="range" min="0" max="315" value="0">
    <div>
    <div class="tile">navbar</div>
    <svg class="icon" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" x="0" y="0" width="50" height="33" viewbox="0 0 50 33"><path style="fill-opacity:1;fill-rule:evenodd;stroke:none" d="m 50,16.111089 c -0.0023,-0.52719 -0.269301,-1.175411 -0.836045,-1.837787 L 33.538651,0 l 0,4.099986 10.560199,12.011103 -10.560199,12.011102 0,4.099986 15.625304,-14.273302 C 49.730699,17.286499 49.997802,16.638278 50,16.111089 Z m -50,0 c 0.0022,-0.52719 0.2693022,-1.175411 0.8360452,-1.837787 L 16.46135,0 l 0,4.099986 -10.5601995,12.011103 10.5601995,12.011102 0,4.099986 L 0.8360452,17.948875 C 0.2693022,17.286499 0.0021979,16.638278 0,16.111089 Z"/></svg>
    </div>
  </body>
    </html>

What I need is to replace this enter image description here with this enter image description here using the colors i listed above. This colors are the nav bar colors of my app on each hue-rotate position, so, it is fixed. They will always be that colors asociated to those degrees and to those color names.

I have tried to fix my code after @ЖнецЪ answer and it is working fine. I will leave it here for future seekers.

// Theme Color Chooser

const currentThemec = localStorage.getItem('themeColor');
const value = document.getElementById('value');
const body = document.getElementById('usrhue');

if (currentThemec) {
const themeColor = currentThemec;
body.style.filter = currentThemec;
value.textContent = 'Color';
}

document.getElementById('hue-rotate').oninput = function() {
const filter = 'hue-rotate(xdeg)'.replace('x', this.value);
value.textContent = 'Color Tomato';
body.style.filter = filter;

localStorage.setItem( 'themeColor', body.style.filter );

switch (this.value) {
case '45':
  return (value.textContent = 'Color Wood');
case '90':
  return (value.textContent = 'Color Green House');
case '135':
  return (value.textContent = 'Color Green Gross');
case '180':
  return (value.textContent = 'Color Fun Green');
case '225':
  return (value.textContent = 'Color Sky Blue');
case '270':
  return (value.textContent = 'Color Vivid Violet');
case '315':
  return (value.textContent = 'Color Rose & Guns');
default:
  return (value.textContent = 'Color Tomato');
}
}
SIMBIOSIS surl
  • 357
  • 3
  • 15
  • This question has several libraries that will convert hex values to color names? https://stackoverflow.com/questions/9224404/get-color-name-by-hex-or-rgb You could combine it with this question to get the names from hsl https://stackoverflow.com/questions/36721830/convert-hsl-to-rgb-and-hex – Samuel Aug 06 '21 at 17:58
  • I already got the names of the colors, the hex values and the degrees which produces each color, so, why to overload my code using a third party library? I just need to create a function (or something else) which instead, for example, of showing that hue-rotate(135deg) shows the real color name for that degrees which is Chathams Blue. Nothing to calculate as this values are be fixed. – SIMBIOSIS surl Aug 06 '21 at 18:05

2 Answers2

1

There isn't as far as I can tell, a way to get the hex color of an element after filters have been applied to it. However, it is possible to implement the hue-rotate function in Javascript to get the hex value of the color.

CSS hue-rotate is just an approximation so we can either use Javascript to do a true hue-rotate or we can use Javascript to generate the same approximation as CSS.

For a true hue-rotate you can the spin() function from TinyColor.js

let hr = new tinycolor("#ff0000").spin(45).toHex();

for a css approximation you can use the function from this answer https://stackoverflow.com/a/29521147/2666293

I've slightly modified this function in the example below.

I have created a snipped below to compare the options. The option contains both an example for getting the color names via lookup table (the colors in the table have very slight changes so they match up with the output of hueRotateJs) and an example of using Name that Color

let initialColor = "#006648";
let colorLookupTable = {
    "#006648":"Fun Green",
    "#135b81":"Chathams Blue",
    "#48489c":"Victoria Violet",
    "#81398a":"Vivid Violet",
    "#9c3654":"Night Shadz",
    "#8a421b":"Rope Color",
    "#545400":"Verdum Green",
    "#1b6313":"Green House"
};

function lookupColor(colorhex)
{
    if (colorLookupTable.hasOwnProperty(colorhex)) {
        return colorLookupTable[colorhex];
    }
    return "Unknown";
}

function processColors()
{
    "use strict";

    let angle = parseInt(document.getElementById("hue-rotate").value);

    //lookup color hex codes
    let tcHex = "#"+(new tinycolor(initialColor).spin(angle).toHex());
    let jsHex = hueRotateJs(initialColor,angle);

    //lookup color names
    let tcNameNtc = ntc.name(tcHex)[1];
    let tcNameLookup = lookupColor(tcHex);

    let jsNameNtc = ntc.name(jsHex)[1];
    let jsNameLookup = lookupColor(jsHex);


    //output colors to html
    let hrTCelement = document.getElementById("hrTC")
    hrTCelement.style.color = tcHex;
    hrTCelement.innerHTML = "TinyColor: <br/>"+
    "NTC name: "+tcNameNtc+"<br/>"+
    "Lookup Table Name: "+tcNameLookup+"<br/>"+
    "hex: "+tcHex;

    let hrJsElement = document.getElementById("hrJS")
    hrJsElement.style.color = jsHex;
    hrJsElement.innerHTML = "hueRotateJs: <br/>"+
    "NTC name: "+jsNameNtc+"<br/>"+
    "Lookup Table Name: "+jsNameLookup+"<br/>"+
    "hex: "+jsHex;

    let cssElement = document.getElementById("hrCss");
    hrCss.style.color = initialColor;
    hrCss.style.filter = "hue-rotate("+angle+"deg)"
}

window.addEventListener("load",processColors);
let rotater = document.getElementById("hue-rotate");
rotater.addEventListener("change",processColors);

function hueRotateJs(color,angle) {
    // Get the RGB and angle to work with.
    var r = parseInt(color.substr(1, 2), 16);
    var g = parseInt(color.substr(3, 2), 16);
    var b = parseInt(color.substr(5, 2), 16);
    var angle = (parseInt(angle) % 360 + 360) % 360;
    
    // Hold your breath because what follows isn't flowers.
    
    var matrix = [ // Just remember this is the identity matrix for
        1, 0, 0,   // Reds
        0, 1, 0,   // Greens
        0, 0, 1    // Blues
    ];
    
    // Luminance coefficients.
    var lumR = 0.2126;
    var lumG = 0.7152;
    var lumB = 0.0722;
    
    // Hue rotate coefficients.
    var hueRotateR = 0.143;
    var hueRotateG = 0.140;
    var hueRotateB = 0.283;
    
    var cos = Math.cos(angle * Math.PI / 180);
    var sin = Math.sin(angle * Math.PI / 180);
    
    matrix[0] = lumR + (1 - lumR) * cos - lumR * sin;
    matrix[1] = lumG - lumG * cos - lumG * sin;
    matrix[2] = lumB - lumB * cos + (1 - lumB) * sin;
    
    matrix[3] = lumR - lumR * cos + hueRotateR * sin;
    matrix[4] = lumG + (1 - lumG) * cos + hueRotateG * sin;
    matrix[5] = lumB - lumB * cos - hueRotateB * sin;
    
    matrix[6] = lumR - lumR * cos - (1 - lumR) * sin;
    matrix[7] = lumG - lumG * cos + lumG * sin;
    matrix[8] = lumB + (1 - lumB) * cos + lumB * sin;
    
    function clamp(num) {
        return Math.round(Math.max(0, Math.min(255, num)));
    }
    
    function hexOf(diget){
        let prepend = (diget <= 15)?"0":"";
        return prepend+diget.toString(16);
    }
    
    var R = clamp(matrix[0] * r + matrix[1] * g + matrix[2] * b);
    var G = clamp(matrix[3] * r + matrix[4] * g + matrix[5] * b);
    var B = clamp(matrix[6] * r + matrix[7] * g + matrix[8] * b);

    return  "#"+hexOf(R)+hexOf(G)+hexOf(B);
}
<script type="text/javascript" src="https://chir.ag/projects/ntc/ntc.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tinycolor/1.4.1/tinycolor.min.js"></script>
<p id="hrCss">Css hue-rotate</p>
<p id="hrJS"></p>
<p id="hrTC"></p>
<input id="hue-rotate" step="45" type="range" min="0" max="315" value="0">
Samuel
  • 1,073
  • 11
  • 22
  • That's pretty nice and appreciate a lot your answer but displaying the hex color still is useless and nonsensed to the user. What i need is to display the name of the color instead the hue-rotate degrees or the hex color code. In your example, instead of displaying hueRotateJs: Red (#ff0000) it should simply display Color: Red. But using the exact colors and names I provided in the question. Could you fix that please? – SIMBIOSIS surl Aug 06 '21 at 19:19
  • @SIMBIOSIS I'm not sure exactly what you are asking for. The colors you have in your question don't match up with the colors actually generated by `hue-rotate()`. For example your 'Green House' you have listed as `("#1C6313") && (315deg)` however if you are starting with `#ff0000` as in your example `hue-rotate(315deg)` gets you `#eb009e` not `1C6313` – Samuel Aug 06 '21 at 20:00
  • Sorry for that, my satarting color really is the first on the list, Fun Green. I misleaded it when coping the css. – SIMBIOSIS surl Aug 06 '21 at 20:23
  • I've updated my answer to use the base color of `#006648`. I've also added an example of using a table of colors to look up the name. – Samuel Aug 06 '21 at 21:02
  • Sorry, but I have just accepted @ЖнецЪ who worked based on my code, what avoided me to refactor the whole thing, what I would have to do with yours, and came a little bit earlier with a working answer. But your code is great and I'm just adding it to my little colection for the future. Which are the libraries you used or the code in the answer works on its own? – SIMBIOSIS surl Aug 06 '21 at 21:22
  • @SIMBIOSIS No problem, the code has the libraries [TinyColor](https://bgrins.github.io/TinyColor/) and [Name that Color](https://chir.ag/projects/ntc/) in it, though you can take that out and some of it will still work. The `hueRotateJs()` function came from [here](https://stackoverflow.com/a/29521147/2666293) with some modifications. – Samuel Aug 06 '21 at 21:31
1

Maybe that approach will solve you issue. Just put the switch in the function.

const value = document.getElementById('value');
const body = document.getElementById('usrhue');

value.textContent = `Color: Tomato`;

document.getElementById('hue-rotate').oninput = function() {
  const filter = 'hue-rotate(xdeg)'.replace('x', this.value);
  body.style.filter = filter;
  switch (this.value) {
    case '45':
      return (value.textContent = `Color: Wood`);
    case '90':
      return (value.textContent = `Color: Green House`);
    case '135':
      return (value.textContent = `Color: Green Gross`);
    case '180':
      return (value.textContent = `Color: Fun Green`);
    case '225':
      return (value.textContent = `Color: Sky Blue`);
    case '270':
      return (value.textContent = `Color: Vivid Violet`);
    case '315':
      return (value.textContent = `Color: Rose & Guns`);
    default:
      return (value.textContent = `Color: Tomato`);
  }
};
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  font-family: serif;
}

body {
  color: #ff0000;
  background: #f2f2f2;
  padding: 20px;
}

a {
  color: #0033cc;
}

h1 {
  color: #0033cc;
}

svg {
  fill: #0000cc;
}
<body id="usrhue">

  <h3>Theme Color</h1>
    <pre><h3><code id="value">hue-rotate(0deg)</code></h3></pre>
    <input id="hue-rotate" step="45" type="range" min="0" max="315" value="0">
    <div>
      <div class="tile">navbar</div>
      <svg class="icon" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
        xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" x="0" y="0" width="50" height="33" viewbox="0 0 50 33"><path style="fill-opacity:1;fill-rule:evenodd;stroke:none" d="m 50,16.111089 c -0.0023,-0.52719 -0.269301,-1.175411 -0.836045,-1.837787 L 33.538651,0 l 0,4.099986 10.560199,12.011103 -10.560199,12.011102 0,4.099986 15.625304,-14.273302 C 49.730699,17.286499 49.997802,16.638278 50,16.111089 Z m -50,0 c 0.0022,-0.52719 0.2693022,-1.175411 0.8360452,-1.837787 L 16.46135,0 l 0,4.099986 -10.5601995,12.011103 10.5601995,12.011102 0,4.099986 L 0.8360452,17.948875 C 0.2693022,17.286499 0.0021979,16.638278 0,16.111089 Z"/></svg>
    </div>
</body>

With LocalStorage

  const theme = localStorage.getItem('theme');
  const value = document.getElementById('value');
  const body = document.getElementById('usrhue');
  const hueRotate = document.getElementById('hue-rotate');

  value.textContent = `Color: Tomato`;

  if (!theme) {
    localStorage.setItem(
      'theme',
      JSON.stringify({
        color: 'hue-rotate(0deg)',
        num: 0,
        name: `Tomato`,
      })
    );
  }
  if (theme) {
    const LS = JSON.parse(theme);
    console.log(LS);
    body.style.filter = LS.color;
    hueRotate.value = LS.num;
    value.textContent = `Color: ${LS.name}`;
  }

  hueRotate.oninput = function () {
    const filter = 'hue-rotate(xdeg)'.replace('x', this.value);
    body.style.filter = filter;
    let name;
    switch (this.value) {
      case '45':
        name = `Wood`;
        localStorage.setItem(
          'theme',
          JSON.stringify({
            color: body.style.filter,
            num: this.value,
            name,
          })
        );

        return (value.textContent = `Color:${name}`);
      case '90':
        name = `Green House`;
        localStorage.setItem(
          'theme',
          JSON.stringify({
            color: body.style.filter,
            num: this.value,
            name,
          })
        );

        return (value.textContent = `Color: ${name}`);
      case '135':
        name = `Green Gross`;
        localStorage.setItem(
          'theme',
          JSON.stringify({
            color: body.style.filter,
            num: this.value,
            name,
          })
        );
        return (value.textContent = `Color: ${name}`);
      case '180':
        name = `Fun Green`;
        localStorage.setItem(
          'theme',
          JSON.stringify({
            color: body.style.filter,
            num: this.value,
            name,
          })
        );
        return (value.textContent = `Color: ${name}`);
      case '225':
        name = `Sky Blue`;
        localStorage.setItem(
          'theme',
          JSON.stringify({
            color: body.style.filter,
            num: this.value,
            name,
          })
        );
        return (value.textContent = `Color: ${name}`);
      case '270':
        name = `Vivid Violet`;
        localStorage.setItem(
          'theme',
          JSON.stringify({
            color: body.style.filter,
            num: this.value,
            name,
          })
        );
        return (value.textContent = `Color: ${name}`);
      case '315':
        name = `Rose & Guns`;
        localStorage.setItem(
          'theme',
          JSON.stringify({
            color: body.style.filter,
            num: this.value,
            name,
          })
        );
        return (value.textContent = `Color: ${name}`);
      default:
        name = `Tomato`;
        localStorage.setItem(
          'theme',
          JSON.stringify({
            color: body.style.filter,
            num: this.value,
            name,
          })
        );
        return (value.textContent = `Color: ${name}`);
    }
  };
Anton
  • 8,058
  • 1
  • 9
  • 27
  • Hi, @ЖнецЪ. This is just what I needed but now my function, on reload, does not apply the last choosen color. Indeed, there is no localStorage item any more. How can I fix that? Please. – SIMBIOSIS surl Aug 06 '21 at 20:14
  • @ ЖнецЪ, please, look at the code I updated at the end of my question and tell me, if you are so kind, why my themeColor is not storaged any more. – SIMBIOSIS surl Aug 06 '21 at 20:30
  • 1
    I could fix it, I just moved the **localStorage.setItem** before the **switch**. I will accept your question and also up vote it. Thank you very much, you saved my day. – SIMBIOSIS surl Aug 06 '21 at 21:11
  • OK.Very good indeed!. You know, I have a related question still unanswered and maybe you could also help me with that, it is at https://stackoverflow.com/questions/68579939/how-to-exclude-specific-classes-from-being-affected-by-a-hue-rotate-filter-set-t just take a look and then you'will decide – SIMBIOSIS surl Aug 06 '21 at 21:28
  • You know that your localStorage version works perfectly on its own but when I integrate it with my project it throw an error with JSON.parse. – SIMBIOSIS surl Aug 07 '21 at 00:55
  • If you have already value `theme` and it's not an object, try to clean up your `localStorage`. – Anton Aug 07 '21 at 06:27
  • I think I tried that one. Anyway I will recheck. Thanks for the advice. – SIMBIOSIS surl Aug 07 '21 at 09:23
  • You was right, I had that value theme already. But now I'll switch to the other answer which obviously is more complete and just what I was trying to achieve. Thanks a lot. – SIMBIOSIS surl Aug 07 '21 at 11:54