From 337a79bca0075c95592195ed4834e28a88afb508 Mon Sep 17 00:00:00 2001 From: Dan Vanderkam Date: Tue, 21 Apr 2015 14:41:39 -0400 Subject: [PATCH] Parse rgba colors (fixes #517) --- auto_tests/tests/error_bars.js | 47 ++++++++++++++++++++++++++++++++++++++++++ auto_tests/tests/utils_test.js | 2 ++ src/dygraph-utils.js | 37 ++++++++++++++++++++++++++------- 3 files changed, 78 insertions(+), 8 deletions(-) diff --git a/auto_tests/tests/error_bars.js b/auto_tests/tests/error_bars.js index 2fd5ae4..e04acde 100644 --- a/auto_tests/tests/error_bars.js +++ b/auto_tests/tests/error_bars.js @@ -144,6 +144,53 @@ it('testErrorBarsCorrectColors', function() { assert.deepEqual([0, 255, 0, 38], Util.samplePixel(g.hidden_, 200, 225)); }); +// Regression test for https://github.com/danvk/dygraphs/issues/517 +// This verifies that the error bars have alpha=fillAlpha, even if the series +// color has its own alpha value. +it('testErrorBarsForAlphaSeriesCorrectColors', function() { + var data = [ + [0, [100, 50]], + [2, [100, 50]] + ]; + + var opts = { + errorBars: true, + sigma: 1.0, + fillAlpha: 0.15, + strokeWidth: 10, + colors: ['rgba(0, 255, 0, 0.5)'], + axes : { + x : { + drawGrid: false, + drawAxis: false, + }, + y : { + drawGrid: false, + drawAxis: false, + } + }, + width: 400, + height: 300, + valueRange: [0, 300], + labels: ['X', 'Y'] + }; + var graph = document.getElementById("graph"); + var g = new Dygraph(graph, data, opts); + + // y-pixels (0=top, 299=bottom) + // 0-148: empty (white) + // 149-198: Y error bar + // 199: Y center line + // 200-248: Y error bar + // 249-299: empty (white) + + // 38 = 255 * 0.15 (fillAlpha) + // 146 = 255 * (0.15 * 0.5 + 1 * 0.5) (fillAlpha from error bar + alpha from series line) + assert.deepEqual([0, 255, 0, 38], Util.samplePixel(g.hidden_, 1, 175)); + assert.deepEqual([0, 255, 0, 146], Util.samplePixel(g.hidden_, 200, 199)); + assert.deepEqual([0, 255, 0, 38], Util.samplePixel(g.hidden_, 1, 225)); +}); + // Regression test for http://code.google.com/p/dygraphs/issues/detail?id=392 it('testRollingAveragePreservesNaNs', function() { diff --git a/auto_tests/tests/utils_test.js b/auto_tests/tests/utils_test.js index 765ed1e..e8ebe5a 100644 --- a/auto_tests/tests/utils_test.js +++ b/auto_tests/tests/utils_test.js @@ -175,6 +175,8 @@ it('testToRGB', function() { assert.deepEqual({r: 255, g: 200, b: 150}, Dygraph.toRGB_('rgb(255,200,150)')); assert.deepEqual({r: 255, g: 200, b: 150}, Dygraph.toRGB_('#FFC896')); assert.deepEqual({r: 255, g: 0, b: 0}, Dygraph.toRGB_('red')); + assert.deepEqual({r: 255, g: 200, b: 150, a: 0.6}, + Dygraph.toRGB_('rgba(255, 200, 150, 0.6)')); }); it('testIsPixelChangingOptionList', function() { diff --git a/src/dygraph-utils.js b/src/dygraph-utils.js index d222ec3..a960ba7 100644 --- a/src/dygraph-utils.js +++ b/src/dygraph-utils.js @@ -1068,27 +1068,48 @@ Dygraph.pow = function(base, exp) { return Math.pow(base, exp); }; +var RGBA_RE = /^rgba?\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})(?:,\s*([01](?:\.\d+)?))?\)$/; + +/** + * Helper for Dygraph.toRGB_ which parses strings of the form: + * rgb(123, 45, 67) + * rgba(123, 45, 67, 0.5) + * @return parsed {r,g,b,a?} tuple or null. + */ +function parseRGBA(rgbStr) { + var bits = RGBA_RE.exec(rgbStr); + if (!bits) return null; + var r = parseInt(bits[1], 10), + g = parseInt(bits[2], 10), + b = parseInt(bits[3], 10); + if (bits[4]) { + return {r: r, g: g, b: b, a: parseFloat(bits[4])}; + } else { + return {r: r, g: g, b: b}; + } +} + /** * Converts any valid CSS color (hex, rgb(), named color) to an RGB tuple. * * @param {!string} colorStr Any valid CSS color string. - * @return {{r:number,g:number,b:number}} Parsed RGB tuple. + * @return {{r:number,g:number,b:number,a:number?}} Parsed RGB tuple. * @private */ Dygraph.toRGB_ = function(colorStr) { - // TODO(danvk): cache color parses to avoid repeated DOM manipulation. + // Strategy: First try to parse colorStr directly. This is fast & avoids DOM + // manipulation. If that fails (e.g. for named colors like 'red'), then + // create a hidden DOM element and parse its computed color. + var rgb = parseRGBA(colorStr); + if (rgb) return rgb; + var div = document.createElement('div'); div.style.backgroundColor = colorStr; div.style.visibility = 'hidden'; document.body.appendChild(div); var rgbStr = window.getComputedStyle(div, null).backgroundColor; document.body.removeChild(div); - var bits = /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/.exec(rgbStr); - return { - r: parseInt(bits[1], 10), - g: parseInt(bits[2], 10), - b: parseInt(bits[3], 10) - }; + return parseRGBA(rgbStr); }; /** -- 2.7.4