External Tooltips, Documentation, and Samples

This commit is contained in:
Tanner Linsley 2014-12-30 10:51:10 -07:00
parent 7e97aa3300
commit 8c1e9588a1
5 changed files with 404 additions and 63 deletions

View File

@ -132,6 +132,9 @@ Chart.defaults.global = {
// Boolean - Determines whether to draw tooltips on the canvas or not // Boolean - Determines whether to draw tooltips on the canvas or not
showTooltips: true, 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 // Array - Array of string names to attach tooltip events
tooltipEvents: ["mousemove", "touchstart", "touchmove"], tooltipEvents: ["mousemove", "touchstart", "touchmove"],

View File

@ -70,6 +70,39 @@ myLineChart.generateLegend();
// => returns HTML string of a legend for this chart // => 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 ### 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. 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.

View File

@ -0,0 +1,129 @@
<!doctype html>
<html>
<head>
<title>Line Chart with Custom Tooltips</title>
<script src="../Chart.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<style>
#canvas-holder1 {
width: 300px;
margin: 20px auto;
}
#canvas-holder2 {
width: 50%;
margin: 20px 25%;
}
#chartjs-tooltip {
opacity: 1;
position: absolute;
background: rgba(0, 0, 0, .7);
color: white;
padding: 3px;
border-radius: 3px;
-webkit-transition: all .1s ease;
transition: all .1s ease;
pointer-events: none;
-webkit-transform: translate(-50%, 0);
transform: translate(-50%, 0);
}
.chartjs-tooltip-key{
display:inline-block;
width:10px;
height:10px;
}
</style>
</head>
<body>
<div id="canvas-holder1">
<canvas id="chart1" width="300" height="30" />
</div>
<div id="canvas-holder2">
<canvas id="chart2" width="450" height="600" />
</div>
<div id="chartjs-tooltip"></div>
<script>
Chart.defaults.global.pointHitDetectionRadius = 1;
Chart.defaults.global.externalTooltips = function(tooltip) {
var tooltipEl = $('#chartjs-tooltip');
if (!tooltip) {
tooltipEl.css({
opacity: 0
});
return;
}
tooltipEl.removeClass('above below');
tooltipEl.addClass(tooltip.yAlign);
var innerHtml = '';
for (var i = tooltip.labels.length - 1; i >= 0; i--) {
innerHtml += [
'<div class="chartjs-tooltip-section">',
' <span class="chartjs-tooltip-key" style="background-color:' + tooltip.legendColors[i].fill + '"></span>',
' <span class="chartjs-tooltip-value">' + tooltip.labels[i] + '</span>',
'</div>'
].join('');
}
tooltipEl.html(innerHtml);
tooltipEl.css({
opacity: 1,
left: tooltip.chart.canvas.offsetLeft + tooltip.x + 'px',
top: tooltip.chart.canvas.offsetTop + tooltip.y + 'px',
fontFamily: tooltip.fontFamily,
fontSize: tooltip.fontSize,
fontStyle: tooltip.fontStyle,
});
};
var randomScalingFactor = function() {
return Math.round(Math.random() * 100);
};
var lineChartData = {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [{
label: "My First dataset",
fillColor: "rgba(220,220,220,0.2)",
strokeColor: "rgba(220,220,220,1)",
pointColor: "rgba(220,220,220,1)",
pointStrokeColor: "#fff",
pointHighlightFill: "#fff",
pointHighlightStroke: "rgba(220,220,220,1)",
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
}, {
label: "My Second dataset",
fillColor: "rgba(151,187,205,0.2)",
strokeColor: "rgba(151,187,205,1)",
pointColor: "rgba(151,187,205,1)",
pointStrokeColor: "#fff",
pointHighlightFill: "#fff",
pointHighlightStroke: "rgba(151,187,205,1)",
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
}]
};
window.onload = function() {
var ctx1 = document.getElementById("chart1").getContext("2d");
window.myLine = new Chart(ctx1).Line(lineChartData, {
showScale: false,
pointDot : true,
responsive: true
});
var ctx2 = document.getElementById("chart2").getContext("2d");
window.myLine = new Chart(ctx2).Line(lineChartData, {
responsive: true
});
};
</script>
</body>
</html>

