Chart Js flickering or switching as i move mouse on canvas - javascript

I created a chart on my page using ChartJs and then i placed three buttons so that when clicked, it represents the data in a different chart type.
but the issue i am having is that if i move the mouse pointer across the canvas after switching to a different graph, it keeps switching or flickering and this is not good, how can i get it to stay on the current chart type.
i tried clearing the canvas and context object before rendering the chart but yet, it still refuses to work, here's my code..
var chartx;
var cv;
var mnts, vals, mx, steps; // for the chart labels..
//retrieve the json data from this url..
var setting = {
type:'GET',
url:'/ajax/stats/summary'
};
$(function(){
'use strict';
cv = document.getElementById('dboard'); //the canvas object
pullChartData('line'); //draw the line graph first
});
function pullChartData(chtype){
shwChldx(); //show the GIF loader
clearCanvas(); //clear the canvas
$.ajax(setting)
.done(function(response){
o = JSON.parse(response);
mnts = String(o.days).split(';'); //array of horizontal labels
vals = String(o.values).split(';'); //array of values
mx = Number(o.mx); //maximum value among chart data
steps = Number(o.step); //get the step to use on chart
setChartType(chtype);
hideChldx()
}).fail(function(xhr,status,error){
hideChldx();
notifyCVS('error loading CHART data...');
});
}
function shwChldx(){ //show loader GIF
$('#tkldr').css('display', 'block');
}
function hideChldx(){ //hide loader GIF
$('#tkldr').css('display', 'none');
}
function drawChart(cdiv, ctype){ //draw graph function
chartx = new Chart(cdiv, {
type: ctype,
data: {
labels: mnts,
datasets: [{
data: vals,
label: ' - visits at on this day',
borderColor: '#324463',
fillColor: 'rgba(109,177,117,0.71)',
borderWidth: 1,
fill: true
}]
},
options: {
backgroundColor: '#FFF',
titleFontSize: 16,
titleFontColor: '#0066ff',
titleMarginBottom: 10,
bodyFontColor: '#000',
bodyFontSize: 12,
displayColors: false,
xPadding:10,
yPadding:10,
},
legend: {
display: false,
labels: {
display: false
}
},
scales: {
yAxes: [{
ticks: {
beginAtZero:true,
fontSize: 12,
max: mx,
stepSize: Number(steps),
}
}],
xAxes: [{
ticks: {
beginAtZero:true,
fontSize: 11
}
}]
}
}
});
}
//clear the canvas object before anything..
function clearCanvas(){
ctx = cv.getContext('2d');
ctx.clearRect(0, 0, cv.width, cv.height);
notifyCVS('retrieving data, please wait...');
}
//this is the function referenced on the buttons..
//example - <button onclick="setChartType('bar')"></button>
// and i have 3 buttons for 'line', 'bar', 'horizontalBar' types
function setChartType(type){
clearCanvas();
ctx = cv.getContext('2d');
ctx.clearRect(0, 0, cv.width, cv.height);
drawChart(cv, type);
}
//i call this function to write on the canvas when there is
//no activity so that its not just a blank white object..
function notifyCVS(txt){
ctx = cv.getContext('2d');
ctx.clearRect(0, 0, cv.width, cv.height);
ctx.fillStyle = "#777777";
ctx.font = "Regular 12px Arial";
ctx.fillText(String(txt), (cv.width / 2) - 17, (cv.height / 2) + 8);
}
so this is my approach, the chart and everything works, but after clicking the different buttons the behaviour changes on mousemove and i dont know where the problem is from, thats just the problem, the flickering behaviour.
please what should i do.

