Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get color value from gradient by percentage with javascript?

I have a fixed width div with gradient applied using css. I want to build slider based color picker based on this gradient.

When i drag the slider i calculate the percentage position, and i want to get the hex or rgb color code based on this value.

My idea was to create an array with the start/stop positions and colors defined, then find two values from this array based on the slider position, then somehow find the color between: this is where i can't move forward.

Demo: http://jsfiddle.net/pdu8rpfv/

var gradient = [
    [
        0,
        'ff0000'
    ],
    [
        28,
        '008000'
    ],
    [
        72,
        '0000ff'
    ],
    [
        100,
        'ff0000'
    ]
];
$( "#slider" ).slider({
    min: 1,
    slide: function( event, ui ) {

        var colorRange = []
        $.each(gradient, function( index, value ) {
            if(ui.value<=value[0]) {
                colorRange = [index-1,index]
                return false;
            }
        });

        $('#result').css("background-color", 'red');

    }
});
like image 885
passatgt Avatar asked May 09 '15 17:05

passatgt


People also ask

What is Rgba in linear gradient?

Linear Gradient - Transparency To add transparency, we use the rgba() function to define the color stops. The last parameter in the rgba() function can be a value from 0 to 1, and it defines the transparency of the color: 0 indicates full transparency, 1 indicates full color (no transparency).

What is gradient Javascript?

Gradients can be used to fill rectangles, circles, lines, text, etc. Shapes on the canvas are not limited to solid colors. There are two different types of gradients: createLinearGradient(x,y,x1,y1) - creates a linear gradient.

How do you find the gradient of a color?

Select the Gradient tool in the toolbar. In the selected artwork you'll see the gradient annotator, which shows the gradient slider and the color stops. Double-click a color stop on the artwork to edit the color, drag the color stops, click beneath the gradient slider to add new color stops, and more.


7 Answers

I was able to solve this issue using this function, which is based on the less.js function: http://lesscss.org/functions/#color-operations-mix

function pickHex(color1, color2, weight) {
    var w1 = weight;
    var w2 = 1 - w1;
    var rgb = [Math.round(color1[0] * w1 + color2[0] * w2),
        Math.round(color1[1] * w1 + color2[1] * w2),
        Math.round(color1[2] * w1 + color2[2] * w2)];
    return rgb;
}

I just simply need to pass the two closest color codes from the gradient array and the ratio where the slider handle is located(which can be calculated easily with the help of the slider width). Here is the live example:

http://jsfiddle.net/vksn3yLL/

like image 110
passatgt Avatar answered Oct 05 '22 14:10

passatgt


There is a nice lib that handles variety of color manipulations chroma.js

yarn add chroma-js

And then

import chroma from 'chroma-js';

const f = chroma.scale(['yellow', 'red', 'black']);

console.log( f(0.25).toString() );                    // #ff8000
console.log( f(0.5).css('rgba') );                    // rgba(255,0,0,1)
console.log( f(0.75).css() );                         // rgb(128,0,0)
console.log( f(1).css() );                            // rgb(0,0,0)
like image 39
Roman Yudin Avatar answered Oct 05 '22 14:10

Roman Yudin


This is another way to convert percentage to color

This exemple make a displayed value change from red to green depending on it's value (like for example in excel conditional formating):

function(percentValue) {
  $(`#output`)
    // print the value
    .html(percentValue)
    // colorize the text, more red if it's close to 0
    // and more green as it approach 100
    .css({color: `rgb(${(100 - percent) *2.56}, ${percent *2.56},0)`})
}

Please see demo below:

