【问题标题】:Blend colors between values在值之间混合颜色
【发布时间】:2019-05-19 09:50:44
【问题描述】:

我正在尝试使用画布弧线创建类似时间倒计时,绘图还可以,但弧线将具有笔触颜色,我想将其混合。

我绘制弧线的方式是使用 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

【问题讨论】:

  • 您可以使用 0 - 100 表示法的 RGB 颜色。示例 rgb(0, 100, 0) 给出绿色。 fiddle
  • @PeterBode 我知道我可以做到这一点,虽然现在的问题是我有 3 种不同颜色的红色、绿色和蓝色值,但现在每个百分比的正确值如何?
  • @PeterBode 这正是我开始测试弧线的方式,尽管这里的问题是我需要在代码 sn-p 中的颜色之间很好地混合,而不是从黑色到绿色。现在它是一种颜色到另一种颜色,然后是另一种颜色,基于百分比。而所有颜色都有红色、绿色和蓝色值。
  • @Tedinoz 我看不出我的问题是如何重复的,因为我需要找到颜色之间的正确混合,而不是在值低于或高于时切换颜色。您能否解释一下我的问题与您链接的问题有何相同之处?谢谢:)

标签: javascript canvas colors color-blending


【解决方案1】:

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


答案分为两个主要部分:

  1. 混合颜色(已经有解决此问题的答案,使用库或创建一种方法来插入/混合颜色十六进制值)
  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);
&lt;canvas id="canvas1" width="500" height="500"&gt;&lt;/canvas&gt;

【讨论】:

  • 我什至不知道那是一回事!谢谢!尽管可以为整个弧线使用相同的 current 颜色吗?干杯!
  • @RichardSandberg 您能否详细说明“尽管可以为整个弧线使用相同的当前颜色吗?”。我不明白你的意思...
  • 所以我有一个值(百分比 100-0),所以如果值为 50,则为半弧,半弧将具有橙色,如果值为 66,则为绿色和橙色的 50/50 混合,如果值为 33,则为橙色和红色的 50/50 混合,如果值为 0,则为红色。但我想拥有它,这样它才能在颜色之间找到一个很好的融合,所以说例如值为 90,那么弧的颜色应该比橙色更绿色
  • @a--m 你试过把它变成倒数计时器吗?我意识到它可以完美地用作静态显示,但它会转化为渐进式显示吗?
  • @RichardSandberg 原创不错,渐进式显示是向前迈出的一大步,并且它包含代码,因此其他人可以使用和适应。
【解决方案2】:

我假设你想要

  • 根据三种已知颜色创建色标 - '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>

【讨论】:

  • 完美,正是我想要的!谢谢!
【解决方案3】:

这是我制作的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);

【讨论】:

  • 我喜欢这个,因为它以倒计时为例。我是对的,它从深绿色开始,然后逐渐过渡到亮绿色,但是整个弧线会改变颜色,而不仅仅是弧线的“新”部分?据我了解,OP 正在寻找弧的现有部分以保持其颜色。
  • @Tedinoz 是的,旋转量也在计算值。正在玩它,你可以得到非常棒的颜色变化。添加 ((100-i)*2) 作为 R 值使其从红色混合到绿色。
【解决方案4】:

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

// 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()
}
&lt;canvas id="the_canvas" width="500" height="300"&gt;&lt;/canvas&gt;

【讨论】:

  • 谢谢!现在我无论如何都不必使用库,我将更多地研究颜色插值是如何工作的。
猜你喜欢
  • 1970-01-01
  • 2011-11-14
  • 2020-01-01
  • 1970-01-01
  • 2020-06-17
  • 1970-01-01
  • 2020-12-25
  • 2014-09-22
  • 2017-09-23
相关资源
最近更新 更多