在值之间混合颜色

Blend colors between values

我正在尝试使用 canvas 圆弧来创建类似时间倒计时的效果,绘图没问题,但圆弧会有描边颜色,我想将其混合。

我画弧的方法是用0到100之间的数字,100基本上是完整的弧,0什么都不是。当该值大于 66 且小于(或等于)100 时,它将是绿色和橙色的混合,100 是全绿色,66 是全橙色。 65 及以下将是橙色和红色的混合,其中总红色为 0。

我不知道从哪里开始,不知道要搜索什么来研究如何获得这些值。

谁能给我指出正确的方向?

到目前为止,这是我的代码 x)

static color (value)
{
    const StartColor  = 0x66BB6A; // Green
    const MiddleColor = 0xFFA726; // Orange
    const EndColor    = 0xEF5350; // Red  
}

编辑:这就是我要找的结果。它现在完全按照我想要的方式工作。干杯。

https://youtu.be/IeR_zMzSaAU

注意:这个答案是对原始答案的后期大修改。在第一次尝试后我误解了 OP 的意图,OP 添加了一个 link 示例,从那时起我打算提供一个解决方案,因为我对当前的答案不满意并且练习很有趣构建。


答案分为两个主要部分:

  1. 混合颜色(已经有解决这个问题的答案,使用库或创建一个方法来 interpolate/blend 颜色十六进制值)
  2. 绘制弧线并使用混合色

/*
 * blending colors 
 * (borrowed and modified from @mark-meyer answer)
 */

// break hex integer into components:
const getComponents = (color) => Array.from({length: 3}, (_, i) => Math.floor(color / 16 ** (2 * i) % 16 ** 2))

// interpolate arrays by component
const interpolate = (arr1, arr2, percent) => arr1.map((v, index) => Math.floor(v + (arr2[index] - v) * (percent / 100)))

const colorBlend = value => {
  const StartColor  = 0x11FF11; // Green
  const MiddleColor = 0xFFA726; // Orange
  const EndColor    = 0xEF5350; // Red
  
  let [start, end, v] = value < 50 
      ? [StartColor, MiddleColor, value * 2 ] 
      : [MiddleColor, EndColor, (value - 50) * 2]

  let interpoled = interpolate(getComponents(start), getComponents(end), v)
  
  const color = interpoled
  .reduce((n, component, index) => n + component * (16 ** (index * 2)), 0)
  .toString(16).padStart(6, '0')
  return `#${color}`
}

/*
 * draw an arc
 */
const canvas = document.getElementById('canvas1')
const ctx = canvas.getContext('2d')
const size = 30
let p = 0

const grad = ctx.createLinearGradient(size, size, size * 2, size * 2)
grad.addColorStop(0, "#66BB6A")
grad.addColorStop(0.5, "#FFA726")
grad.addColorStop(1, "#EF5350")

ctx.lineWidth = 6

const draw = () => {
  p += 0.01
  ctx.clearRect(0, 0, canvas.width, canvas.height)
  ctx.strokeStyle = colorBlend(p * 50)
  ctx.beginPath()
  ctx.arc(size * 1.5, size * 1.5, size, 1.5 * Math.PI, (1.5 + p) * Math.PI)
  ctx.stroke()
  if (p < 2) {
    window.requestAnimationFrame(draw);
  }
}

window.requestAnimationFrame(draw);
<canvas id="canvas1" width="500" height="500"></canvas>

我假设你想要

  • 根据三种已知颜色创建色标 - '66BB6A', 'FFA726', 'EF5350'
  • 输入域内的数字 (0 - 100) 并获得根据比例计算的新颜色。

Chroma.js 是一个不错的小型 js 库,可以进行色阶和转换。

const colorScale = chroma.scale(['66BB6A', 'FFA726', 'EF5350']).domain([0,100]);

console.log(colorScale(20).toString())

var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.fillStyle = colorScale(20);
ctx.beginPath();
ctx.arc(100, 75, 50, 0, 2 * Math.PI);
ctx.fill();
<script src="https://cdnjs.cloudflare.com/ajax/libs/chroma-js/2.0.2/chroma.min.js"></script>
<canvas id="myCanvas"></canvas>

这是我做的quick fiddle

var c = document.getElementById("foo");
var i = 0;
var timer = setInterval(function() {

    var ctx = c.getContext("2d");
    ctx.beginPath();
    ctx.arc(75, 75, 50, 0, (i / 100) * Math.PI);
    ctx.strokeStyle = 'RGB('+((100-i)*2)+', '+i+', 0)';
    ctx.stroke();

    if((i / 100) == 2) {
        clearInterval(timer);
        console.log('FULL');
    }
    i++;
}, 100);

当然,使用专门用于此的库会更容易。但以防万一有人对做数学感兴趣,您可以将十六进制整数分解成组件,然后分别对每个组件进行插值以获得特定位置的颜色:

// break hex integer into components:
const getComonents = (color) => Array.from({length: 3}, (_, i) => Math.floor(color / 16 ** (2 * i) % 16 ** 2))

// interpolate arrays by component
const interpolate = (arr1, arr2, percent) => arr1.map((v, index) => Math.floor(v + (arr2[index] - v) * (percent / 100)))

function colors(value) {
  const StartColor  = 0x11FF11; // Green
  const MiddleColor = 0xFFA726; // Orange
  const EndColor    = 0xEF5350; // Red
  
  let [start, end, v] = value < 50 
      ? [StartColor, MiddleColor, value * 2 ] 
      : [MiddleColor, EndColor, (value - 50) * 2]

  let interpoled = interpolate(getComonents(start), getComonents(end), v)
  
  return interpoled.reduce((n, component, index) => n + component * (16 ** (index * 2)), 0)
}

const canvas = document.getElementById('the_canvas')
const ctx = canvas.getContext('2d')

for (let i = 0; i<100; i++){
  ctx.beginPath();
  
  // padStart is needed to make sure a value of `ff` becomes `0000ff`
  ctx.fillStyle = "#" + colors(i).toString(16).padStart(6, '0')
  ctx.rect(i*5, 0, i*5+5, 300);
  ctx.fill()
}
<canvas id="the_canvas" width="500" height="300"></canvas>