There is no need to literally clear the canvas. But you need to destroy the chartjs object before you reinitialize it. So, it's like re-creating a chartjs object on the same canvas.
All you need to do is check if the instance already exists before creating the object in drawChart method.
function drawChart(cdiv, ctype){
if(chartx){
chartx.destroy();
}
chartx = new Chart(.....
......
}

Related

How to customize chart js Bar chart shape?

How can I change Chart Js's bar chart shape to have pointy top and bottom like this picture?
One way to get the triangles is to do a stacked chart, and add a line graph on top. To keep it reactive you would need to dynamically resize the pointRadius on the line data.
https://jsfiddle.net/0mcd5s13/3/
Or on line 4640 of chart.js you can change element_rectangle draw function to this:
draw: function() {
var ctx = this._chart.ctx;
var vm = this._view;
var rects = boundingRects(vm);
var outer = rects.outer;
var inner = rects.inner;
//ctx.fillStyle = vm.backgroundColor;
//ctx.fillRect(outer.x, outer.y, outer.w, outer.h);
if (outer.w === inner.w && outer.h === inner.h) {
return;
}
let offset = outer.w / 2;
ctx.save();
ctx.beginPath();
ctx.moveTo(outer.x, outer.y);
ctx.lineTo(outer.x, outer.y + outer.h);
ctx.lineTo(outer.x + offset, outer.y + outer.h + offset);
//ctx.lineTo(outer.x + offset, outer.y + outer.h);
ctx.lineTo(outer.x + outer.w, outer.y + outer.h);
ctx.lineTo(outer.x + outer.w, outer.y);
ctx.lineTo(outer.x + offset, outer.y - offset);
ctx.lineTo(outer.x, outer.y);
ctx.stroke();
//ctx.rect(outer.x, outer.y, outer.w, outer.h);
ctx.clip();
ctx.fillStyle = vm.borderColor;
// ctx.rect(inner.x, inner.y, inner.w, inner.h);
ctx.fill('evenodd');
ctx.restore();
},
which yields this:
more likely to import chart.js from a source, you will need make your own type of chart as an extension of bar chart.
(function(Chart) {
var helpers = Chart.helpers;
Chart.defaults.triBar = {
hover: {
mode: "label"
},
dataset: {
categoryPercentage: 0.8,
barPercentage: 0.9
},
scales: {
xAxes: [{
type: "category",
// grid line settings
gridLines: {
offsetGridLines: true
}
}],
yAxes: [{
type: "linear"
}]
}
};
Chart.controllers.triangleBar = Chart.controllers.bar.extend({
//
// extend element_rectangle draw function here
//
});
}).call(this, Chart);

Highcharts: Can I animate changing the order of bars on bar chart?

I want to create a chart like the below.
https://www.reddit.com/r/interestingasfuck/comments/9togwf/the_major_world_economies_over_time/
I created this chart by Highcharts. But, I can't animate changing the order of bars.
function rotate(array, times) {
while (times--) {
var temp = array.shift();
array.push(temp)
}
}
window.data = {
categories: ['Africa', 'America', 'Asia', 'Europe', 'Oceania'],
y_values: [100, 200, 300, 400, 500],
colors: ['#DC4D3A', '#E93D3F', '#83C6C7', '#46D388', '#D1D785']
};
document.getElementById("button").addEventListener("click", function () {
for (var i = 0; i < data['y_values'].length; i++) {
chart.series[0].data[i].update({y: data['y_values'][i]});
chart.series[0].data[i].update({color: data['colors'][i]});
}
chart.xAxis[0].update({categories: data['categories']});
rotate(data['y_values'], 1);
rotate(data['categories'], 1);
rotate(data['colors'], 1);
}, false);
All code I wrote are in JSFiddle.
https://jsfiddle.net/Shinohara/35e8gbyz/
Please anyone can help me?
Highcharts provides animate method for SVG elements, which you can use to achieve the wanted result. You need to animate columns, axis labels and data labels:
document.getElementById("button").addEventListener("click", function() {
var points = chart.series[0].points,
ticks = chart.xAxis[0].ticks;
points[0].graphic.animate({
x: points[1].shapeArgs.x
});
points[1].graphic.animate({
x: points[0].shapeArgs.x
});
points[0].dataLabel.animate({
y: points[1].dataLabel.translateY
});
points[1].dataLabel.animate({
y: points[0].dataLabel.translateY
});
ticks[0].label.animate({
y: ticks[1].label.xy.y
});
ticks[1].label.animate({
y: ticks[0].label.xy.y
});
}, false);
Live demo: https://jsfiddle.net/BlackLabel/ux59vcd6/
API Reference: https://api.highcharts.com/class-reference/Highcharts.SVGElement#animate
It seems there is no native implementation.
The only idea I have it to not display Y axis labels at all. Using SVGRenderer
draw the text and save it as a variable (for further updating)
chart.customText = chart.renderer.text('label name', 100, 100) // label name, X, Y
.attr({
useHTML: true
})
.css({
fontSize: '16px' // and other CSS if necessary
})
.add();
Then you can update x, y or text
chart.customText.attr({
str: 'new text. You can use HTML',
y: 150 // new value
})
Your next step is to do something with Y position. Or even you can try to draw only 1 text with HTML that contains all labels. Then using JS move them as you need.

Scrollable x axis with chart.js 2.1.4

I have a line graph with lot of points to plot
I want x axis to be scrollable
I have already looked few solutions but they are providing solution with old versions of chart js.
Is there any option to get scrollable x axis in chart.js version 2?
And
How can i get width of content in y axis in chart.js version 2?
if there is no direct option to get scrollable x axis, I can copy content in Y-axis region and draw image in other canvas.
My answer on a related question will help you. In my example I have made the Y axis scrollable, but this could easily be applied to the X axis too.
https://stackoverflow.com/a/51282003/10060003
JS fiddle - https://jsfiddle.net/EmmaLouise/eb1aqpx8/3/
I am using the animation onComplete and onProgress options to redraw the axis that I want to scroll with the chart. (See https://www.chartjs.org/docs/latest/configuration/animations.html).
$(function () {
var rectangleSet = false;
var canvasTest = $('#chart-Test');
var chartTest = new Chart(canvasTest, {
type: 'bar',
data: chartData,
maintainAspectRatio: false,
responsive: true,
options: {
tooltips: {
titleFontSize: 0,
titleMarginBottom: 0,
bodyFontSize: 12
},
legend: {
display: false
},
scales: {
xAxes: [{
ticks: {
fontSize: 12,
display: false
}
}],
yAxes: [{
ticks: {
fontSize: 12,
beginAtZero: true
}
}]
},
animation: {
onComplete: function () {
if (!rectangleSet) {
var scale = window.devicePixelRatio;
var sourceCanvas = chartTest.chart.canvas;
var copyWidth = chartTest.scales['y-axis-0'].width - 10;
var copyHeight = chartTest.scales['y-axis-0'].height + chartTest.scales['y-axis-0'].top + 10;
var targetCtx = document.getElementById("axis-Test").getContext("2d");
targetCtx.scale(scale, scale);
targetCtx.canvas.width = copyWidth * scale;
targetCtx.canvas.height = copyHeight * scale;
targetCtx.canvas.style.width = `${copyWidth}px`;
targetCtx.canvas.style.height = `${copyHeight}px`;
targetCtx.drawImage(sourceCanvas, 0, 0, copyWidth * scale, copyHeight * scale, 0, 0, copyWidth * scale, copyHeight * scale);
var sourceCtx = sourceCanvas.getContext('2d');
// Normalize coordinate system to use css pixels.
sourceCtx.clearRect(0, 0, copyWidth * scale, copyHeight * scale);
rectangleSet = true;
}
},
onProgress: function () {
if (rectangleSet === true) {
var copyWidth = chartTest.scales['y-axis-0'].width;
var copyHeight = chartTest.scales['y-axis-0'].height + chartTest.scales['y-axis-0'].top + 10;
var sourceCtx = chartTest.chart.canvas.getContext('2d');
sourceCtx.clearRect(0, 0, copyWidth, copyHeight);
}
}
}
}
});

Fill Chart.js bar chart with diagonal stripes or other patterns

I am trying to fill a bar graph with stripes so that it looks like the attached image. Is there a way to do this? How about other patterns?
tl;dr
Just pass a CanvasPattern or CanvasGradient to the dataset's backgroundColor property as the official docs say.
Excuse me, what?
This can be done through a 3rd party library like patternomaly, but if you just want a few simple pattern it is unnecessary since you can easily create a custom function that takes a color and give you back a canvas pattern:
function createDiagonalPattern(color = 'black') {
// create a 10x10 px canvas for the pattern's base shape
let shape = document.createElement('canvas')
shape.width = 10
shape.height = 10
// get the context for drawing
let c = shape.getContext('2d')
// draw 1st line of the shape
c.strokeStyle = color
c.beginPath()
c.moveTo(2, 0)
c.lineTo(10, 8)
c.stroke()
// draw 2nd line of the shape
c.beginPath()
c.moveTo(0, 8)
c.lineTo(2, 10)
c.stroke()
// create the pattern from the shape
return c.createPattern(shape, 'repeat')
}
Then just call it in your datasets (don't forget to add a border if you need that):
datasets: [{
label: 'Good questions',
data: [3, 4, 1, 6, 10],
backgroundColor: createDiagonalPattern('green'),
// create a border with the same color
borderColor: 'green',
borderWidth: 1,
}],
Edge cases
Keep in mind that canvas has anti-aliasing so when you draw stuff around the corners it can mess up your pattern. To mitigate this just draw your lines from an edge.
If you create the diagonal line between the corners like this:
c.beginPath()
c.moveTo(0, 0)
c.lineTo(10, 10)
c.stroke()
Then the pattern wouldn't look seamless because the corner crops off parts so you lose that infinite effect:
Demo
var element = document.getElementById('chart');
var ctx = element.getContext("2d");
function createDiagonalPattern(color = 'black') {
let shape = document.createElement('canvas')
shape.width = 10
shape.height = 10
let c = shape.getContext('2d')
c.strokeStyle = color
c.beginPath()
c.moveTo(2, 0)
c.lineTo(10, 8)
c.stroke()
c.beginPath()
c.moveTo(0, 8)
c.lineTo(2, 10)
c.stroke()
return c.createPattern(shape, 'repeat')
}
var graph = new Chart(element, {
type: 'bar',
data: {
labels: ['jan', 'feb', 'mar', 'apr', 'may'],
datasets: [
{
label: 'Good questions',
data: [3, 4, 1, 6, 10],
backgroundColor: createDiagonalPattern('green'),
borderColor: 'green',
borderWidth: 1,
},
{
label: 'Bad questions',
data: [2, 7, 3, 5, 1],
backgroundColor: createDiagonalPattern('#FF0000'),
borderColor: '#FF0000',
borderWidth: 1,
},
],
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: true,
},
}],
},
},
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
<canvas id="chart" ></canvas>
Old question, but now there's the patternomaly add-on :-)
https://github.com/ashiguruma/patternomaly
It contains 21 pre-defined patterns you can use in chart.js.
There is a section on patterns and gradients in the ChartJS documentation that allows to pass a CanvasPattern or CanvasGradient object instead of a string colour.
Read about it here:
http://www.chartjs.org/docs/latest/general/colors.html
Looking at the bar and global options, this doesn't seem possible using only chartjs.
http://www.chartjs.org/docs/#bar-chart-chart-options
http://www.chartjs.org/docs/#getting-started-global-chart-configuration
As chartsjs uses a canvas element to display its elements, you won't be able to implement a CSS solution either. If you really want to do this, you could try editing the chartjs library. Or just pick a solid color.

Style specific bar column with Flot

I'm working with Flot to create a bar chart. However, I need to add special styling to certain columns. Is this possible at all?
My HTML looks like this:
<div id="monthly-usage" style="width: 100%; height: 400px;"></div>
And my JS like this:
somePlot = null;
$(function() {
//Data from this year and last year
var thisYear = [
[3, 231.01],
[4, 219.65],
[5, 222.47],
[6, 223.09],
[7, 248.43],
[8, 246.22]
];
var lastYear = [
[3, 171.7],
[4, 130.62],
[5, 163.03],
[6, 166.46],
[7, 176.16],
[8, 169.04]
];
var usageData = [{
//Usage this year
label: "2014",
data: thisYear,
bars: {
show: true,
barWidth: .3,
fill: true,
lineWidth: 0,
order: 1,
fillColor: 'rgba(194, 46, 52, .85)'
},
color: '#c22e34'
}, {
//Usage last year to compare with current usage
label: "2013",
data: lastYear,
bars: {
show: true,
barWidth: .3,
fill: true,
lineWidth: 0,
order: 2,
fillColor: 'rgba(73, 80, 94, .85)'
},
color: '#49505e'
}];
//X-axis labels
var months = [
[0, "Jan"],
[1, "Feb"],
[2, "Mar"],
[3, "Apr"],
[4, "Maj"],
[5, "Jun"],
[6, "Jul"],
[7, "Aug"],
[8, "Sep"],
[9, "Okt"],
[10, "Nov"],
[11, "Dec"]
];
//Draw the graph
somePlot = $.plot(('#monthly-usage'), usageData, {
grid: {
color: '#646464',
borderColor: 'transparent',
hoverable: true
},
xaxis: {
ticks: months,
color: '#d4d4d4'
},
yaxis: {
tickSize: 50,
tickFormatter: function(y, axis) {
return y + " kWh";
}
},
legend: {
show: false
}
});
var ctx = somePlot.getCanvas().getContext("2d"); // get the context from plot
var data = somePlot.getData()[0].data; // get your series data
var xaxis = somePlot.getXAxes()[0]; // xAxis
var yaxis = somePlot.getYAxes()[0]; // yAxis
var offset = somePlot.getPlotOffset(); // plots offset
var imageObj = new Image(); // create image
imageObj.onload = function() { // when finish loading image add to canvas
xPos = xaxis.p2c(data[4][0]) + offset.left;
yPos = yaxis.p2c(data[4][1]) + offset.top;
ctx.drawImage(this, xPos, yPos);
xPos = xaxis.p2c(data[5][0]) + offset.left;
yPos = yaxis.p2c(data[5][1]) + offset.top;
ctx.drawImage(this, xPos, yPos);
};
imageObj.src = 'path/to/file.png'; // set it's source to kick off load
});
});
Optimally, I would like to insert an icon in bar 5 and 6 that warns the user. Alternatively, I'd like to change the color of bars 5 and 6. Any ideas on how to fix this?
EDIT: I've updated my JS according to Mark's answer which works.
#Mark, how can I position the images correctly. They are a bit off. I need the image inside the red bar and not besides the bar. I'm trying to finetune this but it doesn't seem as if I can use for instance "0.5". I use side by side bars which is different from your version.
xPos = xaxis.p2c(data[4][0]) + offset.left;
yPos = yaxis.p2c(data[4][1]) + offset.top;
You can't do exactly what you ask with standard options, but there are a couple of possible approaches:
Write your own draw method and use the hooks to install it in place of the standard flot drawing code. This obviously entails a lot of work, but you'll have complete control over how to render your data. (That said, I wouldn't recommend it.)
Break your data into two different data sets. One data set would have dummy values (e.g. 0, or whatever your minimum is) for bars 5 and 6. The second data set would have dummy values for all bars except 5 and 6. You could then style the "two" data sets independently, giving each, for example a different color. Graph the two sets as a stacked bar chart with whatever additional styling tweaks are appropriate for your chart.
(As a FYI, there's a fair bit of information and examples at jsDataV.is. Look at the "Book" section; chapter 2 is dedicated to flot.)
flot gives you access to the HTML5 Canvas it's drawing on; so you just add your icon on there yourself. Borrowing from my own answer here.
var ctx = somePlot.getCanvas().getContext("2d"); // get the context from plot
var data = somePlot.getData()[0].data; // get your series data
var xaxis = somePlot.getXAxes()[0]; // xAxis
var yaxis = somePlot.getYAxes()[0]; // yAxis
var offset = somePlot.getPlotOffset(); // plots offset
$.get("someImage.txt", function(img) { // grad some image, I'm loading it from a base64 resource
var imageObj = new Image(); // create image
imageObj.onload = function() { // when finish loading image add to canvas
var xPos = xaxis.p2c(data[4][0]) + offset.left;
var yPos = yaxis.p2c(data[4][2]) + offset.top;
ctx.drawImage(this, xPos, yPos);
xPos = xaxis.p2c(data[5][0]) + offset.left;
yPos = yaxis.p2c(data[5][3]) + offset.top;
ctx.drawImage(this, xPos, yPos);
};
imageObj.src = img; // set it's source to kick off load
});
Example here.
Looks like:

Categories