X-Git-Url: https://adrianiainlam.tk/git/?a=blobdiff_plain;f=dygraph-canvas.js;h=ea488ca53b47050e5afa908063388cb29653a0a9;hb=33b5c4b245bf29f3e908931bf70dde00b5fc8a51;hp=b038fec03d10a503df15c45c8c1a714f6b0695ba;hpb=48fc47867e9796ad49e105546fbb21c5304039c1;p=dygraphs.git diff --git a/dygraph-canvas.js b/dygraph-canvas.js index b038fec..ea488ca 100644 --- a/dygraph-canvas.js +++ b/dygraph-canvas.js @@ -25,7 +25,7 @@ */ /*jshint globalstrict: true */ -/*global Dygraph:false,RGBColorParser:false */ +/*global Dygraph:false */ "use strict"; @@ -52,20 +52,17 @@ var DygraphCanvasRenderer = function(dygraph, element, elementContext, layout) { this.layout = layout; this.element = element; this.elementContext = elementContext; - this.container = this.element.parentNode; this.height = this.element.height; this.width = this.element.width; // --- check whether everything is ok before we return // NOTE(konigsberg): isIE is never defined in this object. Bug of some sort. - if (!this.isIE && !(DygraphCanvasRenderer.isSupported(this.element))) + if (!this.isIE && !(Dygraph.isCanvasSupported(this.element))) throw "Canvas is not supported."; // internal state this.area = layout.getPlotArea(); - this.container.style.position = "relative"; - this.container.style.width = this.width + "px"; // Set up a clipping area for the canvas (and the interaction canvas). // This ensures that we don't overdraw. @@ -89,15 +86,6 @@ var DygraphCanvasRenderer = function(dygraph, element, elementContext, layout) { }; /** - * This just forwards to dygraph.attr_. - * TODO(danvk): remove this? - * @private - */ -DygraphCanvasRenderer.prototype.attr_ = function(name, opt_seriesName) { - return this.dygraph_.attr_(name, opt_seriesName); -}; - -/** * Clears out all chart content and DOM elements. * This is called immediately before render() on every frame, including * during zooms and pans. @@ -127,30 +115,6 @@ DygraphCanvasRenderer.prototype.clear = function() { }; /** - * Checks whether the browser supports the <canvas> tag. - * @private - */ -DygraphCanvasRenderer.isSupported = function(canvasName) { - var canvas = null; - try { - if (typeof(canvasName) == 'undefined' || canvasName === null) { - canvas = document.createElement("canvas"); - } else { - canvas = canvasName; - } - canvas.getContext("2d"); - } - catch (e) { - var ie = navigator.appVersion.match(/MSIE (\d\.\d)/); - var opera = (navigator.userAgent.toLowerCase().indexOf("opera") != -1); - if ((!ie) || (ie[1] < 6) || (opera)) - return false; - return true; - } - return true; -}; - -/** * This method is responsible for drawing everything on the chart, including * lines, error bars, fills and axes. * It is called immediately after clear() on every frame, including during pans @@ -262,18 +226,19 @@ DygraphCanvasRenderer._drawStyledLine = function(e, drawPointCallback, pointSize) { var g = e.dygraph; // TODO(konigsberg): Compute attributes outside this method call. - var stepPlot = g.getOption("stepPlot", e.setName); + var stepPlot = g.getBooleanOption("stepPlot", e.setName); if (!Dygraph.isArrayLike(strokePattern)) { strokePattern = null; } - var drawGapPoints = g.getOption('drawGapEdgePoints', e.setName); + var drawGapPoints = g.getBooleanOption('drawGapEdgePoints', e.setName); var points = e.points; + var setName = e.setName; var iter = Dygraph.createIterator(points, 0, points.length, DygraphCanvasRenderer._getIteratorPredicate( - g.getOption("connectSeparatedPoints"))); // TODO(danvk): per-series? + g.getBooleanOption("connectSeparatedPoints", setName))); var stroking = strokePattern && (strokePattern.length >= 2); @@ -334,6 +299,9 @@ DygraphCanvasRenderer._drawSeries = function(e, point = arr[i]; } + // FIXME: The 'canvasy != canvasy' test here catches NaN values but the test + // doesn't catch Infinity values. Could change this to + // !isFinite(point.canvasy), but I assume it avoids isNaN for performance? if (point.canvasy === null || point.canvasy != point.canvasy) { if (stepPlot && prevCanvasX !== null) { // Draw a horizontal line to the start of the missing data @@ -374,7 +342,7 @@ DygraphCanvasRenderer._drawSeries = function(e, ctx.moveTo(point.canvasx, point.canvasy); } if (drawPoints || isIsolated) { - pointsOnLine.push([point.canvasx, point.canvasy]); + pointsOnLine.push([point.canvasx, point.canvasy, point.idx]); } prevCanvasX = point.canvasx; prevCanvasY = point.canvasy; @@ -398,8 +366,8 @@ DygraphCanvasRenderer._drawPointsOnLine = function( for (var idx = 0; idx < pointsOnLine.length; idx++) { var cb = pointsOnLine[idx]; ctx.save(); - drawPointCallback( - e.dygraph, e.setName, ctx, cb[0], cb[1], color, pointSize); + drawPointCallback.call(e.dygraph, + e.dygraph, e.setName, ctx, cb[0], cb[1], color, pointSize, cb[2]); ctx.restore(); } }; @@ -440,9 +408,10 @@ DygraphCanvasRenderer.prototype._updatePoints = function() { * DygraphCanvasRenderer._updatePoints. * * @param {string=} opt_seriesName when specified, only that series will - * be drawn. (This is used for expedited redrawing with highlightSeriesOpts) - * @param {CanvasRenderingContext2D} opt_ctx when specified, the drawing context. - * However, lines are typically drawn on the object's elementContext. + * be drawn. (This is used for expedited redrawing with highlightSeriesOpts) + * @param {CanvasRenderingContext2D} opt_ctx when specified, the drawing + * context. However, lines are typically drawn on the object's + * elementContext. * @private */ DygraphCanvasRenderer.prototype._renderLineChart = function(opt_seriesName, opt_ctx) { @@ -456,7 +425,7 @@ DygraphCanvasRenderer.prototype._renderLineChart = function(opt_seriesName, opt_ this.colors = this.dygraph_.colorsMap_; // Determine which series have specialized plotters. - var plotter_attr = this.attr_("plotter"); + var plotter_attr = this.dygraph_.getOption("plotter"); var plotters = plotter_attr; if (!Dygraph.isArrayLike(plotters)) { plotters = [plotters]; @@ -465,7 +434,7 @@ DygraphCanvasRenderer.prototype._renderLineChart = function(opt_seriesName, opt_ var setPlotters = {}; // series name -> plotter fn. for (i = 0; i < setNames.length; i++) { setName = setNames[i]; - var setPlotter = this.attr_("plotter", setName); + var setPlotter = this.dygraph_.getOption("plotter", setName); if (setPlotter == plotter_attr) continue; // not specialized. setPlotters[setName] = setPlotter; @@ -547,12 +516,12 @@ DygraphCanvasRenderer._linePlotter = function(e) { // TODO(danvk): Check if there's any performance impact of just calling // getOption() inside of _drawStyledLine. Passing in so many parameters makes // this code a bit nasty. - var borderWidth = g.getOption("strokeBorderWidth", setName); + var borderWidth = g.getNumericOption("strokeBorderWidth", setName); var drawPointCallback = g.getOption("drawPointCallback", setName) || Dygraph.Circles.DEFAULT; var strokePattern = g.getOption("strokePattern", setName); - var drawPoints = g.getOption("drawPoints", setName); - var pointSize = g.getOption("pointSize", setName); + var drawPoints = g.getBooleanOption("drawPoints", setName); + var pointSize = g.getNumericOption("pointSize", setName); if (borderWidth && strokeWidth) { DygraphCanvasRenderer._drawStyledLine(e, @@ -584,23 +553,24 @@ DygraphCanvasRenderer._linePlotter = function(e) { DygraphCanvasRenderer._errorPlotter = function(e) { var g = e.dygraph; var setName = e.setName; - var errorBars = g.getOption("errorBars") || g.getOption("customBars"); + var errorBars = g.getBooleanOption("errorBars") || + g.getBooleanOption("customBars"); if (!errorBars) return; - var fillGraph = g.getOption("fillGraph", setName); + var fillGraph = g.getBooleanOption("fillGraph", setName); if (fillGraph) { - g.warn("Can't use fillGraph option with error bars"); + console.warn("Can't use fillGraph option with error bars"); } var ctx = e.drawingContext; var color = e.color; - var fillAlpha = g.getOption('fillAlpha', setName); - var stepPlot = g.getOption("stepPlot", setName); + var fillAlpha = g.getNumericOption('fillAlpha', setName); + var stepPlot = g.getBooleanOption("stepPlot", setName); var points = e.points; var iter = Dygraph.createIterator(points, 0, points.length, DygraphCanvasRenderer._getIteratorPredicate( - g.getOption("connectSeparatedPoints"))); + g.getBooleanOption("connectSeparatedPoints", setName))); var newYs; @@ -609,7 +579,7 @@ DygraphCanvasRenderer._errorPlotter = function(e) { var prevY = NaN; var prevYs = [-1, -1]; // should be same color as the lines but only 15% opaque. - var rgb = new RGBColorParser(color); + var rgb = Dygraph.toRGB_(color); var err_color = 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + fillAlpha + ')'; ctx.fillStyle = err_color; @@ -685,7 +655,7 @@ DygraphCanvasRenderer._fillPlotter = function(e) { var anySeriesFilled = (function() { for (var i = 0; i < setNames.length; i++) { - if (g.getOption("fillGraph", setNames[i])) return true; + if (g.getBooleanOption("fillGraph", setNames[i])) return true; } return false; })(); @@ -697,20 +667,27 @@ DygraphCanvasRenderer._fillPlotter = function(e) { var sets = e.allSeriesPoints; var setCount = sets.length; - var fillAlpha = g.getOption('fillAlpha'); - var stackedGraph = g.getOption("stackedGraph"); + var fillAlpha = g.getNumericOption('fillAlpha'); + var stackedGraph = g.getBooleanOption("stackedGraph"); var colors = g.getColors(); - var baseline = {}; // for stacked graphs: baseline for filling + // For stacked graphs, track the baseline for filling. + // + // The filled areas below graph lines are trapezoids with two + // vertical edges. The top edge is the line segment being drawn, and + // the baseline is the bottom edge. Each baseline corresponds to the + // top line segment from the previous stacked line. In the case of + // step plots, the trapezoids are rectangles. + var baseline = {}; var currBaseline; var prevStepPlot; // for different line drawing modes (line/step) per series // process sets in reverse order (needed for stacked graphs) for (var setIdx = setCount - 1; setIdx >= 0; setIdx--) { var setName = setNames[setIdx]; - if (!g.getOption('fillGraph', setName)) continue; + if (!g.getBooleanOption('fillGraph', setName)) continue; - var stepPlot = g.getOption('stepPlot', setName); + var stepPlot = g.getBooleanOption('stepPlot', setName); var color = colors[setIdx]; var axis = g.axisPropertiesForSeries(setName); var axisY = 1.0 + axis.minyval * axis.yscale; @@ -721,14 +698,14 @@ DygraphCanvasRenderer._fillPlotter = function(e) { var points = sets[setIdx]; var iter = Dygraph.createIterator(points, 0, points.length, DygraphCanvasRenderer._getIteratorPredicate( - g.getOption("connectSeparatedPoints"))); + g.getBooleanOption("connectSeparatedPoints", setName))); // setup graphics context var prevX = NaN; var prevYs = [-1, -1]; var newYs; // should be same color as the lines but only 15% opaque. - var rgb = new RGBColorParser(color); + var rgb = Dygraph.toRGB_(color); var err_color = 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + fillAlpha + ')'; ctx.fillStyle = err_color; @@ -736,8 +713,11 @@ DygraphCanvasRenderer._fillPlotter = function(e) { var last_x, is_first = true; while (iter.hasNext) { var point = iter.next(); - if (!Dygraph.isOK(point.y)) { + if (!Dygraph.isOK(point.y) && !stepPlot) { prevX = NaN; + if (point.y_stacked !== null && !isNaN(point.y_stacked)) { + baseline[point.canvasx] = area.h * point.y_stacked + area.y; + } continue; } if (stackedGraph) { @@ -774,7 +754,11 @@ DygraphCanvasRenderer._fillPlotter = function(e) { } } else { - newYs = [ point.canvasy, axisY ]; + if (isNaN(point.canvasy) && stepPlot) { + newYs = [ area.y + area.h, axisY ]; + } else { + newYs = [ point.canvasy, axisY ]; + } } if (!isNaN(prevX)) { ctx.moveTo(prevX, prevYs[0]);