diff --git a/docs/00-Getting-Started.md b/docs/00-Getting-Started.md index 74287a94c..7cb1097aa 100644 --- a/docs/00-Getting-Started.md +++ b/docs/00-Getting-Started.md @@ -132,6 +132,9 @@ Chart.defaults.global = { // Boolean - Determines whether to draw tooltips on the canvas or not showTooltips: true, + // Function - Determines whether to execute the externalTooltips function instead of drawing the built in tooltips (See [Advanced - External Tooltips](#advanced-usage-external-tooltips)) + externalTooltips: false, + // Array - Array of string names to attach tooltip events tooltipEvents: ["mousemove", "touchstart", "touchmove"], diff --git a/docs/06-Advanced.md b/docs/06-Advanced.md index f9d696da2..d6cbf3a03 100644 --- a/docs/06-Advanced.md +++ b/docs/06-Advanced.md @@ -70,6 +70,39 @@ myLineChart.generateLegend(); // => returns HTML string of a legend for this chart ``` +### External Tooltips + +You can enable external tooltips in the global or chart configuration like so: + +```javascript +var myPieChart = new Chart(ctx).Pie(data, { + externalTooltips: function(tooltip) { + + // tooltip will be false if tooltip is not visible or should be hidden + if (!tooltip) { + return; + } + + // Otherwise, tooltip will be an object with all tooltip properties like: + + // tooltip.caretHeight + // tooltip.caretPadding + // tooltip.chart + // tooltip.cornerRadius + // tooltip.fillColor + // tooltip.font... + // tooltip.text + // tooltip.x + // tooltip.y + // etc... + + }; +}); +``` + +See files `sample/pie-externalTooltips.html` and `sample/line-externalTooltips.html` for examples on how to get started. + + ### Writing new chart types Chart.js 1.0 has been rewritten to provide a platform for developers to create their own custom chart types, and be able to share and utilise them through the Chart.js API. diff --git a/samples/line-externalTooltips.html b/samples/line-externalTooltips.html new file mode 100644 index 000000000..67eab85ee --- /dev/null +++ b/samples/line-externalTooltips.html @@ -0,0 +1,129 @@ + + + + + Line Chart with Custom Tooltips + + + + + + + +
+ +
+
+ +
+ +
+ + + + + + diff --git a/samples/pie-externalTooltips.html b/samples/pie-externalTooltips.html new file mode 100644 index 000000000..c126c640f --- /dev/null +++ b/samples/pie-externalTooltips.html @@ -0,0 +1,156 @@ + + + + + Pie Chart with Custom Tooltips + + + + + + + +
+ +
+
+ +
+ +
+ + + + + + diff --git a/src/Chart.Core.js b/src/Chart.Core.js index 03b99b45a..07c63c4d5 100755 --- a/src/Chart.Core.js +++ b/src/Chart.Core.js @@ -98,6 +98,9 @@ // Boolean - Determines whether to draw tooltips on the canvas or not - attaches events to touchmove & mousemove showTooltips: true, + // Boolean - Determines whether to draw built-in tooltip or call custom tooltip function + externalTooltips: false, + // Array - Array of string names to attach tooltip events tooltipEvents: ["mousemove", "touchstart", "touchmove", "mouseout"], @@ -895,6 +898,9 @@ this.activeElements = ChartElements; } this.draw(); + if(this.options.externalTooltips){ + this.options.externalTooltips(false); + } if (ChartElements.length > 0){ // If we have multiple datasets, show a MultiTooltip for all of the data points at that index if (this.datasets && this.datasets.length > 1) { @@ -975,7 +981,8 @@ legendColorBackground : this.options.multiTooltipKeyBackground, title: ChartElements[0].label, chart: this.chart, - ctx: this.chart.ctx + ctx: this.chart.ctx, + external: this.options.externalTooltips }).draw(); } else { @@ -994,7 +1001,8 @@ caretHeight: this.options.tooltipCaretSize, cornerRadius: this.options.tooltipCornerRadius, text: template(this.options.tooltipTemplate, Element), - chart: this.chart + chart: this.chart, + external: this.options.externalTooltips }).draw(); }, this); } @@ -1250,7 +1258,7 @@ this.yAlign = "above"; //Distance between the actual element.y position and the start of the tooltip caret - var caretPadding = 2; + var caretPadding = this.caretPadding = 2; var tooltipWidth = ctx.measureText(this.text).width + 2*this.xPadding, tooltipRectHeight = this.fontSize + 2*this.yPadding, @@ -1272,47 +1280,53 @@ ctx.fillStyle = this.fillColor; - switch(this.yAlign) - { - case "above": - //Draw a caret above the x/y - ctx.beginPath(); - ctx.moveTo(this.x,this.y - caretPadding); - ctx.lineTo(this.x + this.caretHeight, this.y - (caretPadding + this.caretHeight)); - ctx.lineTo(this.x - this.caretHeight, this.y - (caretPadding + this.caretHeight)); - ctx.closePath(); - ctx.fill(); - break; - case "below": - tooltipY = this.y + caretPadding + this.caretHeight; - //Draw a caret below the x/y - ctx.beginPath(); - ctx.moveTo(this.x, this.y + caretPadding); - ctx.lineTo(this.x + this.caretHeight, this.y + caretPadding + this.caretHeight); - ctx.lineTo(this.x - this.caretHeight, this.y + caretPadding + this.caretHeight); - ctx.closePath(); - ctx.fill(); - break; + // Custom Tooltips + if(this.external){ + this.external(this); } + else{ + switch(this.yAlign) + { + case "above": + //Draw a caret above the x/y + ctx.beginPath(); + ctx.moveTo(this.x,this.y - caretPadding); + ctx.lineTo(this.x + this.caretHeight, this.y - (caretPadding + this.caretHeight)); + ctx.lineTo(this.x - this.caretHeight, this.y - (caretPadding + this.caretHeight)); + ctx.closePath(); + ctx.fill(); + break; + case "below": + tooltipY = this.y + caretPadding + this.caretHeight; + //Draw a caret below the x/y + ctx.beginPath(); + ctx.moveTo(this.x, this.y + caretPadding); + ctx.lineTo(this.x + this.caretHeight, this.y + caretPadding + this.caretHeight); + ctx.lineTo(this.x - this.caretHeight, this.y + caretPadding + this.caretHeight); + ctx.closePath(); + ctx.fill(); + break; + } - switch(this.xAlign) - { - case "left": - tooltipX = this.x - tooltipWidth + (this.cornerRadius + this.caretHeight); - break; - case "right": - tooltipX = this.x - (this.cornerRadius + this.caretHeight); - break; + switch(this.xAlign) + { + case "left": + tooltipX = this.x - tooltipWidth + (this.cornerRadius + this.caretHeight); + break; + case "right": + tooltipX = this.x - (this.cornerRadius + this.caretHeight); + break; + } + + drawRoundedRectangle(ctx,tooltipX,tooltipY,tooltipWidth,tooltipRectHeight,this.cornerRadius); + + ctx.fill(); + + ctx.fillStyle = this.textColor; + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + ctx.fillText(this.text, tooltipX + tooltipWidth/2, tooltipY + tooltipRectHeight/2); } - - drawRoundedRectangle(ctx,tooltipX,tooltipY,tooltipWidth,tooltipRectHeight,this.cornerRadius); - - ctx.fill(); - - ctx.fillStyle = this.textColor; - ctx.textAlign = "center"; - ctx.textBaseline = "middle"; - ctx.fillText(this.text, tooltipX + tooltipWidth/2, tooltipY + tooltipRectHeight/2); } }); @@ -1366,36 +1380,42 @@ }, draw : function(){ - drawRoundedRectangle(this.ctx,this.x,this.y - this.height/2,this.width,this.height,this.cornerRadius); - var ctx = this.ctx; - ctx.fillStyle = this.fillColor; - ctx.fill(); - ctx.closePath(); + // Custom Tooltips + if(this.external){ + this.external(this); + } + else{ + drawRoundedRectangle(this.ctx,this.x,this.y - this.height/2,this.width,this.height,this.cornerRadius); + var ctx = this.ctx; + ctx.fillStyle = this.fillColor; + ctx.fill(); + ctx.closePath(); - ctx.textAlign = "left"; - ctx.textBaseline = "middle"; - ctx.fillStyle = this.titleTextColor; - ctx.font = this.titleFont; + ctx.textAlign = "left"; + ctx.textBaseline = "middle"; + ctx.fillStyle = this.titleTextColor; + ctx.font = this.titleFont; - ctx.fillText(this.title,this.x + this.xPadding, this.getLineHeight(0)); + ctx.fillText(this.title,this.x + this.xPadding, this.getLineHeight(0)); - ctx.font = this.font; - helpers.each(this.labels,function(label,index){ - ctx.fillStyle = this.textColor; - ctx.fillText(label,this.x + this.xPadding + this.fontSize + 3, this.getLineHeight(index + 1)); + ctx.font = this.font; + helpers.each(this.labels,function(label,index){ + ctx.fillStyle = this.textColor; + ctx.fillText(label,this.x + this.xPadding + this.fontSize + 3, this.getLineHeight(index + 1)); - //A bit gnarly, but clearing this rectangle breaks when using explorercanvas (clears whole canvas) - //ctx.clearRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize); - //Instead we'll make a white filled block to put the legendColour palette over. + //A bit gnarly, but clearing this rectangle breaks when using explorercanvas (clears whole canvas) + //ctx.clearRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize); + //Instead we'll make a white filled block to put the legendColour palette over. - ctx.fillStyle = this.legendColorBackground; - ctx.fillRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize); + ctx.fillStyle = this.legendColorBackground; + ctx.fillRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize); - ctx.fillStyle = this.legendColors[index].fill; - ctx.fillRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize); + ctx.fillStyle = this.legendColors[index].fill; + ctx.fillRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize); - },this); + },this); + } } });