Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,7 @@ tinycolor("#f00").complement().toHexString(); // "#00ffff"
```js
tinycolor.equals(color1, color2)
tinycolor.mix(color1, color2, amount = 50)
tinycolor.alphaBlend('#f008', '#0f08') // #ae5100
```
### random

Expand Down
14 changes: 14 additions & 0 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,12 @@ test("isReadable", function() {
ok(tinycolor.isReadable("#db91b8", "#2e0c3a",{level:"AA",size:"large"}), "readable - AA large");
ok(tinycolor.isReadable("#db91b8", "#2e0c3a",{level:"AAA",size:"small"}), "readable - AAA small");
ok(tinycolor.isReadable("#db91b8", "#2e0c3a",{level:"AAA",size:"large"}), "readable - AAA large");

// transparency
// "#0009" as foreground: contrast ratio 5.74
// "#0009" as background: contrast ration between 5.74 and 21
ok(!tinycolor.isReadable("#fff", "#0009",{level:"AAA",size:"small"}), "not readable - transparency");
ok(tinycolor.isReadable("#fff", "#0009",{level:"AA",size:"small"}), "readable - transparency");
});

test("readability", function() {
Expand Down Expand Up @@ -750,6 +756,14 @@ test("Mix", function () {
}
});

test("AlphaBlend", function () {
equal(tinycolor.alphaBlend('#fff', '#000').toHex(), 'ffffff', "Blending with solid foreground returns foreground");
equal(tinycolor.alphaBlend('#fff0', '#000').toHex(), '000000', "Blending with full transparent foreground returns background");
equal(tinycolor.alphaBlend('#f008', '#0f0').toHex(), '887700', "Blending with solid background works");
equal(tinycolor.alphaBlend('#f008', '#0f08').toHex(), 'ae5100', "Blending works");
equal(tinycolor.alphaBlend('#fff0', '#0000').toHex(), '000000', "Blending returns background if both are fully transparent");
});

// The combination tests need to be expanded further
module("Combinations");

Expand Down
48 changes: 45 additions & 3 deletions tinycolor.js
Original file line number Diff line number Diff line change
Expand Up @@ -716,17 +716,59 @@ tinycolor.mix = function(color1, color2, amount) {
return tinycolor(rgba);
};

tinycolor.alphaBlend = function(foreground, background) {
var c1 = tinycolor(foreground);
var a1 = c1.getAlpha();
var rgb1 = c1.toRgb();

var c2 = tinycolor(background);
var a2 = c2.getAlpha();
var rgb2 = c2.toRgb();

if (a1 === 0 && a2 === 0) {
return c2;
}

var alpha = a1 + (1 - a1) * a2;

return tinycolor({
r: (a1 * rgb1.r + (1 - a1) * a2 * rgb2.r) / alpha,
g: (a1 * rgb1.g + (1 - a1) * a2 * rgb2.g) / alpha,
b: (a1 * rgb1.b + (1 - a1) * a2 * rgb2.b) / alpha,
a: alpha
});
};

// Readability Functions
// ---------------------
// <http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef (WCAG Version 2)

function _readability (foreground, background) {
var c1 = tinycolor.alphaBlend(foreground, background);
var c2 = tinycolor(background);
return (Math.max(c1.getLuminance(),c2.getLuminance())+0.05) / (Math.min(c1.getLuminance(),c2.getLuminance())+0.05);
}

function _readability_minimal (foreground, background) {
var fg = tinycolor(foreground);
var bgOnBlack = tinycolor.alphaBlend(background, '#000');
var bgOnWhite = tinycolor.alphaBlend(background, '#fff');

if (bgOnWhite.getLuminance() < fg.getLuminance()) {
return _readability(foreground, bgOnWhite);
} else if (bgOnBlack.getLuminance() > fg.getLuminance()) {
return _readability(foreground, bgOnBlack);
} else {
return 1;
}
}

// `contrast`
// Analyze the 2 colors and returns the color contrast defined by (WCAG Version 2)
tinycolor.readability = function(color1, color2) {
var c1 = tinycolor(color1);
var c2 = tinycolor(color2);
return (Math.max(c1.getLuminance(),c2.getLuminance())+0.05) / (Math.min(c1.getLuminance(),c2.getLuminance())+0.05);
var r1 = _readability_minimal(color1, color2);
var r2 = _readability_minimal(color2, color1);
return (r1 + r2) / 2;
};

// `isReadable`
Expand Down