问题
If one knows the "weights" of red, green, and blue, one can create a color using a class like
.blaRGB {
color: rgb(128, 128, 128)
}
.blaHSL {
color: hsl(33%, 34%, 33%)
}
and use it in HTML like so:
<p class="blaRGB">BLA RGB</p>
<p class="blaHSL">BLA HSL</p>
With the values shown above, blaRGB
is dingy gray and blaHSL
is white.
But what if one wants to represent combinations of other colors, such as green, blue, orange, and gold?
Is there a way to define such colors with a notation like the following?
.CyndiLauper {
color: truecolors(24, 13, 11, 12)
}
Or with color: gbog(24, 13, 11, 12)
where the letters in gbog
stand for green, blue, orange, and gold respectively?
The intent is that with the CSS definition above, the HTML
<p class="CyndiLauper">These are your True Colors, shining through!</p>
would display the text in a weighted combination of those four colors.
I believe this is possible using some convoluted math using the RGB values of green, blue, orange, and gold, but I don't know how to proceed with that.
UPDATE
I realize that a key component to the question, which I initially omitted, would be, "What exactly do you mean by green, blue, orange, and gold"?
To answer that, according to the official "True Colors" website, and using the ColorZilla browser add-in, the RGB values for these colors are:
green = 15, 167, 73
blue = 0, 152, 215
orange = 243, 123, 38
gold = 255, 230, 101
(They actually look more like forest green, dodger blue, red-orange, and yellow to me.)
回答1:
There are many ways to mix colors, depending on what color model you're using:
- additive mixing
- subtractive mixing
- alpha compositing
The following code snippet demonstrates two mixing methods. The leftmost column displays the four original colors. The next column shows the same colors with 0.25 opacity, resulting in translucent colors. The third column layers the translucent colors on top of one another, which is equivalent to alpha compositing.
The fourth column shows an alternative approach to mixing. Here we see the result of separately averaging the R, G, and B components of each of the four original colors.
var Colors = {
names: ['green', 'blue', 'orange', 'gold'],
values: {
green: { r: 15, g: 167, b: 73 },
blue: { r: 0, g: 152, b: 215 },
orange: { r: 243, g: 123, b: 38 },
gold: { r: 255, g: 230, b: 101 }
}
};
Colors.toHex2 = function (decimal) {
var hex = decimal.toString(16);
return (hex.length == 1 ? '0'+hex : hex);
};
Colors.toColorString = function (value) {
var g = Colors,
toHex2 = g.toHex2;
var parts = [ '#', toHex2(value.r), toHex2(value.g), toHex2(value.b) ];
return parts.join('');
};
Colors.load = function () {
var g = Colors,
names = g.names,
values = g.values,
containers = {
original: document.getElementById('original'),
translucent: document.getElementById('translucent'),
layered: document.getElementById('layered'),
averaged: document.getElementById('averaged')
},
averaged = { r: 0, g: 0, b: 0 };
document.body.style.paddingTop = 10*(1+names.length) + 'px';
for (var i = 0; i < names.length; ++i) {
var name = names[i],
value = values[name],
color = g.toColorString(value),
swatch = document.createElement('div'),
proportion = 1 / names.length;
swatch.className = 'swatch';
swatch.style.backgroundColor = color;
containers.original.appendChild(swatch);
swatch = swatch.cloneNode();
swatch.style.opacity = proportion;
containers.translucent.appendChild(swatch);
swatch = swatch.cloneNode();
swatch.style.height = 60 + 10*(names.length-1) + 'px';
swatch.style.top = 10*(1+i-names.length) + 'px';
containers.layered.appendChild(swatch);
averaged.r += proportion * value.r;
averaged.g += proportion * value.g;
averaged.b += proportion * value.b;
}
swatch = document.createElement('div');
swatch.className = 'swatch';
averaged.r = Math.round(averaged.r);
averaged.g = Math.round(averaged.g);
averaged.b = Math.round(averaged.b);
swatch.style.backgroundColor = g.toColorString(averaged);
containers.averaged.appendChild(swatch);
};
window.onload = Colors.load;
body {
font-family: sans-serif;
font-size: 24px;
margin: 0;
padding-left: 20px;
}
.display {
width: 120px;
float: left;
}
.swatch {
width: 100px;
height: 60px;
margin-bottom: 10px;
}
.layered {
position: relative;
height: 60px;
}
.layered .swatch {
position: absolute;
}
<div id="original" class="display"></div>
<div id="translucent" class="display"></div>
<div id="layered" class="display layered"></div>
<div id="averaged" class="display"></div>
In the demonstration above, we let the web browser do the alpha compositing for us. We can also do alpha compositing directly. Layer opacity is equivalent to the alpha channel, so we set the alpha channel of each color to the weight of the color. In other words, if the weight of a color is 25%, we set its alpha channel to 0.25.
Note that the alpha channel ranges from 0 to 1. However, our RGB components range from 0 to 255.
Suppose we are mixing these two colors:
- RGB value
background
with alpha channelalpha.background
- RGB value
foreground
with alpha channelalpha.foreground
We calculate the resulting color's alpha channel alpha.mix
like so:
alpha.mix = 1 - (1-alpha.background)*(1-alpha.foreground);
To compute the R component of the RGB value mix
, we do this:
mix.r = 255 * (foreground.r/255.0 * alpha.foreground/alpha.mix +
mix.r/255.0 * alpha.background*(1-alpha.foreground)/alpha.mix);
And likewise for the G component mix.g
and the B component mix.b
.
The code snippet below is an interactive demonstration that compares alpha compositing with the simpler approach of averaging the RGB components in parallel. Run the code and play around with the sliders to see the difference between these two approaches.
var Mixer = {
color: {
names: ['green', 'blue', 'orange', 'gold'],
rgb: {
green: { r: 15, g: 167, b: 73 },
blue: { r: 0, g: 152, b: 215 },
orange: { r: 243, g: 123, b: 38 },
gold: { r: 255, g: 230, b: 101 }
}
},
style: {
swatch: { width: 150, height: 90, margin: { right: 15 } },
slider: { height: 20 },
handle: { width: 20, height: 34 }
}
};
Mixer.toHex2 = function (decimal) {
var hex = decimal.toString(16);
return (hex.length == 1 ? '0'+hex : hex);
};
Mixer.toCSS = function (rgb) {
var g = Mixer,
toHex2 = g.toHex2;
var parts = [ '#', toHex2(rgb.r), toHex2(rgb.g), toHex2(rgb.b) ];
return parts.join('');
};
Mixer.toString = function (rgb) {
return 'rgb(' + [rgb.r, rgb.g, rgb.b].join(', ') + ')';
};
Mixer.makeUnselectable = function (element) {
element.className += ' unselectable';
element.ondragstart = element.onselectstart = function (event) {
event.preventDefault();
};
};
Mixer.makeElement = function (tag, className, innerHTML, unselectable) {
var g = Mixer,
element = document.createElement(tag);
element.className = (className ? className : '');
element.innerHTML = (innerHTML ? innerHTML : '');
if (unselectable) {
g.makeUnselectable(element);
}
return element;
};
Mixer.handleDown = function (event) {
event = event || window.event;
var g = Mixer;
g.mouseX = { depart: event.screenX };
g.activeHandle = this;
window.onmousemove = Mixer.handleMove;
window.onmouseup = Mixer.handleUp;
};
Mixer.handleMove = function (event) {
event = event || window.event;
var g = Mixer,
handle = g.activeHandle,
pos = handle.pos,
handles = g.handles,
num = g.num,
slider = g.slider,
proportion = g.proportion,
segments = g.segments,
handleWidth = g.style.handle.width,
swatches = g.swatches,
canvas = g.canvas,
context = g.context,
mixingFunctions = g.mixingFunctions,
limit = {
min: (pos == 0 ? 0 : handles[pos-1].right),
max: (pos == num-2 ? slider.length.total : handles[pos+1].left) -
handleWidth
},
mouseX = g.mouseX;
mouseX.current = event.screenX;
var left = handle.left + mouseX.current - mouseX.depart;
if (left < limit.min) {
left = limit.min;
}
if (left > limit.max) {
left = limit.max;
}
handle.newLeft = left;
segments[pos] = left - limit.min;
context.fillStyle = swatches[pos].css;
context.fillRect(limit.min, 0, segments[pos], canvas.height);
segments[pos+1] = limit.max - left;
context.fillStyle = swatches[pos+1].css;
context.fillRect(left + handleWidth, 0, segments[pos+1], canvas.height);
handle.style.left = left + 'px';
var segmentSpan = segments[pos] + segments[pos+1],
proportionSpan = proportion[pos] + proportion[pos+1];
if (segmentSpan != 0) {
proportion[pos] = Math.round(segments[pos]/segmentSpan * proportionSpan);
proportion[pos+1] = proportionSpan - proportion[pos];
swatches[pos].percent.innerHTML = proportion[pos] + '%';
swatches[pos+1].percent.innerHTML = proportion[pos+1] + '%';
}
g.mixColors();
};
Mixer.handleUp = function (event) {
var g = Mixer,
handle = g.activeHandle;
window.onmousemove = null;
window.onmouseup = null;
handle.left = handle.newLeft;
handle.right = handle.left + g.style.handle.width;
};
Mixer.makeFunctionName = function (title) {
var parts = ['mix'],
tokens = title.split(' ');
for (var i = 0; i < tokens.length; ++i) {
var token = tokens[i];
parts.push(token[0].toUpperCase() + token.substring(1));
}
return parts.join('');
};
Mixer.mixAlphaCompositing = function (swatch, label) {
return function () {
var g = Mixer,
swatches = g.swatches,
proportion = g.proportion,
num = g.num,
mix = {},
subs = ['r', 'g', 'b'];
for (var i = 0; i < subs.length; ++i) {
var x = subs[i];
mix[x] = swatches[0].rgb[x];
}
var alpha = { back: proportion[0]/100 };
for (var pos = 1; pos < num; ++pos) {
var fore = swatches[pos].rgb;
alpha.fore = proportion[pos]/100,
alpha.mix = 1 - (1-alpha.back)*(1-alpha.fore);
if (alpha.mix >= 1.0e-6) {
for (var i = 0; i < subs.length; ++i) {
var x = subs[i];
mix[x] = 255 * (fore[x]/255 * alpha.fore/alpha.mix +
mix[x]/255 * alpha.back*(1-alpha.fore)/alpha.mix);
}
}
alpha.back = alpha.mix;
}
for (var i = 0; i < subs.length; ++i) {
var x = subs[i];
mix[x] = Math.round(mix[x]);
}
var css = g.toCSS(mix);
label.rgb.innerHTML = g.toString(mix);
label.css.innerHTML = css;
swatch.style.backgroundColor = css;
swatch.style.opacity = alpha.mix;
};
};
Mixer.mixWeightedAverage = function (swatch, label) {
return function () {
var g = Mixer,
swatches = g.swatches,
proportion = g.proportion,
num = g.num,
mix = { r: 0, g: 0, b: 0 },
subs = ['r', 'g', 'b'];
for (var pos = 0; pos < num; ++pos) {
for (var i = 0; i < subs.length; ++i) {
var x = subs[i];
mix[x] += proportion[pos]/100 * swatches[pos].rgb[x];
}
}
for (var i = 0; i < subs.length; ++i) {
var x = subs[i];
mix[x] = Math.round(mix[x]);
}
var css = g.toCSS(mix);
label.rgb.innerHTML = g.toString(mix);
label.css.innerHTML = css;
swatch.style.backgroundColor = css;
};
};
Mixer.mixColors = function () {
var g = Mixer,
mixingFunctions = g.mixingFunctions;
for (var i = 0; i < mixingFunctions.length; ++i) {
mixingFunctions[i]();
}
};
Mixer.load = function () {
var g = Mixer,
style = g.style;
// Make color swatches.
var palette = g.palette = document.getElementById('palette'),
names = g.color.names,
swatches = g.swatches = [],
num = g.num = names.length;
for (var i = 0; i < num; ++i) {
var name = names[i],
rgb = g.color.rgb[name],
css = g.toCSS(rgb),
container = g.makeElement('div', 'swatchContainer', '', true),
percent = g.makeElement('div', 'title', '', true),
swatch = g.makeElement('div', 'swatch', '', true);
swatches[i] = { rgb: rgb, css: css, percent: percent };
container.appendChild(percent);
swatch.style.backgroundColor = css;
swatch.style.width = style.swatch.width + 'px';
swatch.style.height = style.swatch.height + 'px';
swatch.style.marginRight = style.swatch.margin.right + 'px';
container.appendChild(swatch);
container.appendChild(g.makeElement('div', 'label', g.toString(rgb), true));
container.appendChild(g.makeElement('div', 'label', css, true));
palette.appendChild(container);
}
var totalWidth = num*style.swatch.width + (num-1)*style.swatch.margin.right;
// Initialize proportions.
var proportion = g.proportion = new Array(num),
each = Math.floor(100/num);
for (var i = 0; i < num-1; ++i) {
proportion[i] = each;
}
proportion[num-1] = 100 - (num-1)*each;
for (var i = 0; i < num; ++i) {
swatches[i].percent.innerHTML = proportion[i] + '%';
}
// Prepare the blended swatches.
var blend = g.blend = { container: document.getElementById('blend') },
mixers = ['alpha compositing', 'weighted average'],
between = (totalWidth - mixers.length*style.swatch.width) /
(mixers.length + 1);
g.makeUnselectable(blend);
blend.container.style.width = totalWidth + 'px';
blend.container.style.height = style.swatch.height + 'px';
g.mixingFunctions = [];
for (var i = 0; i < mixers.length; ++i) {
var mixer = mixers[i],
container = g.makeElement('div', 'swatchContainer', '', true),
title = g.makeElement('div', 'title', mixer, true),
swatch = g.makeElement('div', 'swatch', '', true),
label = {
rgb: g.makeElement('div', 'label', '', true),
css: g.makeElement('div', 'label', '', true)
};
swatch.style.width = style.swatch.width + 'px';
swatch.style.height = style.swatch.height + 'px';
container.style.left = i*style.swatch.width + (i+1)*between + 'px';
container.appendChild(title);
container.appendChild(swatch);
container.appendChild(label.rgb);
container.appendChild(label.css);
blend.container.appendChild(container);
var functionName = g.makeFunctionName(mixer),
mixingFunction = g[functionName](swatch, label);
g.mixingFunctions.push(mixingFunction);
}
// Assemble the slider widget.
var slider = g.slider = document.getElementById('slider');
slider.length = {
total: totalWidth,
free: totalWidth - (num-1)*style.handle.width
};
var segments = g.segments = new Array(num);
var tail = slider.length.free;
for (var i = 0; i < num-1; ++i) {
var current = Math.round(proportion[i]/100*slider.length.free);
segments[i] = current;
tail -= current;
}
segments[num-1] = tail;
slider.style.width = slider.length.total + 'px';
slider.style.height = style.slider.height + 'px';
var canvas = g.canvas = g.makeElement('canvas'),
context = g.context = canvas.getContext('2d');
g.makeUnselectable(slider);
g.makeUnselectable(canvas);
canvas.width = slider.length.total;
canvas.height = style.slider.height;
slider.appendChild(canvas);
var handles = g.handles = new Array(num-1);
var left = 0;
for (var i = 0; i < num; ++i) {
context.fillStyle = swatches[i].css;
context.fillRect(left, 0, segments[i], canvas.height);
if (i == num-1) {
break;
}
var handle = handles[i] = g.makeElement('div', 'handle', '', true);
handle.pos = i;
handle.style.width = style.handle.width + 'px';
handle.style.height = style.handle.height + 'px';
handle.style.top = (style.slider.height - style.handle.height)/2 + 'px';
handle.left = left + segments[i];
handle.style.left = handle.left + 'px';
handle.right = handle.left + style.handle.width;
left = handle.right;
handle.onmousedown = g.handleDown;
slider.appendChild(handle);
}
g.mixColors();
};
window.onload = Mixer.load;
body {
font-family: sans-serif;
color: #444;
}
.unselectable {
-webkit-user-select: none;
-khtml-user-drag: none;
-khtml-user-select: none;
-moz-user-select: none;
-moz-user-select: -moz-none;
-ms-user-select: none;
user-select: none;
}
#blend {
position: relative;
padding: 10px 0 90px;
}
#blend .swatchContainer {
position: absolute;
}
#blend .swatchContainer .title {
font-size: 17px;
padding-bottom: 5px;
}
#slider {
margin: 20px 0;
position: relative;
}
.handle {
position: absolute;
background: #444;
border-radius: 5px;
cursor: pointer;
}
.swatchContainer {
float: left;
}
.swatchContainer .title {
font-size: 25px;
text-align: center;
}
.swatchContainer .label {
font-size: 17px;
}
<div id="blend"></div>
<div id="slider"></div>
<div id="palette"></div>
回答2:
No, you cannot. Here is a link to W3 Css Colors definition which states at the top that only blue, red, and green can be used.
Edit: Ok, so let me clarify. Natively, this is not possible, from my understanding. However, there may be things you can do using Less and Sass that can achieve your desired result. Here is some tutorial regarding Less Color functions. Most interestingly is the part about Mixing colors:
.box.mix {
background-color: mix(@blue, @yellow, 50%);
}
You would need to explore this, however, as I have no experience with it.
回答3:
I wonder if the answer isn't just to 'mix' colors in RGB?
var green = [15, 167, 73];
var blue = [0, 152, 215];
var orange = [243, 123, 38];
var gold = [255, 230, 101];
generateTrueColor(greenRatio, blueRatio, orangeRatio, goldRatio){
for(var i = 0; i < 3, i++){
RGB[i] = greenRatio * green[i] + blueRatio * blue[i] + orangeRatio * orange[i] + goldRatio * gold[i];
}
return RGB;
for(var i = 0; i < 3, i++){
if(RGB[i] > 255) RGB[i] = 255;
}
}
generateTrueColor(1, 0.1, 0.1, 0.1);
回答4:
I'm not sure dividing by 50 is the right/best thing to do, but this pretty much works:
// Note that the results differ from "true" blue, green, orange, and gold; the colors used to represent these are custom versions of those colors, as found on the True Colors website
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace Sandbox
{
public partial class FormTrueColorsMain : Form
{
// Primary constituents of the "True Colors" hues
const int GREEN_RED = 15;
const int GREEN_GREEN = 167;
const int GREEN_BLUE = 73;
const int BLUE_RED = 0;
const int BLUE_GREEN = 152;
const int BLUE_BLUE = 215;
const int ORANGE_RED = 243;
const int ORANGE_GREEN = 123;
const int ORANGE_BLUE = 38;
const int GOLD_RED = 255;
const int GOLD_GREEN = 230;
const int GOLD_BLUE = 101;
Color tcGreen = Color.FromArgb(15, 167, 73);
Color tcGreenComplement = Color.FromArgb(240, 88, 182);
Color tcBlue = Color.FromArgb(0, 152, 215);
Color tcOrange = Color.FromArgb(243, 123, 38);
Color tcGold = Color.FromArgb(255, 230, 101);
public FormTrueColorsMain()
{
InitializeComponent();
InitializeControls();
}
private void InitializeControls()
{
lblGreen.ForeColor = tcGreen;
lblGreen.BackColor = Color.White; // tcGreenComplement;
lblBlue.ForeColor = tcBlue;
lblBlue.BackColor = Color.White;
lblOrange.ForeColor = tcOrange;
lblOrange.BackColor = Color.Black;
lblGold.ForeColor = tcGold;
lblGold.BackColor = Color.Black;
// For these to work, needed to also comment out "Application.EnableVisualStyles();" in Program.cs (see Crispy's answer at http://stackoverflow.com/questions/778678/how-to-change-the-color-of-progressbar-in-c-sharp-net-3-5)
progressBarGreen.ForeColor = tcGreen;
progressBarGreen.Style = System.Windows.Forms.ProgressBarStyle.Continuous;
progressBarBlue.ForeColor = tcBlue;
progressBarBlue.Style = System.Windows.Forms.ProgressBarStyle.Continuous;
progressBarOrange.ForeColor = tcOrange;
progressBarOrange.Style = System.Windows.Forms.ProgressBarStyle.Continuous;
progressBarGold.ForeColor = tcGold;
progressBarGold.Style = System.Windows.Forms.ProgressBarStyle.Continuous;
}
private void button1_Click(object sender, EventArgs e)
{
// If they have already been set (user clicked the arrows rather than entered the value directly), this is moot but it's
// probably not worthwhile checking to see whether their value is 0, and only calling the methods conditionally in that case.
SetGreenProgressBar();
SetBlueProgressBar();
SetOrangeProgressBar();
SetGoldProgressBar();
int greenVal = (int)numericUpDownGreen.Value;
int blueVal = (int)numericUpDownBlue.Value;
int orangeVal = (int)numericUpDownOrange.Value;
int goldVal = (int)numericUpDownGold.Value;
Color trueColor = GetTrueCombinedColor(greenVal, blueVal, orangeVal, goldVal);
pictureBoxTCCombo.BackColor = trueColor;
}
private Color GetTrueCombinedColor(int greenVal, int blueVal, int orangeVal, int goldVal)
{
// tcBlue has no red, so can disregard blueVal for redTotal
int redTotal = ((greenVal * GREEN_RED) + (orangeVal * ORANGE_RED) + (goldVal * GOLD_RED));
int greenTotal = ((greenVal * GREEN_GREEN) + (blueVal * BLUE_GREEN) + (orangeVal * ORANGE_GREEN) + (goldVal * GOLD_GREEN));
int blueTotal = ((greenVal * GREEN_BLUE) + (blueVal * BLUE_BLUE) + (orangeVal * ORANGE_BLUE) + (goldVal * GOLD_BLUE));
int redWeight = redTotal / 50;
int greenWeight = greenTotal / 50;
int blueWeight = blueTotal / 50;
if (redWeight <= 0) redWeight = 1;
if (greenWeight <= 0) greenWeight = 1;
if (blueWeight <= 0) blueWeight = 1;
if (redWeight >= 256) redWeight = 255;
if (greenWeight >= 256) greenWeight = 255;
if (blueWeight >= 256) blueWeight = 255;
Color trueColorCombo = Color.FromArgb(redWeight, greenWeight, blueWeight);
//Color trueColorCombo = Color.FromArgb(redWeight, greenWeight, blueWeight, 10);
return trueColorCombo;
}
private void numericUpDownGreen_ValueChanged(object sender, EventArgs e)
{
SetGreenProgressBar();
}
private void SetGreenProgressBar()
{
progressBarGreen.Value = (int)numericUpDownGreen.Value;
}
private void numericUpDownBlue_ValueChanged(object sender, EventArgs e)
{
SetBlueProgressBar();
}
private void SetBlueProgressBar()
{
progressBarBlue.Value = (int)numericUpDownBlue.Value;
}
private void numericUpDownOrange_ValueChanged(object sender, EventArgs e)
{
SetOrangeProgressBar();
}
private void SetOrangeProgressBar()
{
progressBarOrange.Value = (int)numericUpDownOrange.Value;
}
private void numericUpDownGold_ValueChanged(object sender, EventArgs e)
{
SetGoldProgressBar();
}
private void SetGoldProgressBar()
{
progressBarGold.Value = (int)numericUpDownGold.Value;
}
}
}
Here's what the util looks like:
Cyndi Lauper, eat your heart out!
来源:https://stackoverflow.com/questions/29951130/how-can-i-create-a-color-with-values-for-green-blue-orange-and-gold-only