View File

@ -0,0 +1,156 @@
<!doctype html>
<html>
<head>
<title>Pie Chart with Custom Tooltips</title>
<script src="../Chart.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<style>
#canvas-holder {
width: 100%;
margin-top: 50px;
text-align: center;
}
#chartjs-tooltip {
opacity: 1;
position: absolute;
background: rgba(0, 0, 0, .7);
color: white;
padding: 3px;
border-radius: 3px;
-webkit-transition: all .1s ease;
transition: all .1s ease;
pointer-events: none;
-webkit-transform: translate(-50%, 0);
transform: translate(-50%, 0);
}
#chartjs-tooltip.below {
-webkit-transform: translate(-50%, 0);
transform: translate(-50%, 0);
}
#chartjs-tooltip.below:before {
border: solid;
border-color: #111 transparent;
border-color: rgba(0, 0, 0, .8) transparent;
border-width: 0 8px 8px 8px;
bottom: 1em;
content: "";
display: block;
left: 50%;
position: absolute;
z-index: 99;
-webkit-transform: translate(-50%, -100%);
transform: translate(-50%, -100%);
}
#chartjs-tooltip.above {
-webkit-transform: translate(-50%, -100%);
transform: translate(-50%, -100%);
}
#chartjs-tooltip.above:before {
border: solid;
border-color: #111 transparent;
border-color: rgba(0, 0, 0, .8) transparent;
border-width: 8px 8px 0 8px;
bottom: 1em;
content: "";
display: block;
left: 50%;
top: 100%;
position: absolute;
z-index: 99;
-webkit-transform: translate(-50%, 0);
transform: translate(-50%, 0);
}
</style>
</head>
<body>
<div id="canvas-holder">
<canvas id="chart-area1" width="50" height="50" />
</div>
<div id="canvas-holder">
<canvas id="chart-area2" width="300" height="300" />
</div>
<div id="chartjs-tooltip"></div>
<script>
Chart.defaults.global.externalTooltips = function(tooltip) {
// Tooltip Element
var tooltipEl = $('#chartjs-tooltip');
// Hide if no tooltip
if (!tooltip) {
tooltipEl.css({
opacity: 0
});
return;
}
// Set caret Position
tooltipEl.removeClass('above below');
tooltipEl.addClass(tooltip.yAlign);
// Set Text
tooltipEl.html(tooltip.text);
// Find Y Location on page
var top;
if (tooltip.yAlign == 'above') {
top = tooltip.y - tooltip.caretHeight - tooltip.caretPadding;
} else {
top = tooltip.y + tooltip.caretHeight + tooltip.caretPadding;
}
// Display, position, and set styles for font
tooltipEl.css({
opacity: 1,
left: tooltip.chart.canvas.offsetLeft + tooltip.x + 'px',
top: tooltip.chart.canvas.offsetTop + top + 'px',
fontFamily: tooltip.fontFamily,
fontSize: tooltip.fontSize,
fontStyle: tooltip.fontStyle,
});
};
var pieData = [{
value: 300,
color: "#F7464A",
highlight: "#FF5A5E",
label: "Red"
}, {
value: 50,
color: "#46BFBD",
highlight: "#5AD3D1",
label: "Green"
}, {
value: 100,
color: "#FDB45C",
highlight: "#FFC870",
label: "Yellow"
}, {
value: 40,
color: "#949FB1",
highlight: "#A8B3C5",
label: "Grey"
}, {
value: 120,
color: "#4D5360",
highlight: "#616774",
label: "Dark Grey"
}];
window.onload = function() {
var ctx1 = document.getElementById("chart-area1").getContext("2d");
window.myPie = new Chart(ctx1).Pie(pieData);
var ctx2 = document.getElementById("chart-area2").getContext("2d");
window.myPie = new Chart(ctx2).Pie(pieData);
};
</script>
</body>
</html>

View File

