Change tooltip positioning in doughnut chart using Chart.js - javascript
I have a doughnut chart using Chart.js that displays login data for my app correctly, however I have modified the chart so that the total number of logins is displayed in text in the center cutout:
The problem I am running into is with the tooltips. When I hover over the light teal piece of the pie chart, if the chart is scaled smaller, the tooltip is overlapped by the text in the center, like this:
I want to be able to change the direction the tooltip extends out, so instead of it going towards the center, it moves away so that both the tooltip and the center analytic are visible, but I have yet to find a concise explanation on how to change tooltip positioning. Here is the code I have currently:
var loslogged = dataset[0][0].loslogged;
var realtorlogged = dataset[1][0].realtorlogged;
var borrowerlogged = dataset[2][0].borrowerlogged;
var totallogged = parseInt(loslogged) + parseInt(realtorlogged) + parseInt(borrowerlogged);
Chart.pluginService.register({
afterDraw: function (chart) {
if (chart.config.options.elements.center) {
var helpers = Chart.helpers;
var centerX = (chart.chartArea.left + chart.chartArea.right) / 2;
var centerY = (chart.chartArea.top + chart.chartArea.bottom) / 2;
var ctx = chart.chart.ctx;
ctx.save();
var fontSize = helpers.getValueOrDefault(chart.config.options.elements.center.fontSize, Chart.defaults.global.defaultFontSize);
var fontStyle = helpers.getValueOrDefault(chart.config.options.elements.center.fontStyle, Chart.defaults.global.defaultFontStyle);
var fontFamily = helpers.getValueOrDefault(chart.config.options.elements.center.fontFamily, Chart.defaults.global.defaultFontFamily);
var font = helpers.fontString(fontSize, fontStyle, fontFamily);
ctx.font = font;
ctx.fillStyle = helpers.getValueOrDefault(chart.config.options.elements.center.fontColor, Chart.defaults.global.defaultFontColor);
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(chart.config.options.elements.center.text, centerX, centerY);
ctx.restore();
}
}
});
var loginChartData = {
labels: ["Loan Officers","Realtors","Borrowers"],
datasets: [{
label: "Number of Logins",
data: [loslogged, realtorlogged, borrowerlogged],
backgroundColor: [
"rgba(191, 25, 25, 0.75)",
"rgba(58, 73, 208, 0.75)",
"rgba(79, 201, 188, 0.75)"
],
borderColor: [
"rgba(255, 255, 255, 1)",
"rgba(255, 255, 255, 1)",
"rgba(255, 255, 255, 1)"
],
borderWidth: 4
}],
gridLines: {
display: false
}
};
var loginChartOptions = {
title: {
display: false
},
cutoutPercentage: 50,
elements: {
center: {
text: totallogged,
fontColor: '#000',
fontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
fontSize: 36,
fontStyle: 'bold'
}
}
};
var loginChart = document.getElementById('loginsChart').getContext('2d');
new Chart(loginChart, {
type: 'doughnut',
data: loginChartData,
options: loginChartOptions
});
It used to be a lot easier to reverse the tooltips in previous versions of chart.js (v2.3 and before). All you had to do was overwrite the determineAlignment tooltip method and reverse the logic.
However starting in v2.4, the functions that calculate the tooltip positions (including determineAlignment) were made private, so there is no longer a way to simply overwrite them (instead you have to duplicate them).
Here is a working reversed tooltip solution that unfortunately requires a lot of copy and paste from the chart.js source (this is required since the methods are private). The risk with this approach is that the underlying private functions could change in new releases at any time and your new reverse tooltip could break unexpectedly.
With that said, here is walk through of the implementation (with a codepen example at the bottom).
1) First, let's extend the Chart.Tooltip object and create a new Chart.ReversedTooltip object. We really only need to overwrite the update method since it performs all the positioning logic. In fact, this overwrite is just a straight copy and paste from the source because we actually only need to modify the private determineAlignment method which is called by update.
// create a new reversed tooltip. we must overwrite the update method which is
// where all the positioning occurs
Chart.ReversedTooltip = Chart.Tooltip.extend({
update: function(changed) {
var me = this;
var opts = me._options;
// Need to regenerate the model because its faster than using extend and it is necessary due to the optimization in Chart.Element.transition
// that does _view = _model if ease === 1. This causes the 2nd tooltip update to set properties in both the view and model at the same time
// which breaks any animations.
var existingModel = me._model;
var model = me._model = getBaseModel(opts);
var active = me._active;
var data = me._data;
var chartInstance = me._chartInstance;
// In the case where active.length === 0 we need to keep these at existing values for good animations
var alignment = {
xAlign: existingModel.xAlign,
yAlign: existingModel.yAlign
};
var backgroundPoint = {
x: existingModel.x,
y: existingModel.y
};
var tooltipSize = {
width: existingModel.width,
height: existingModel.height
};
var tooltipPosition = {
x: existingModel.caretX,
y: existingModel.caretY
};
var i, len;
if (active.length) {
model.opacity = 1;
var labelColors = [];
tooltipPosition = Chart.Tooltip.positioners[opts.position](active, me._eventPosition);
var tooltipItems = [];
for (i = 0, len = active.length; i < len; ++i) {
tooltipItems.push(createTooltipItem(active[i]));
}
// If the user provided a filter function, use it to modify the tooltip items
if (opts.filter) {
tooltipItems = tooltipItems.filter(function(a) {
return opts.filter(a, data);
});
}
// If the user provided a sorting function, use it to modify the tooltip items
if (opts.itemSort) {
tooltipItems = tooltipItems.sort(function(a, b) {
return opts.itemSort(a, b, data);
});
}
// Determine colors for boxes
helpers.each(tooltipItems, function(tooltipItem) {
labelColors.push(opts.callbacks.labelColor.call(me, tooltipItem, chartInstance));
});
// Build the Text Lines
model.title = me.getTitle(tooltipItems, data);
model.beforeBody = me.getBeforeBody(tooltipItems, data);
model.body = me.getBody(tooltipItems, data);
model.afterBody = me.getAfterBody(tooltipItems, data);
model.footer = me.getFooter(tooltipItems, data);
// Initial positioning and colors
model.x = Math.round(tooltipPosition.x);
model.y = Math.round(tooltipPosition.y);
model.caretPadding = helpers.getValueOrDefault(tooltipPosition.padding, 2);
model.labelColors = labelColors;
// data points
model.dataPoints = tooltipItems;
// We need to determine alignment of the tooltip
tooltipSize = getTooltipSize(this, model);
alignment = determineAlignment(this, tooltipSize);
// Final Size and Position
backgroundPoint = getBackgroundPoint(model, tooltipSize, alignment);
} else {
model.opacity = 0;
}
model.xAlign = alignment.xAlign;
model.yAlign = alignment.yAlign;
model.x = backgroundPoint.x;
model.y = backgroundPoint.y;
model.width = tooltipSize.width;
model.height = tooltipSize.height;
// Point where the caret on the tooltip points to
model.caretX = tooltipPosition.x;
model.caretY = tooltipPosition.y;
me._model = model;
if (changed && opts.custom) {
opts.custom.call(me, model);
}
return me;
},
});
2) As you can see, the update method uses a handful of private methods (e.g. getBaseModel, createTooltipItem, determineAlignment, etc.). In order for our update method to actually work, we have to provide an implementation for each of these methods. Here again is another copy and paste from the source. The only method that we need to modify however is the determineAlignment method. Here is the modified version that reverses the alignment logic.
// modified from source to reverse the position
function determineAlignment(tooltip, size) {
var model = tooltip._model;
var chart = tooltip._chart;
var chartArea = tooltip._chartInstance.chartArea;
var xAlign = 'center';
var yAlign = 'center';
// set caret position to top or bottom if tooltip y position will extend outsite the chart top/bottom
if (model.y < size.height) {
yAlign = 'top';
} else if (model.y > (chart.height - size.height)) {
yAlign = 'bottom';
}
var leftAlign, rightAlign; // functions to determine left, right alignment
var overflowLeft, overflowRight; // functions to determine if left/right alignment causes tooltip to go outside chart
var yAlign; // function to get the y alignment if the tooltip goes outside of the left or right edges
var midX = (chartArea.left + chartArea.right) / 2;
var midY = (chartArea.top + chartArea.bottom) / 2;
if (yAlign === 'center') {
leftAlign = function(x) {
return x >= midX;
};
rightAlign = function(x) {
return x < midX;
};
} else {
leftAlign = function(x) {
return x <= (size.width / 2);
};
rightAlign = function(x) {
return x >= (chart.width - (size.width / 2));
};
}
overflowLeft = function(x) {
return x - size.width < 0;
};
overflowRight = function(x) {
return x + size.width > chart.width;
};
yAlign = function(y) {
return y <= midY ? 'bottom' : 'top';
};
if (leftAlign(model.x)) {
xAlign = 'left';
// Is tooltip too wide and goes over the right side of the chart.?
if (overflowLeft(model.x)) {
xAlign = 'center';
yAlign = yAlign(model.y);
}
} else if (rightAlign(model.x)) {
xAlign = 'right';
// Is tooltip too wide and goes outside left edge of canvas?
if (overflowRight(model.x)) {
xAlign = 'center';
yAlign = yAlign(model.y);
}
}
var opts = tooltip._options;
return {
xAlign: opts.xAlign ? opts.xAlign : xAlign,
yAlign: opts.yAlign ? opts.yAlign : yAlign
};
};
3) Now that our new Chart.ReversedTooltip is complete, we need to use the plugin system to change the original tooltip to our reversed tooltip. We can do this using the afterInit plugin method.
Chart.plugins.register({
afterInit: function (chartInstance) {
// replace the original tooltip with the reversed tooltip
chartInstance.tooltip = new Chart.ReversedTooltip({
_chart: chartInstance.chart,
_chartInstance: chartInstance,
_data: chartInstance.data,
_options: chartInstance.options.tooltips
}, chartInstance);
chartInstance.tooltip.initialize();
}
});
After all that, we finally have reversed tooltips! Checkout a full working example at this codepen.
It's also worth mentioning that this approach is very brittle and, as I mentioned, can easily break overtime (on account of the copy and pasting required). Another option would be to just use a custom tooltip instead and position it wherever you desire on the chart.
Checkout this chart.js sample that shows how to setup and use a custom tooltip. You could go with this approach and just modify the positioning logic.
If you have a small tooltip label, you can use simple chart.js options to fix overlaps issue:
plugins: {
tooltip: {
xAlign: 'center',
yAlign: 'bottom'
}
}
I managed to solve the same by setting zIndex of Doughnut wrapper div to 1, settting the zIndex of text shown in the middle of Doughnut to -1, and canvas is transparent by default.
Hope this hels.
Related
Data-labeling ChartJS, Removing Duplicates?
Is there a good way to remove duplicate data labels in Chartjs? Or a good way to space labels out better above data points so they are not on top of each other? Chart.plugins.register({ afterDatasetsDraw: function(chart, easing) { // To only draw at the end of animation, check for easing === 1 var ctx = chart.ctx; chart.data.datasets.forEach(function (dataset, i) { var meta = chart.getDatasetMeta(i); if (!meta.hidden) { meta.data.forEach(function(element, index) { // Draw the text in black, with the specified font ctx.fillStyle = 'rgb(255, 255, 255)'; var fontSize = 12; var fontStyle = 'normal'; var fontFamily = 'Arial'; ctx.font = Chart.helpers.fontString(fontSize, fontStyle, fontFamily); // Just naively convert to string for now var dataString = dataset.data[index].toString(); // Make sure alignment settings are correct ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; var bodySpacing = 3; var padding = 10; var position = element.tooltipPosition(); ctx.fillText(dataString, position.x, position.y - (fontSize / 2) - padding); }); } }); } }); To clarify, the data is my data. The code is taken from the chartjs sample data labeling page and basically pasted in.
Visit this chart Style in chart.js library. It show list of all data on specific point. Here is link
How to display data values on Chart.js
Is it possible using Chart.js to display data values? I want to print the graph. Thanks for any advice..
There is an official plugin for Chart.js 2.7.0+ to do this: Datalabels Otherwise, you can loop through the points / bars onAnimationComplete and display the values Preview HTML <canvas id="myChart1" height="300" width="500"></canvas> <canvas id="myChart2" height="300" width="500"></canvas> Script var chartData = { labels: ["January", "February", "March", "April", "May", "June"], datasets: [ { fillColor: "#79D1CF", strokeColor: "#79D1CF", data: [60, 80, 81, 56, 55, 40] } ] }; var ctx = document.getElementById("myChart1").getContext("2d"); var myLine = new Chart(ctx).Line(chartData, { showTooltips: false, onAnimationComplete: function () { var ctx = this.chart.ctx; ctx.font = this.scale.font; ctx.fillStyle = this.scale.textColor ctx.textAlign = "center"; ctx.textBaseline = "bottom"; this.datasets.forEach(function (dataset) { dataset.points.forEach(function (points) { ctx.fillText(points.value, points.x, points.y - 10); }); }) } }); var ctx = document.getElementById("myChart2").getContext("2d"); var myBar = new Chart(ctx).Bar(chartData, { showTooltips: false, onAnimationComplete: function () { var ctx = this.chart.ctx; ctx.font = this.scale.font; ctx.fillStyle = this.scale.textColor ctx.textAlign = "center"; ctx.textBaseline = "bottom"; this.datasets.forEach(function (dataset) { dataset.bars.forEach(function (bar) { ctx.fillText(bar.value, bar.x, bar.y - 5); }); }) } }); Fiddle - http://jsfiddle.net/uh9vw0ao/
This works for Chart.js 2.3, including for both line/bar types. Important: Even if you don't need the animation, don't change the duration option to 0. Otherwise, you will get chartInstance.controller is undefined error. var chartData = { labels: ["January", "February", "March", "April", "May", "June"], datasets: [ { fillColor: "#79D1CF", strokeColor: "#79D1CF", data: [60, 80, 81, 56, 55, 40] } ] }; var opt = { events: false, tooltips: { enabled: false }, hover: { animationDuration: 0 }, animation: { duration: 1, onComplete: function () { var chartInstance = this.chart, ctx = chartInstance.ctx; ctx.font = Chart.helpers.fontString(Chart.defaults.global.defaultFontSize, Chart.defaults.global.defaultFontStyle, Chart.defaults.global.defaultFontFamily); ctx.textAlign = 'center'; ctx.textBaseline = 'bottom'; this.data.datasets.forEach(function (dataset, i) { var meta = chartInstance.controller.getDatasetMeta(i); meta.data.forEach(function (bar, index) { var data = dataset.data[index]; ctx.fillText(data, bar._model.x, bar._model.y - 5); }); }); } } }; var ctx = document.getElementById("Chart1"), myLineChart = new Chart(ctx, { type: 'bar', data: chartData, options: opt }); <canvas id="myChart1" height="300" width="500"></canvas>
If you are using the plugin chartjs-plugin-datalabels then the following code options object will help. Make sure you import import ChartDataLabels from 'chartjs-plugin-datalabels'; in your TypeScript file or add reference to <script src="chartjs-plugin-datalabels.js"></script> in your javascript file and register the plugin using ChartJS.register(ChartDataLabels). options: { maintainAspectRatio: false, responsive: true, scales: { yAxes: [{ ticks: { beginAtZero: true, } }] }, plugins: { datalabels: { anchor: 'end', align: 'top', formatter: Math.round, font: { weight: 'bold' } } } }
This animation option works for 2.1.3 on a bar chart. Slightly modified Ross answer: animation: { duration: 0, onComplete: function () { // render the value of the chart above the bar var ctx = this.chart.ctx; ctx.font = Chart.helpers.fontString(Chart.defaults.global.defaultFontSize, 'normal', Chart.defaults.global.defaultFontFamily); ctx.fillStyle = this.chart.config.options.defaultFontColor; ctx.textAlign = 'center'; ctx.textBaseline = 'bottom'; this.data.datasets.forEach(function (dataset) { for (var i = 0; i < dataset.data.length; i++) { var model = dataset._meta[Object.keys(dataset._meta)[0]].data[i]._model; ctx.fillText(dataset.data[i], model.x, model.y - 5); } }); } }
Based on Ross's answer for Chart.js 2.0 and up, I had to include a little tweak to guard against the case when the bar's heights comes too chose to the scale boundary. The animation attribute of the bar chart's option: animation: { duration: 500, easing: "easeOutQuart", onComplete: function () { var ctx = this.chart.ctx; ctx.font = Chart.helpers.fontString(Chart.defaults.global.defaultFontFamily, 'normal', Chart.defaults.global.defaultFontFamily); ctx.textAlign = 'center'; ctx.textBaseline = 'bottom'; this.data.datasets.forEach(function (dataset) { for (var i = 0; i < dataset.data.length; i++) { var model = dataset._meta[Object.keys(dataset._meta)[0]].data[i]._model, scale_max = dataset._meta[Object.keys(dataset._meta)[0]].data[i]._yScale.maxHeight; ctx.fillStyle = '#444'; var y_pos = model.y - 5; // Make sure data value does not get overflown and hidden // when the bar's value is too close to max value of scale // Note: The y value is reverse, it counts from top down if ((scale_max - model.y) / scale_max >= 0.93) y_pos = model.y + 20; ctx.fillText(dataset.data[i], model.x, y_pos); } }); } }
I think the nicest option to do this in Chart.js v2.x is by using a plugin, so you don't have a large block of code in the options. In addition, it prevents the data from disappearing when hovering over a bar. I.e., simply use this code, which registers a plugin that adds the text after the chart is drawn. Chart.pluginService.register({ afterDraw: function(chartInstance) { var ctx = chartInstance.chart.ctx; // render the value of the chart above the bar ctx.font = Chart.helpers.fontString(Chart.defaults.global.defaultFontSize, 'normal', Chart.defaults.global.defaultFontFamily); ctx.textAlign = 'center'; ctx.textBaseline = 'bottom'; chartInstance.data.datasets.forEach(function (dataset) { for (var i = 0; i < dataset.data.length; i++) { var model = dataset._meta[Object.keys(dataset._meta)[0]].data[i]._model; ctx.fillText(dataset.data[i], model.x, model.y - 2); } }); } });
Following this good answer, I'd use these options for a bar chart: var chartOptions = { animation: false, responsive : true, tooltipTemplate: "<%= value %>", tooltipFillColor: "rgba(0,0,0,0)", tooltipFontColor: "#444", tooltipEvents: [], tooltipCaretSize: 0, onAnimationComplete: function() { this.showTooltip(this.datasets[0].bars, true); } }; window.myBar = new Chart(ctx1).Bar(chartData, chartOptions); This still uses the tooltip system and his advantages (automatic positionning, templating, ...) but hiding the decorations (background color, caret, ...)
I'd recommend using this plugin: datalabels Labels can be added to your charts simply by importing the plugin into the JavaScript file, for example: import 'chartjs-plugin-datalabels' And can be fine-tuned using this documentation: https://chartjs-plugin-datalabels.netlify.com/options.html
From my experience, once you include the chartjs-plugin-datalabels plugin (make sure to place the <script> tag after the chart.js tag on your page), your charts begin to display values. If you then choose you can customize it to fit your needs. The customization is clearly documented here but basically, the format is like this hypothetical example: var myBarChart = new Chart(ctx, { type: 'bar', data: yourDataObject, options: { // other options plugins: { datalabels: { anchor :'end', align :'top', // and if you need to format how the value is displayed... formatter: function(value, context) { return GetValueFormatted(value); } } } } });
From Chart.js samples (file Chart.js-2.4.0/samples/data_labelling.html): // Define a plugin to provide data labels Chart.plugins.register({ afterDatasetsDraw: function(chartInstance, easing) { // To only draw at the end of animation, check for easing === 1 var ctx = chartInstance.chart.ctx; chartInstance.data.datasets.forEach(function (dataset, i) { var meta = chartInstance.getDatasetMeta(i); if (!meta.hidden) { meta.data.forEach(function(element, index) { // Draw the text in black, with the specified font ctx.fillStyle = 'rgb(0, 0, 0)'; var fontSize = 16; var fontStyle = 'normal'; var fontFamily = 'Helvetica Neue'; ctx.font = Chart.helpers.fontString(fontSize, fontStyle, fontFamily); // Just naively convert to string for now var dataString = dataset.data[index].toString(); // Make sure alignment settings are correct ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; var padding = 5; var position = element.tooltipPosition(); ctx.fillText(dataString, position.x, position.y - (fontSize / 2) - padding); }); } }); } });
Adapted the #Ross answer to work with 3.7.0 version of the Chartjs animation: { duration: 0, onComplete: function() { ctx = this.ctx; ctx.font = Chart.helpers.fontString(Chart.defaults.font.size, Chart.defaults.font.style, Chart.defaults.font.family); ctx.textAlign = 'center'; ctx.textBaseline = 'bottom'; chartinst = this; this.data.datasets.forEach(function(dataset, i) { if(chartinst.isDatasetVisible(i)){ var meta = chartinst.getDatasetMeta(i); meta.data.forEach(function(bar, index) { var data = dataset.data[index]; ctx.fillText(data, bar.x, bar.y - 5); }); } }); } } In this case, animation can be 0 To have a nicer looking, you can disable the hover and the tooltip if you want a more "static" visualization Also, the isDataSetVisible works to get rid of the numbers that stay shown when you hide the dataset in case of multiple datasets
I edited Aaron Hudon's answer a little, but only for bar charts. My version adds: Fade in animation for the values. Prevent clipping by positioning the value inside the bar if the bar is too high. No blinking. Downside: When hovering over a bar that has a value inside it, the value might look a little jagged. I have not found a solution do disable hover effects. It might also need tweaking depending on your own settings. Configuration: bar: { tooltips: { enabled: false }, hover: { animationDuration: 0 }, animation: { onComplete: function() { this.chart.controller.draw(); drawValue(this, 1); }, onProgress: function(state) { var animation = state.animationObject; drawValue(this, animation.currentStep / animation.numSteps); } } } Helpers: // Font color for values inside the bar var insideFontColor = '255,255,255'; // Font color for values above the bar var outsideFontColor = '0,0,0'; // How close to the top edge bar can be before the value is put inside it var topThreshold = 20; var modifyCtx = function(ctx) { ctx.font = Chart.helpers.fontString(Chart.defaults.global.defaultFontSize, 'normal', Chart.defaults.global.defaultFontFamily); ctx.textAlign = 'center'; ctx.textBaseline = 'bottom'; return ctx; }; var fadeIn = function(ctx, obj, x, y, black, step) { var ctx = modifyCtx(ctx); var alpha = 0; ctx.fillStyle = black ? 'rgba(' + outsideFontColor + ',' + step + ')' : 'rgba(' + insideFontColor + ',' + step + ')'; ctx.fillText(obj, x, y); }; var drawValue = function(context, step) { var ctx = context.chart.ctx; context.data.datasets.forEach(function (dataset) { for (var i = 0; i < dataset.data.length; i++) { var model = dataset._meta[Object.keys(dataset._meta)[0]].data[i]._model; var textY = (model.y > topThreshold) ? model.y - 3 : model.y + 20; fadeIn(ctx, dataset.data[i], model.x, textY, model.y > topThreshold, step); } }); };
To prevent your numbers from being cut off if they're too close to the top of the canvas: yAxes: [{ ticks: { stepSize: Math.round((1.05*(Math.max.apply(Math, myListOfyValues)) / 10)/5)*5, suggestedMax: 1.05*(Math.max.apply(Math, myListOfyValues)), beginAtZero: true, precision: 0 } }] 10 = the number of ticks 5 = rounds tick values to the nearest 5 - all y values will be incremented evenly 1.05 = increases the maximum y axis tick value so the numbers don't get cut off Something similar will work for xAxes too.
jQuery flot bar numbers: Numbers positioning
I'm using jquery.flot.barnumbers.js plugin for the Javascript plotting (charts) library for jQuery to show the numbers on the bars. My code: $.plot("#placeholderByDay", [ { data: DataOne, label: "Total Calls", bars: { numbers:{ show:true, xAlign: 80,//align top yAlign: 1 //yAlign: function(y) { return y+ 1; } //upside of bars } } ]); What I get now is: What I need is: So when there is no data for a bar, the zeros should be just above the axis, and where there are values should be as is, both rotated let 90 degrees. How can I achieve this?
Oops, I did it again. If you'd like to drop the plugin and do this the fun way; code it up yourself. It'll give you the freedom to customize any way you like. // after you draw the plot var ctx = somePlot.getCanvas().getContext("2d"); var data = somePlot.getData()[0].data; var xaxis = somePlot.getXAxes()[0]; var yaxis = somePlot.getYAxes()[0]; var offset = somePlot.getPlotOffset(); ctx.font = "16px 'Segoe UI'"; ctx.fillStyle = "black"; for (var i = 0; i < data.length; i++){ var text = data[i][4] + ''; var metrics = ctx.measureText(text); var xPos = xaxis.p2c(data[i][0]) + offset.left; var yPos = yaxis.p2c(data[i][5]) + offset.top + metrics.width + 5; // perform the rotation ctx.save(); ctx.translate(xPos, yPos); ctx.rotate(-Math.PI/2); ctx.fillText(text, 1, 1); ctx.restore(); } Example here.
part of the plot I use "categories" in yaxis,the y position of the text should be like that: for(var i=0;i<data1.length;i++){ ..... var yPos = yaxis.p2c(i) + fontSize; ..... } if you want the flot shows like that: the y position of the text should be like that: for(var i=0;i<data1.length;i++){ ..... var yPos = yaxis.p2c(i+align) + fontSize; ..... } align = barWidth/2, if bars' align = 'left'; align = -barWidth/2, if bars' align = 'right;
Stacked Bar Flot Chart - labels inside each series of Stacked Bar [duplicate]
I'm using jquery.flot.barnumbers.js plugin for the Javascript plotting (charts) library for jQuery to show the numbers on the bars. My code: $.plot("#placeholderByDay", [ { data: DataOne, label: "Total Calls", bars: { numbers:{ show:true, xAlign: 80,//align top yAlign: 1 //yAlign: function(y) { return y+ 1; } //upside of bars } } ]); What I get now is: What I need is: So when there is no data for a bar, the zeros should be just above the axis, and where there are values should be as is, both rotated let 90 degrees. How can I achieve this?
Oops, I did it again. If you'd like to drop the plugin and do this the fun way; code it up yourself. It'll give you the freedom to customize any way you like. // after you draw the plot var ctx = somePlot.getCanvas().getContext("2d"); var data = somePlot.getData()[0].data; var xaxis = somePlot.getXAxes()[0]; var yaxis = somePlot.getYAxes()[0]; var offset = somePlot.getPlotOffset(); ctx.font = "16px 'Segoe UI'"; ctx.fillStyle = "black"; for (var i = 0; i < data.length; i++){ var text = data[i][4] + ''; var metrics = ctx.measureText(text); var xPos = xaxis.p2c(data[i][0]) + offset.left; var yPos = yaxis.p2c(data[i][5]) + offset.top + metrics.width + 5; // perform the rotation ctx.save(); ctx.translate(xPos, yPos); ctx.rotate(-Math.PI/2); ctx.fillText(text, 1, 1); ctx.restore(); } Example here.
part of the plot I use "categories" in yaxis,the y position of the text should be like that: for(var i=0;i<data1.length;i++){ ..... var yPos = yaxis.p2c(i) + fontSize; ..... } if you want the flot shows like that: the y position of the text should be like that: for(var i=0;i<data1.length;i++){ ..... var yPos = yaxis.p2c(i+align) + fontSize; ..... } align = barWidth/2, if bars' align = 'left'; align = -barWidth/2, if bars' align = 'right;
HighCharts--custom redraw handler borks zooming?
I'm trying to make a column chart in HighCharts with annotations that are properly repositioned whenever the chart is resized or zoomed. http://jsfiddle.net/2tJ3G/ You can see what I mean by resizing the frame around the chart. The annotations stay where they should be. The problem only comes about when this redraw function is called from a zoom.. the chart just reloads. I've had some success providing a function for the redraw event, however this has completely broken select-zooming (which just shows all data). Here's my handler: function drawIt() { var optionsTmp = options; chart = new Highcharts.Chart(optionsTmp, function(chart) { var text, box, point; var count = 0; for (var annotX in annots) { var annot = annots[annotX]; if (annot.length > 0) { //draw rectangles / text with annot vals } count++; } }); options = optionsTmp; } It's possible I'm not properly passing through the new extremes from the zoom (options.xAxis.min) but trying to pass this into the handler hasn't given me much luck. Any ideas?
The problem is that you are trying to remove chart, and create new one - this is not possible just by setting empty container - you need to also destroy chart. But still this is not possible to destroy chart while redrawing - it will cause errors. I think you should try another solution, for example updating that annotations. Something similar is done here with extra lines for points, see: http://www.highcharts.com/jsbin/oyadep/4/edit#javascript,live Of course, it's not finished but works fine when zooming in. function drawPaths(options) { var maxNum = 2; //add the paths group_init = options.renderer.g().add(); // console.log(options); for (i = 0; i < maxNum; i++) { $.each(options.series[i].data, function(i2, point) { var xpos = options.xAxis[0].translate(this.x); var ypos = options.yAxis[0].translate(this.y, false, true); var gr = this.y; //gross revenue var p_gr = 0; //for tests var eq; //equation var my; //console.log(this); eq = gr + 1; //equation my = options.yAxis[0].translate(eq, false, true); //console.log(xpos, ypos, my); options.renderer.path(['M', xpos + 105, ypos + 5, 'L', xpos + 105, my + 5]).attr({ 'stroke-width': 1, stroke: '#ad2b2b' }).add(group_init); }); } }
Long story short, my setExtremes function (which gets called before a total redraw of the chart) was stomping on the feet of the following redraw function. I had to set the thresholds there and silence it early. setExtremes: function(e) { doZoom = true; min= e.min; max = e.max; e.preventDefault(); e.stopPropagation(); } Corrected fiddle: http://jsfiddle.net/9LgUf/1/