$('button').click( e => {
  const percent = Math.floor(Math.random()*100);
  const newElm = $(`<b>${percent}</b><br>`)
  .css({color: `rgb(${(100 - percent) *2.56},${percent *2.56},0)`})
  $('body').append(newElm);
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button>Click to make new percentage</button><br>
like image 25
TOPKAT Avatar answered Oct 05 '22 16:10

TOPKAT


Three color gradient

Just in case someone wants a 3 color gradient, here's an example using red, yellow and green:

enter image description here

The github JS code for the colorGradient function is available here.

function colorGradient(fadeFraction, rgbColor1, rgbColor2, rgbColor3) {
    var color1 = rgbColor1;
    var color2 = rgbColor2;
    var fade = fadeFraction;

    // Do we have 3 colors for the gradient? Need to adjust the params.
    if (rgbColor3) {
      fade = fade * 2;

      // Find which interval to use and adjust the fade percentage
      if (fade >= 1) {
        fade -= 1;
        color1 = rgbColor2;
        color2 = rgbColor3;
      }
    }

    var diffRed = color2.red - color1.red;
    var diffGreen = color2.green - color1.green;
    var diffBlue = color2.blue - color1.blue;

    var gradient = {
      red: parseInt(Math.floor(color1.red + (diffRed * fade)), 10),
      green: parseInt(Math.floor(color1.green + (diffGreen * fade)), 10),
      blue: parseInt(Math.floor(color1.blue + (diffBlue * fade)), 10),
    };

    return 'rgb(' + gradient.red + ',' + gradient.green + ',' + gradient.blue + ')';
}

Demo:

// Gradient Function
function colorGradient(fadeFraction, rgbColor1, rgbColor2, rgbColor3) {
    console.log('>> fade: ', fadeFraction)
    var color1 = rgbColor1;
    var color2 = rgbColor2;
    var fade = fadeFraction;

    // Do we have 3 colors for the gradient? Need to adjust the params.
    if (rgbColor3) {
      fade = fade * 2;

      // Find which interval to use and adjust the fade percentage
      if (fade >= 1) {
        fade -= 1;
        color1 = rgbColor2;
        color2 = rgbColor3;
      }
    }

    var diffRed = color2.red - color1.red;
    var diffGreen = color2.green - color1.green;
    var diffBlue = color2.blue - color1.blue;

    var gradient = {
      red: parseInt(Math.floor(color1.red + (diffRed * fade)), 10),
      green: parseInt(Math.floor(color1.green + (diffGreen * fade)), 10),
      blue: parseInt(Math.floor(color1.blue + (diffBlue * fade)), 10),
    };
    return 'rgb(' + gradient.red + ',' + gradient.green + ',' + gradient.blue + ')';
}
  
// IMPLEMENTATION EXAMPLE:
var spans = $('.gradient-test');
var count = spans.length, color;
var color1 = {
  red: 19, green: 233, blue: 19
};
var color3 = {
  red: 255, green: 0, blue: 0
};
var color2 = {
  red: 255, green: 255, blue: 0
};
$('.gradient-test').each(function(i, span) {
  var g = Math.round(((i+1) * 100) / count);
  if (g < 10){
    g = '0' + g;
  } 
  g = +g === 100 ? 1 : +('0.' + g)
  color = colorGradient(g, color1, color2, color3);
  $(span).css('background-color', color);
});
.gradient-test {
  width: 1rem;
  height: 1rem;
  display: inline-block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<span class="gradient-test"></span>
<span class="gradient-test"></span>
<span class="gradient-test"></span>
<span class="gradient-test"></span>
<span class="gradient-test"></span>
<span class="gradient-test"></span>
<span class="gradient-test"></span>
<span class="gradient-test"></span>
<span class="gradient-test"></span>
<span class="gradient-test"></span>
<span class="gradient-test"></span>
<span class="gradient-test"></span>
<span class="gradient-test"></span>
<span class="gradient-test"></span>
<span class="gradient-test"></span>
<span class="gradient-test"></span>
<span class="gradient-test"></span>
like image 23
luiscla27 Avatar answered Oct 05 '22 16:10

luiscla27


Infinite number of colors gradient (two or more)

If your have 2, 3, 4 or twenty colors you can use this solution. Generate an HTML element and style it with CSS gradient background. Then look at a single pixel color value.

  1. Create a <canvas> element. with width of 101px (0 to 100 percent) and height 1px (we don't care about height). Then set the background-color to gradient. See method initCanvas(colorsArray).

  2. Look at a single pixel of the canvas, and return it's color. See method getColor(percent).

  3. At the end you can find and example of gradient made from 5 colors (red, orange, green, lime, blue).

const WIDTH = 101; // 0 to 100
const HEIGHT = 1;
  
let context;

function initCanvas(gradientColors) // gradientColors [colorA, colorB, ..]
{
  // Canvas
  const canvasElement = document.createElement("CANVAS");
  canvasElement.width = WIDTH;
  canvasElement.height = HEIGHT;

  context = canvasElement.getContext("2d");
  
  // Gradient
  const gradient = context.createLinearGradient(0, 0, WIDTH, 0); // x0, y0, x1, y1
  
  const step = 1 / (gradientColors.length - 1); // need to validate at least two colors in gradientColors
  let val = 0;
  gradientColors.forEach(color => {
    gradient.addColorStop(val, color);
    val += step;
  });

  // Fill with gradient
  context.fillStyle = gradient;
  context.fillRect(0, 0, WIDTH, HEIGHT); // x, y, width, height
}

function getColor(percent) // percent [0..100]
{
  const color = context.getImageData(percent, 0, 1, 1); // x, y, width, height
  const rgba = color.data;
  
  return `rgb(${ rgba[0] }, ${ rgba[1] }, ${ rgba[2] })`;
}

// Test:
initCanvas(['red', 'orange', 'green', 'lime', 'blue']);

document.getElementById("color0"  ).style.backgroundColor = getColor(0);
document.getElementById("color10" ).style.backgroundColor = getColor(10);
document.getElementById("color20" ).style.backgroundColor = getColor(20);
document.getElementById("color30" ).style.backgroundColor = getColor(30);
document.getElementById("color40" ).style.backgroundColor = getColor(40);
document.getElementById("color50" ).style.backgroundColor = getColor(50);
document.getElementById("color60" ).style.backgroundColor = getColor(60);
document.getElementById("color70" ).style.backgroundColor = getColor(70);
document.getElementById("color80" ).style.backgroundColor = getColor(80);
document.getElementById("color90" ).style.backgroundColor = getColor(90);
document.getElementById("color100").style.backgroundColor = getColor(100);
.example {
  width: 100px;
  height: 25px;
}
<h3>Gradient colors: red, orange, green, lime, blue</h3>
<div id="color0"   class="example">0%  </div>
<div id="color10"  class="example">10% </div>
<div id="color20"  class="example">20% </div>
<div id="color30"  class="example">30% </div>
<div id="color40"  class="example">40% </div>
<div id="color50"  class="example">50% </div>
<div id="color60"  class="example">60% </div>
<div id="color70"  class="example">70% </div>
<div id="color80"  class="example">80% </div>
<div id="color90"  class="example">90% </div>
<div id="color100" class="example">100%</div>

P.S - I code with two methods initCanvs() and getColors() so you won't generate new canvas for each color selection. But you can merge them if you have new gradient each time.

like image 40
Gil Epshtain Avatar answered Oct 05 '22 15:10

Gil Epshtain


Using similar logic as Felipe Ribeiro's top answer,

I have created a Javascript color-scales that handles it for you. You are also able to input multiple color stops as well as set a transparency level.

A live demo is here: https://codepen.io/dalisc/pen/yLVXoeR

Link to the package is here: https://www.npmjs.com/package/color-scales

Example usage:

const ColorScale = require("color-scales");
let colorScale = new ColorScale(0, 100, ["#ffffff", "#000000"], 0.5);
let rgbaStr = colorScale.getColor(50).toRGBAString(); // returns "rgba(127,127,127, 0.5)"

The package is able to output a hex, rgb, or rgba string depending on your needs. It can also output a custom Color object ({r,g,b,a}) in case you need to cherry-pick the individual color components.

I came across a similar issue when trying to mimick a Tableau dashboard on a Javascript-based application. I realised it is a common feature of data visualization tools such as Tableau and Microsoft Excel, so I have created an npm package to handle it on Javascript application. If you would like to reduce your code, you could use the package.

like image 33
Dalis Chan Avatar answered Oct 05 '22 14:10

Dalis Chan


This is my realization for multiple color gradient set RGB. (Without alpha channel). Extended variant of previous answer.

r1.addEventListener('change',(ev)=>{
  let res = test(ev.target.value)
  d2.innerText=ev.target.value +' color: '+res
  d2.style.backgroundColor = res
})
function test(value){
  let colorPicked = pickRgbRange(value,
       {color:[255,0,228,1], position:0},
       {color:[0,194,255,1,1], position:15},
       {color:[35,200,0,1], position:35},
       {color:[255, 250, 164], position:50},
       {color:[255,0,0,1], position:75},
       {color:[0,0,0,1], position:100}
       );
  let resultRgba = `rgba(${colorPicked[0]},${colorPicked[1]},${colorPicked[2]},${colorPicked[3]})`;
     return resultRgba
}

//(ildarin cc0) Start copy from here: ----------------------------------
/** @description usage
   let colorPickedRgba = pickRgbRange(value,
     {color:[255,0,228,1], position:0},
     {color:[0,194,255,1,0.5], position:.15},
     {color:[35,200,0,1], position:.35},
     {color:[255, 250, 164], position:.50},
     {color:[255,0,0,1], position:.75},
     {color:[0,0,0,1], position:.100}
     )
     let resultRgba = `rgba(${colorPicked[0]},${colorPicked[1]},${colorPicked[2]},${colorPicked[3]})`
*/
function pickRgbRange(position, ...elements) {
    var [left, right, weight] = pickClosest(position, ...elements);
    return pickRgba(left.color, right.color, weight);
}

function pickRgba(color1, color2, weight) {
    var w1 = weight;
    var w2 = 1 - w1;
    var rgba = [
        Math.round(color1[0] * w2 + color2[0] * w1),
        Math.round(color1[1] * w2 + color2[1] * w1),
        Math.round(color1[2] * w2 + color2[2] * w1),
        1
    ];
    return rgba;
}

function pickClosest(position, ...elements) {
    var left = elements[0], 
    right = { color: [0, 0, 0], position: Number.MAX_VALUE };
    var leftIndex = 0;
    for (var i = 0; i < elements.length; i++) {
        if (position >= elements[i].position && position > left.position){
            left = elements[i];
            leftIndex = i;
        }
    }
    if (elements.length - 1 === leftIndex) {
        right = elements[leftIndex];
    }
    else {
        right = elements[leftIndex + 1];
    }
    if(left == right){
      return [right, right, 0];
    }
    var dleft = position - left.position;
    var sum = dleft + right.position - position;
    var weight = dleft / sum;
    return [left, right, weight];
}
#r1{
width:100%;
}
#d1,
#d2 {
  width: 100%;
  height: 50px;
}

#d1 {
  background: linear-gradient(90deg,
      rgb(255, 0, 228) 0%,
      rgb(0, 194, 255) 15%,
      rgb(35, 200, 0) 35%,
      rgb(255, 250, 164) 50%,
      rgb(255, 0, 0) 75%,
      rgb(0, 0, 0) 100%);
}

#d2 {
  text-shadow:0 0 4px #fff;
  background-color: #ccc;
}
<div id='d1'></div>
<input id='r1' type='range' />
<div id='d2'></div>
like image 1
ildarin Avatar answered Oct 05 '22 16:10

ildarin