X-Git-Url: https://adrianiainlam.tk/git/?a=blobdiff_plain;f=dygraph-canvas.js;h=6bf5fbcb457e187bb089ce0b93f395e1d9d6423f;hb=ebf77a9fa6953523975f7ee608a05e1c66c43e47;hp=99ad493962b183b0a674367ec4ede451775fec65;hpb=12b879f424bcdd49822f33378c740063370bb4be;p=dygraphs.git diff --git a/dygraph-canvas.js b/dygraph-canvas.js index 99ad493..6bf5fbc 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"; @@ -39,8 +39,8 @@ * The chart canvas has already been created by the Dygraph object. The * renderer simply gets a drawing context. * - * @param {Dyraph} dygraph The chart to which this renderer belongs. - * @param {Canvas} element The <canvas> DOM element on which to draw. + * @param {Dygraph} dygraph The chart to which this renderer belongs. + * @param {HTMLCanvasElement} element The <canvas> DOM element on which to draw. * @param {CanvasRenderingContext2D} elementContext The drawing context. * @param {DygraphLayout} layout The chart's DygraphLayout object. * @@ -58,7 +58,8 @@ var DygraphCanvasRenderer = function(dygraph, element, elementContext, layout) { this.width = this.element.width; // --- check whether everything is ok before we return - if (!this.isIE && !(DygraphCanvasRenderer.isSupported(this.element))) + // NOTE(konigsberg): isIE is never defined in this object. Bug of some sort. + if (!this.isIE && !(Dygraph.isCanvasSupported(this.element))) throw "Canvas is not supported."; // internal state @@ -88,15 +89,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. @@ -126,30 +118,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 @@ -261,18 +229,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); @@ -333,6 +302,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 @@ -373,7 +345,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,7 +370,7 @@ DygraphCanvasRenderer._drawPointsOnLine = function( var cb = pointsOnLine[idx]; ctx.save(); drawPointCallback( - e.dygraph, e.setName, ctx, cb[0], cb[1], color, pointSize); + e.dygraph, e.setName, ctx, cb[0], cb[1], color, pointSize, cb[2]); ctx.restore(); } }; @@ -433,14 +405,16 @@ DygraphCanvasRenderer.prototype._updatePoints = function() { /** * Add canvas Actually draw the lines chart, including error bars. - * If opt_seriesName is specified, only that series will be drawn. - * (This is used for expedited redrawing with highlightSeriesOpts) - * Lines are typically drawn in the non-interactive dygraph canvas. If opt_ctx - * is specified, they can be drawn elsewhere. * * This function can only be called if DygraphLayout's points array has been * updated with canvas{x,y} attributes, i.e. by * 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. * @private */ DygraphCanvasRenderer.prototype._renderLineChart = function(opt_seriesName, opt_ctx) { @@ -454,7 +428,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]; @@ -463,7 +437,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; @@ -545,12 +519,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, @@ -582,23 +556,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"); + Dygraph.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; @@ -607,7 +582,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; @@ -683,7 +658,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; })(); @@ -695,20 +670,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; @@ -719,14 +701,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,6 +718,9 @@ DygraphCanvasRenderer._fillPlotter = function(e) { var point = iter.next(); if (!Dygraph.isOK(point.y)) { prevX = NaN; + if (point.y_stacked !== null && !isNaN(point.y_stacked)) { + baseline[point.canvasx] = area.h * point.y_stacked + area.y; + } continue; } if (stackedGraph) {