diff --git a/API.md b/API.md
index f23f7cc..d6d5879 100644
--- a/API.md
+++ b/API.md
@@ -948,6 +948,50 @@ markings: function (axes) {
}
```
+By default, the markings will be drawn under the grid ticks, if you
+need to draw a marking above the grid ticks, you can specifiy a
+"aboveGrid" attribute and set it to true, e.g.
+
+```js
+markings: [ { xaxis: { from: 1, to: 2 }, yaxis: { from: 1, to: 2 }, aboveGrid: true }, ... ]
+```
+Usually a marking is a regular rectangle, when you want it to have a rounded
+corner, you can specify a "rounded" value to it, e.g.
+
+```js
+markings: [ { xaxis: { from: 1, to: 2 }, yaxis: { from: 1, to: 2 }, aboveGrid: true, rounded: 3 }, ... ]
+```
+
+In case you want to put some texts attaching to markings, you can specifiy
+an "text" object to the marking object, along with a font object as well as
+other 2 attributes (textAlign and textBaseline) to align texts well in the
+marking if you want to give the texts a customized look, e.g.
+
+```js
+markings: [
+ {
+ xaxis: { from: 1, to: 2 },
+ yaxis: { from: 1, to: 2 },
+ aboveGrid: true,
+ color: "#000",
+ rounded: 3,
+ text: "The 1st line. The 2nd line.",
+ textAlign: "center",
+ textBaseline: "middle",
+ font: {
+ color: "#fff",
+ size: "12"
+ }
+ },
+ ...
+]
+```
+
+As you see from the above code example, you can control the look and feel
+of the texts by inline css style, besides this approach, you can also
+customize the look and feel of these texts by defining css rules to the
+class *flot-marking*, which class the texts has.
+
If you set "clickable" to true, the plot will listen for click events
on the plot area and fire a "plotclick" event on the placeholder with
a position and a nearby data item object as parameters. The coordinates
diff --git a/examples/index.html b/examples/index.html
index e91016a..baf636e 100644
--- a/examples/index.html
+++ b/examples/index.html
@@ -68,6 +68,7 @@
This example shows how to add text into markings, and how to draw markings with rounded corner.
+
The chart here is actually a goal ladder map, and it also shows how to use image plugin to draw icons on to your chart (Notice the "You are here" icon on this chart.). If you don't need to draw images/icons onto your chart, you don't need to reference the image plugin.
+
+
+
+
+
+
+
diff --git a/examples/markings/you_are_here.png b/examples/markings/you_are_here.png
new file mode 100644
index 0000000..a90836c
Binary files /dev/null and b/examples/markings/you_are_here.png differ
diff --git a/jquery.flot.js b/jquery.flot.js
index 4b5bd60..674a6c3 100644
--- a/jquery.flot.js
+++ b/jquery.flot.js
@@ -866,7 +866,8 @@ Licensed under the MIT license.
variant: placeholder.css("font-variant"),
weight: placeholder.css("font-weight"),
family: placeholder.css("font-family")
- };
+ },
+ markings;
fontDefaults.lineHeight = fontDefaults.size * 1.15;
@@ -938,6 +939,16 @@ Licensed under the MIT license.
}
}
+ markings = options.grid.markings;
+ if ($.isArray(markings)) {
+ for (i = 0; i < markings.length; ++i) {
+
+ if (markings[i].font) {
+ markings[i].font = $.extend({}, fontDefaults, markings[i].font);
+ }
+ }
+ }
+
// backwards compatibility, to be removed in future
if (options.xaxis.noTicks && options.xaxis.ticks == null) {
options.xaxis.ticks = options.xaxis.noTicks;
@@ -2089,7 +2100,13 @@ Licensed under the MIT license.
ctx.save();
ctx.translate(plotOffset.left, plotOffset.top);
- // draw markings
+ var markingLayer = "flot-markings";
+ surface.removeText(markingLayer);
+
+ // process markings
+ var markingsUnderGrid = [];
+ var markingsAboveGrid = [];
+
var markings = options.grid.markings;
if (markings) {
if ($.isFunction(markings)) {
@@ -2105,62 +2122,17 @@ Licensed under the MIT license.
}
for (i = 0; i < markings.length; ++i) {
- var m = markings[i],
- xrange = extractRange(m, "x"),
- yrange = extractRange(m, "y");
-
- // fill in missing
- if (xrange.from == null) {
- xrange.from = xrange.axis.min;
- }
- if (xrange.to == null) {
- xrange.to = xrange.axis.max;
- }
- if (yrange.from == null) {
- yrange.from = yrange.axis.min;
- }
- if (yrange.to == null) {
- yrange.to = yrange.axis.max;
- }
-
- // clip
- if (xrange.to < xrange.axis.min || xrange.from > xrange.axis.max ||
- yrange.to < yrange.axis.min || yrange.from > yrange.axis.max) {
- continue;
- }
-
- xrange.from = Math.max(xrange.from, xrange.axis.min);
- xrange.to = Math.min(xrange.to, xrange.axis.max);
- yrange.from = Math.max(yrange.from, yrange.axis.min);
- yrange.to = Math.min(yrange.to, yrange.axis.max);
-
- if (xrange.from === xrange.to && yrange.from === yrange.to) {
- continue;
- }
-
- // then draw
- xrange.from = xrange.axis.p2c(xrange.from);
- xrange.to = xrange.axis.p2c(xrange.to);
- yrange.from = yrange.axis.p2c(yrange.from);
- yrange.to = yrange.axis.p2c(yrange.to);
-
- if (xrange.from === xrange.to || yrange.from === yrange.to) {
- // draw line
- ctx.beginPath();
- ctx.strokeStyle = m.color || options.grid.markingsColor;
- ctx.lineWidth = m.lineWidth || options.grid.markingsLineWidth;
- ctx.moveTo(xrange.from, yrange.from);
- ctx.lineTo(xrange.to, yrange.to);
- ctx.stroke();
+ var m = markings[i];
+
+ if (m.aboveGrid) {
+ markingsAboveGrid.push(m);
} else {
- // fill area
- ctx.fillStyle = m.color || options.grid.markingsColor;
- ctx.fillRect(xrange.from, yrange.to,
- xrange.to - xrange.from,
- yrange.from - yrange.to);
+ markingsUnderGrid.push(m);
}
}
}
+
+ drawMarkings(markingsUnderGrid, markingLayer);
// draw the ticks
axes = allAxes();
@@ -2265,6 +2237,7 @@ Licensed under the MIT license.
ctx.stroke();
}
+ drawMarkings(markingsAboveGrid, markingLayer);
// draw border
if (bw) {
@@ -2323,6 +2296,109 @@ Licensed under the MIT license.
ctx.restore();
}
+
+ function drawMarkings(markings, markingLayer) {
+ if (!markings) {
+ return;
+ }
+
+ for (var i = 0; i < markings.length; i++) {
+ drawMarking(markings[i], markingLayer);
+ }
+ }
+
+ function drawMarking(m, markingLayer) {
+ var xrange = extractRange(m, "x"),
+ yrange = extractRange(m, "y");
+
+ // fill in missing
+ if (xrange.from == null) {
+ xrange.from = xrange.axis.min;
+ }
+ if (xrange.to == null) {
+ xrange.to = xrange.axis.max;
+ }
+ if (yrange.from == null) {
+ yrange.from = yrange.axis.min;
+ }
+ if (yrange.to == null) {
+ yrange.to = yrange.axis.max;
+ }
+
+ // clip
+ if (xrange.to < xrange.axis.min || xrange.from > xrange.axis.max ||
+ yrange.to < yrange.axis.min || yrange.from > yrange.axis.max) {
+ return;
+ }
+
+ xrange.from = Math.max(xrange.from, xrange.axis.min);
+ xrange.to = Math.min(xrange.to, xrange.axis.max);
+ yrange.from = Math.max(yrange.from, yrange.axis.min);
+ yrange.to = Math.min(yrange.to, yrange.axis.max);
+
+ if (xrange.from === xrange.to && yrange.from === yrange.to) {
+ return;
+ }
+
+ // then draw
+ xrange.from = xrange.axis.p2c(xrange.from);
+ xrange.to = xrange.axis.p2c(xrange.to);
+ yrange.from = yrange.axis.p2c(yrange.from);
+ yrange.to = yrange.axis.p2c(yrange.to);
+
+ if (xrange.from === xrange.to || yrange.from === yrange.to) {
+ // draw line
+ ctx.beginPath();
+ ctx.strokeStyle = m.color || options.grid.markingsColor;
+ ctx.lineWidth = m.lineWidth || options.grid.markingsLineWidth;
+ ctx.moveTo(xrange.from, yrange.from);
+ ctx.lineTo(xrange.to, yrange.to);
+ ctx.stroke();
+ } else {
+ // fill area
+ ctx.fillStyle = m.color || options.grid.markingsColor;
+
+ if (!m.rounded) {
+ ctx.fillRect(xrange.from, yrange.to,
+ xrange.to - xrange.from,
+ yrange.from - yrange.to);
+ } else {
+ roundRect(ctx, xrange.from, yrange.to, xrange.to - xrange.from, yrange.from - yrange.to, m.rounded);
+ ctx.fill();
+ }
+ }
+
+ if (m.text) {
+ // left aligned horizontal position:
+ var xPos = xrange.from + plotOffset.left;
+ // top baselined vertical position:
+ var yPos = (yrange.to + plotOffset.top);
+
+ if (!!m.textAlign) {
+ switch (m.textAlign.toLowerCase()) {
+ case "right":
+ xPos = xrange.to + plotOffset.left;
+ break;
+ case "center":
+ xPos = (xrange.from + plotOffset.left + xrange.to + plotOffset.left) / 2;
+ break;
+ }
+ }
+
+ if (!!m.textBaseline) {
+ switch (m.textBaseline.toLowerCase()) {
+ case "bottom":
+ yPos = (yrange.from + plotOffset.top);
+ break;
+ case "middle":
+ yPos = (yrange.from + plotOffset.top + yrange.to + plotOffset.top) / 2;
+ break;
+ }
+ }
+
+ surface.addText(markingLayer, xPos, yPos, m.text, m.font || "flot-marking", 0, null, m.textAlign, m.textBaseline);
+ }
+ }
function drawAxisLabels() {
@@ -3405,4 +3481,32 @@ Licensed under the MIT license.
return base * Math.floor(n / base);
}
+ // Draw a rectangle with rounded corner on the canvas.
+ //
+ // @param {CanvasRenderingContext2D} ctx The canvas 2D context.
+ // @param {number} x The x-axis coordinate of the upper left corner of
+ // the rectangle to be drawn.
+ // @param {number} y The y-axis coordinate of the upper left corner of
+ // the rectangle to be drawn.
+ // @param {number} width The width of the rectangle to be drawn.
+ // @param {number} height The height of the rectangle to be drawn.
+ // @param {number} radius The radius of the corner of the rectangle
+ // to be drawn.
+ function roundRect(ctx, x, y, width, height, radius) {
+ var r = x + width;
+ var b = y + height;
+ ctx.beginPath();
+ ctx.moveTo(x + radius, y);
+ ctx.lineTo(r - radius, y);
+ ctx.quadraticCurveTo(r, y, r, y + radius);
+ ctx.lineTo(r, y + height - radius);
+ ctx.quadraticCurveTo(r, b, r - radius, b);
+ ctx.lineTo(x + radius, b);
+ ctx.quadraticCurveTo(x, b, x, b - radius);
+ ctx.lineTo(x, y + radius);
+ ctx.quadraticCurveTo(x, y, x + radius, y);
+ ctx.closePath();
+ return ctx;
+ }
+
})(jQuery);