1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | /** Markers **/ /** * Formats the marker labels. * @param {Object} obj - Marker value Object {x:..,y:..} * @return {String} Formatted marker string */ (function () { Flotr.defaultMarkerFormatter = function(obj){ return (Math.round(obj.y*100)/100)+''; }; Flotr.addType('markers', { options: { show: false, // => setting to true will show markers, false will hide lineWidth: 1, // => line width of the rectangle around the marker color: '#000000', // => text color fill: false, // => fill or not the marekers' rectangles fillColor: "#FFFFFF", // => fill color fillOpacity: 0.4, // => fill opacity stroke: false, // => draw the rectangle around the markers position: 'ct', // => the markers position (vertical align: b, m, t, horizontal align: l, c, r) verticalMargin: 0, // => the margin between the point and the text. labelFormatter: Flotr.defaultMarkerFormatter, fontSize: Flotr.defaultOptions.fontSize, stacked: false, // => true if markers should be stacked stackingType: 'b', // => define staching behavior, (b- bars like, a - area like) (see Issue 125 for details) horizontal: false // => true if markers should be horizontal (For now only in a case on horizontal stacked bars, stacks should be calculated horizontaly) }, // TODO test stacked markers. stack : { positive : [], negative : [], values : [] }, draw : function (options) { var data = options.data, context = options.context, stack = options.stacked ? options.stack : false, stackType = options.stackingType, stackOffsetNeg, stackOffsetPos, stackOffset, i, x, y, label; context.save(); context.lineJoin = 'round'; context.lineWidth = options.lineWidth; context.strokeStyle = 'rgba(0,0,0,0.5)'; context.fillStyle = options.fillStyle; function stackPos (a, b) { stackOffsetPos = stack.negative[a] || 0; stackOffsetNeg = stack.positive[a] || 0; if (b > 0) { stack.positive[a] = stackOffsetPos + b; return stackOffsetPos + b; } else { stack.negative[a] = stackOffsetNeg + b; return stackOffsetNeg + b; } } for (i = 0; i < data.length; ++i) { x = data[i][0]; y = data[i][1]; if (stack) { if (stackType == 'b') { if (options.horizontal) y = stackPos(y, x); else x = stackPos(x, y); } else if (stackType == 'a') { stackOffset = stack.values[x] || 0; stack.values[x] = stackOffset + y; y = stackOffset + y; } } label = options.labelFormatter({x: x, y: y, index: i, data : data}); this.plot(options.xScale(x), options.yScale(y), label, options); } context.restore(); }, plot: function(x, y, label, options) { var context = options.context; if (isImage(label) && !label.complete) { throw 'Marker image not loaded.'; } else { this._plot(x, y, label, options); } }, _plot: function(x, y, label, options) { var context = options.context, margin = 2, left = x, top = y, dim; if (isImage(label)) dim = {height : label.height, width: label.width}; else dim = options.text.canvas(label); dim.width = Math.floor(dim.width+margin*2); dim.height = Math.floor(dim.height+margin*2); if (options.position.indexOf('c') != -1) left -= dim.width/2 + margin; else if (options.position.indexOf('l') != -1) left -= dim.width; if (options.position.indexOf('m') != -1) top -= dim.height/2 + margin; else if (options.position.indexOf('t') != -1) top -= dim.height + options.verticalMargin; else top += options.verticalMargin; left = Math.floor(left)+0.5; top = Math.floor(top)+0.5; if(options.fill) context.fillRect(left, top, dim.width, dim.height); if(options.stroke) context.strokeRect(left, top, dim.width, dim.height); if (isImage(label)) context.drawImage(label, left+margin, top+margin); else Flotr.drawText(context, label, left+margin, top+margin, {textBaseline: 'top', textAlign: 'left', size: options.fontSize, color: options.color}); } }); function isImage (i) { return typeof i === 'object' && i.constructor && (Image ? true : i.constructor === Image); } })(); |