123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196 |
- "use strict";
- const { Heritage } = require("devtools/client/shared/widgets/view-helpers");
- const { AbstractCanvasGraph } = require("devtools/client/shared/widgets/Graphs");
- // Bar graph constants.
- const GRAPH_DAMPEN_VALUES_FACTOR = 0.9;
- const GRAPH_BACKGROUND_COLOR = "#ddd";
- // px
- const GRAPH_STROKE_WIDTH = 1;
- const GRAPH_STROKE_COLOR = "rgba(255,255,255,0.9)";
- // px
- const GRAPH_HELPER_LINES_DASH = [5];
- const GRAPH_HELPER_LINES_WIDTH = 1;
- const GRAPH_CLIPHEAD_LINE_COLOR = "#fff";
- const GRAPH_SELECTION_LINE_COLOR = "#fff";
- const GRAPH_SELECTION_BACKGROUND_COLOR = "rgba(44,187,15,0.25)";
- const GRAPH_SELECTION_STRIPES_COLOR = "rgba(255,255,255,0.1)";
- const GRAPH_REGION_BACKGROUND_COLOR = "transparent";
- const GRAPH_REGION_STRIPES_COLOR = "rgba(237,38,85,0.2)";
- /**
- * A mountain graph, plotting sets of values as line graphs.
- *
- * @see AbstractCanvasGraph for emitted events and other options.
- *
- * Example usage:
- * let graph = new MountainGraphWidget(node);
- * graph.format = ...;
- * graph.once("ready", () => {
- * graph.setData(src);
- * });
- *
- * The `graph.format` traits are mandatory and will determine how each
- * section of the moutain will be styled:
- * [
- * { color: "#f00", ... },
- * { color: "#0f0", ... },
- * ...
- * { color: "#00f", ... }
- * ]
- *
- * Data source format:
- * [
- * { delta: x1, values: [y11, y12, ... y1n] },
- * { delta: x2, values: [y21, y22, ... y2n] },
- * ...
- * { delta: xm, values: [ym1, ym2, ... ymn] }
- * ]
- * where the [ymn] values is assumed to aready be normalized from [0..1].
- *
- * @param nsIDOMNode parent
- * The parent node holding the graph.
- */
- this.MountainGraphWidget = function (parent, ...args) {
- AbstractCanvasGraph.apply(this, [parent, "mountain-graph", ...args]);
- };
- MountainGraphWidget.prototype = Heritage.extend(AbstractCanvasGraph.prototype, {
- backgroundColor: GRAPH_BACKGROUND_COLOR,
- strokeColor: GRAPH_STROKE_COLOR,
- strokeWidth: GRAPH_STROKE_WIDTH,
- clipheadLineColor: GRAPH_CLIPHEAD_LINE_COLOR,
- selectionLineColor: GRAPH_SELECTION_LINE_COLOR,
- selectionBackgroundColor: GRAPH_SELECTION_BACKGROUND_COLOR,
- selectionStripesColor: GRAPH_SELECTION_STRIPES_COLOR,
- regionBackgroundColor: GRAPH_REGION_BACKGROUND_COLOR,
- regionStripesColor: GRAPH_REGION_STRIPES_COLOR,
- /**
- * List of rules used to style each section of the mountain.
- * @see constructor
- * @type array
- */
- format: null,
- /**
- * Optionally offsets the `delta` in the data source by this scalar.
- */
- dataOffsetX: 0,
- /**
- * Optionally uses this value instead of the last tick in the data source
- * to compute the horizontal scaling.
- */
- dataDuration: 0,
- /**
- * The scalar used to multiply the graph values to leave some headroom
- * on the top.
- */
- dampenValuesFactor: GRAPH_DAMPEN_VALUES_FACTOR,
- /**
- * Renders the graph's background.
- * @see AbstractCanvasGraph.prototype.buildBackgroundImage
- */
- buildBackgroundImage: function () {
- let { canvas, ctx } = this._getNamedCanvas("mountain-graph-background");
- let width = this._width;
- let height = this._height;
- ctx.fillStyle = this.backgroundColor;
- ctx.fillRect(0, 0, width, height);
- return canvas;
- },
- /**
- * Renders the graph's data source.
- * @see AbstractCanvasGraph.prototype.buildGraphImage
- */
- buildGraphImage: function () {
- if (!this.format || !this.format.length) {
- throw new Error("The graph format traits are mandatory to style " +
- "the data source.");
- }
- let { canvas, ctx } = this._getNamedCanvas("mountain-graph-data");
- let width = this._width;
- let height = this._height;
- let totalSections = this.format.length;
- let totalTicks = this._data.length;
- let firstTick = totalTicks ? this._data[0].delta : 0;
- let lastTick = totalTicks ? this._data[totalTicks - 1].delta : 0;
- let duration = this.dataDuration || lastTick;
- let dataScaleX = this.dataScaleX = width / (duration - this.dataOffsetX);
- let dataScaleY = this.dataScaleY = height * this.dampenValuesFactor;
- // Draw the graph.
- let prevHeights = Array.from({ length: totalTicks }).fill(0);
- ctx.globalCompositeOperation = "destination-over";
- ctx.strokeStyle = this.strokeColor;
- ctx.lineWidth = this.strokeWidth * this._pixelRatio;
- for (let section = 0; section < totalSections; section++) {
- ctx.fillStyle = this.format[section].color || "#000";
- ctx.beginPath();
- for (let tick = 0; tick < totalTicks; tick++) {
- let { delta, values } = this._data[tick];
- let currX = (delta - this.dataOffsetX) * dataScaleX;
- let currY = values[section] * dataScaleY;
- let prevY = prevHeights[tick];
- if (delta == firstTick) {
- ctx.moveTo(-GRAPH_STROKE_WIDTH, height);
- ctx.lineTo(-GRAPH_STROKE_WIDTH, height - currY - prevY);
- }
- ctx.lineTo(currX, height - currY - prevY);
- if (delta == lastTick) {
- ctx.lineTo(width + GRAPH_STROKE_WIDTH, height - currY - prevY);
- ctx.lineTo(width + GRAPH_STROKE_WIDTH, height);
- }
- prevHeights[tick] += currY;
- }
- ctx.fill();
- ctx.stroke();
- }
- ctx.globalCompositeOperation = "source-over";
- ctx.lineWidth = GRAPH_HELPER_LINES_WIDTH;
- ctx.setLineDash(GRAPH_HELPER_LINES_DASH);
- // Draw the maximum value horizontal line.
- ctx.beginPath();
- let maximumY = height * this.dampenValuesFactor;
- ctx.moveTo(0, maximumY);
- ctx.lineTo(width, maximumY);
- ctx.stroke();
- // Draw the average value horizontal line.
- ctx.beginPath();
- let averageY = height / 2 * this.dampenValuesFactor;
- ctx.moveTo(0, averageY);
- ctx.lineTo(width, averageY);
- ctx.stroke();
- return canvas;
- }
- });
- module.exports = MountainGraphWidget;
|