Plotting times of day to a fixed color gradient?

213 views Asked by At

I'm making an appointments app.

I have this gradient structure (created in Pixelmator), with which I want to mark the times of day:

gradient from green to yellow to blue

In the intended scheme, 8am would be solid green, 12 noon would be solid yellow, and 8pm would be solid blue.

I need an algorithm to take the times of day and turn them into those colors, but I can't figure it out, particularly from noon to evening.

These colors are composed using the HSB value system: all colors have S and B at 100%, and from left to right the hue values are 121 (green), 60 (yellow), and 229 (blue).

The progression from the green to yellow is (morning to noon) is straightforward, because it's just a linear scaling from 121 to 60, but from yellow to blue (noon to evening), is not; this is clear if you think about the fact that going from 60 to 229 in a linear fashion would first duplicate the green-to-yellow gradient, just in reverse order, and then would go to from green to blue. In other words, a strictly linear progression would make the gradient look more like this:

gradient from green to yellow to green to blue

Can anyone point me in the right direction to understanding how to make the algorithm I need here? Do I have to use a different color value system, like RGB?

Thanks in advance for any and all help!

3

There are 3 answers

1
David Eisenstat On BEST ANSWER

Pixelmator looks like it's using RGB gradients. Demo:

const canvas = document.getElementById("gradient");
const ctx = canvas.getContext("2d");
for (let i = 0; i < canvas.width; i++) {
  const alpha = (i + 0.5) / canvas.width;
  const r = 2 * Math.min(alpha, 1 - alpha);
  const g = Math.min(1, 2 * (1 - alpha));
  const b = Math.max(0, 2 * alpha - 1);
  ctx.fillStyle = `rgba(${255*r},${255*g},${255*b})`
  ctx.fillRect(i, 0, 1, canvas.height);
}
<canvas id="gradient" width="240" height="40">

3
Pablo-No On

Here is an algorithm for that:

  1. Convert the hour to 24 hour and pass minutes and seconds to a fraction or a decimal number (i.e 8:30 -> 8.5, 8:20 -> 25/3)
  2. Substract 8 to the hour (now we have a number from 0 to 12)
  3. If the hour, h, is between 0 and 4 we will do ((-h+4)*(61/4))+60 else we will do ((-h+12)*(191/8))-131
  4. If the value is negative we'll add 360

The value we obtain will be the hue value of the color

4
Eric Backus On

Pablo-No gives a reasonable answer if it's OK for the yellow->blue transition to go through red. But the OP's original picture doesn't go through red, it goes through some kind of grey. Perhaps the saturation S should be used to try to achieve this:

// Assume time is a real value between 8 (8am) and 20 (8pm)
// H is between 0 and 360
// S and B are between 0 and 100
B = 255;
if (time < 12)
{
    // Before noon, linearly go from H=121 (green) to H=60 (yellow)
    H = (time - 8) * (60-121)/4.0 + 121;
    S = 100;
}
else
{
    // After noon, linearly go from H=60 (green) to H=229 (blue)
    // But in the middle, where the color goes green, linearly scale
    // Saturation down to zero and back to 100.
    H = (time - 12) * (229-60)/8.0 + 60;
    auto secondGreenTime = (121-60)*8.0/(229-60) + 12;
    if (time < secondGreenTime)
        S = (time - 12) * (-100.0)/(secondGreenTime-12) + 100;
    else
        S = (time - secondGreenTime) * 100.0/(20-secondGreenTime);
}