在值之间混合颜色
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
}
编辑:这就是我要找的结果。它现在完全按照我想要的方式工作。干杯。
注意:这个答案是对原始答案的后期大修改。在第一次尝试后我误解了 OP 的意图,OP 添加了一个 link 示例,从那时起我打算提供一个解决方案,因为我对当前的答案不满意并且练习很有趣构建。
答案分为两个主要部分:
- 混合颜色(已经有解决这个问题的答案,使用库或创建一个方法来 interpolate/blend 颜色十六进制值)
- 绘制弧线并使用混合色
/*
* 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>
我正在尝试使用 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
}
编辑:这就是我要找的结果。它现在完全按照我想要的方式工作。干杯。
注意:这个答案是对原始答案的后期大修改。在第一次尝试后我误解了 OP 的意图,OP 添加了一个 link 示例,从那时起我打算提供一个解决方案,因为我对当前的答案不满意并且练习很有趣构建。
答案分为两个主要部分:
- 混合颜色(已经有解决这个问题的答案,使用库或创建一个方法来 interpolate/blend 颜色十六进制值)
- 绘制弧线并使用混合色
/*
* 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>