@ -98,6 +98,9 @@
// Boolean - Determines whether to draw tooltips on the canvas or not - attaches events to touchmove & mousemove // Boolean - Determines whether to draw tooltips on the canvas or not - attaches events to touchmove & mousemove
showTooltips: true, 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 // Array - Array of string names to attach tooltip events
tooltipEvents: ["mousemove", "touchstart", "touchmove", "mouseout"], tooltipEvents: ["mousemove", "touchstart", "touchmove", "mouseout"],
@ -895,6 +898,9 @@
this.activeElements = ChartElements; this.activeElements = ChartElements;
} }
this.draw(); this.draw();
if(this.options.externalTooltips){
this.options.externalTooltips(false);
}
if (ChartElements.length > 0){ if (ChartElements.length > 0){
// If we have multiple datasets, show a MultiTooltip for all of the data points at that index // If we have multiple datasets, show a MultiTooltip for all of the data points at that index
if (this.datasets && this.datasets.length > 1) { if (this.datasets && this.datasets.length > 1) {
@ -975,7 +981,8 @@
legendColorBackground : this.options.multiTooltipKeyBackground, legendColorBackground : this.options.multiTooltipKeyBackground,
title: ChartElements[0].label, title: ChartElements[0].label,
chart: this.chart, chart: this.chart,
ctx: this.chart.ctx ctx: this.chart.ctx,
external: this.options.externalTooltips
}).draw(); }).draw();
} else { } else {
@ -994,7 +1001,8 @@
caretHeight: this.options.tooltipCaretSize, caretHeight: this.options.tooltipCaretSize,
cornerRadius: this.options.tooltipCornerRadius, cornerRadius: this.options.tooltipCornerRadius,
text: template(this.options.tooltipTemplate, Element), text: template(this.options.tooltipTemplate, Element),
chart: this.chart chart: this.chart,
external: this.options.externalTooltips
}).draw(); }).draw();
}, this); }, this);
} }
@ -1250,7 +1258,7 @@
this.yAlign = "above"; this.yAlign = "above";
//Distance between the actual element.y position and the start of the tooltip caret //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, var tooltipWidth = ctx.measureText(this.text).width + 2*this.xPadding,
tooltipRectHeight = this.fontSize + 2*this.yPadding, tooltipRectHeight = this.fontSize + 2*this.yPadding,
@ -1272,47 +1280,53 @@
ctx.fillStyle = this.fillColor; ctx.fillStyle = this.fillColor;
switch(this.yAlign) // Custom Tooltips
{ if(this.external){
case "above": this.external(this);
//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;
} }
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) switch(this.xAlign)
{ {
case "left": case "left":
tooltipX = this.x - tooltipWidth + (this.cornerRadius + this.caretHeight); tooltipX = this.x - tooltipWidth + (this.cornerRadius + this.caretHeight);
break; break;
case "right": case "right":
tooltipX = this.x - (this.cornerRadius + this.caretHeight); tooltipX = this.x - (this.cornerRadius + this.caretHeight);
break; 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(){ draw : function(){
drawRoundedRectangle(this.ctx,this.x,this.y - this.height/2,this.width,this.height,this.cornerRadius); // Custom Tooltips
var ctx = this.ctx; if(this.external){
ctx.fillStyle = this.fillColor; this.external(this);
ctx.fill(); }
ctx.closePath(); 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.textAlign = "left";
ctx.textBaseline = "middle"; ctx.textBaseline = "middle";
ctx.fillStyle = this.titleTextColor; ctx.fillStyle = this.titleTextColor;
ctx.font = this.titleFont; 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; ctx.font = this.font;
helpers.each(this.labels,function(label,index){ helpers.each(this.labels,function(label,index){
ctx.fillStyle = this.textColor; ctx.fillStyle = this.textColor;
ctx.fillText(label,this.x + this.xPadding + this.fontSize + 3, this.getLineHeight(index + 1)); 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) //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); //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. //Instead we'll make a white filled block to put the legendColour palette over.
ctx.fillStyle = this.legendColorBackground; ctx.fillStyle = this.legendColorBackground;
ctx.fillRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize); ctx.fillRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize);
ctx.fillStyle = this.legendColors[index].fill; ctx.fillStyle = this.legendColors[index].fill;
ctx.fillRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize); ctx.fillRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize);
},this); },this);
}
} }
}); });