Hi guys,
I am trying Charts js in one of our solutions and i ran into the issues of getting very low resolution image when animation is set to false. Below is the example of the chart i am using. When animation and responsive both set to true, everything works as expected. When they are set to false, the chart rendered as very low resolution image. I need it to be not responsive and not animated because we use charts to generate PDF reports with WKHtmlToPdf library/toolkit. The v1.+ worked fine in that same configuration (responsive: false, animation: false).
var barChartData = {
labels: ["Week 1", "Week 2", "Week 3", "Week 4", "Week 5", "Week 6", "Week 7"],
datasets: [{
type: 'bar',
label: "Standard",
data: [200, 185, 590, 621, 250, 400, 95],
fill: false,
backgroundColor: '#61A4DD',
borderColor: '#61A4DD',
hoverBackgroundColor: '#61A4DD',
hoverBorderColor: '#61A4DD',
yAxisID: 'y-axis-1'
}, {
type: 'bar',
label: "Premium",
data: [240, 195, 720, 780, 300, 500, 150],
fill: false,
backgroundColor: '#F47E7A',
borderColor: '#F47E7A',
hoverBackgroundColor: '#F47E7A',
hoverBorderColor: '#F47E7A',
yAxisID: 'y-axis-1'
}, {
label: "Estimated Rebate",
type: 'line',
data: [51, 65, 40, 49, 60, 37, 40],
fill: false,
borderColor: '#EC932F',
backgroundColor: '#EC932F',
pointBorderColor: '#EC932F',
pointBackgroundColor: '#EC932F',
pointHoverBackgroundColor: '#EC932F',
pointHoverBorderColor: '#EC932F',
yAxisID: 'y-axis-2'
}]
};
window.onload = function () {
var ctx = document.getElementById("canvas").getContext("2d");
window.myBar = new Chart(ctx, {
type: 'bar',
data: barChartData,
options: {
responsive: false,
animation: false,
tooltips: {
mode: 'label'
},
elements: {
line: {
fill: false
}
},
scales: {
xAxes: [{
display: true,
stacked: true,
gridLines: {
display: false
},
labels: {
show: true,
}
}],
yAxes: [{
type: "linear",
stacked: true,
display: true,
position: "left",
id: "y-axis-1",
gridLines: {
display: false
},
labels: {
show: true,
}
}, {
type: "linear",
display: true,
position: "right",
id: "y-axis-2",
gridLines: {
display: false
},
labels: {
show: true,
}
}]
}
}
});
};
What is the size of your canvas? If the resolution is bad, try increasing the size of the canvas and forcing it down with CSS.
No no, the size of the canvas is not the issue. If I use responsive: true and animation: true, the image appears to be fine...
Here is the link to what the same charts look like with enabled / disabled animation: http://1drv.ms/1SyVqEp. On the left is the chart with responsive: true, animation: true, where as on the right the chart with responsive: false, animation: false, I tried different canvas sizes, the image appears low res regardless of the canvas size. The size of the canvas appears to be in tact, it is the image of the chart that is very low resolution. Increasing the size of the canvas increases the image size, but the resolution remains the same meaning the chart is more pixelated and blurry
@apryiomka is your device high DPI? I'm not sure why the animation setting affects this, but I can see how the responsive one does. It may be that no resize happens at the beginning to run the DPI scaler in our code. What it does is increases the canvas width and height by a factor of the DPI and then scales down the canvas back to the original size using CSS.
@etimberg, so, how could this be addressed? Because it wasn't an issue in version 1+. Setting both to false did not cause any DPI scaling issues. Is there a work around it? The reason we need to disable responsiveness and animation is because it appears that it causes some kind of "delay" and the chart image is not "immediately" available when the window is loaded. This is a problem for WKHtmlToPdf because it renders "blank space" for the chart image in this case. Setting responsive to false fixes WKHtmlToPdf rendering problem, but the chart appears low res. it worked fine in 1+, but we need to use v2 as version 1 doesn't allow combining bar and line charts together. Is there a way to set the DPI explicitly?
@apryiomka this definitely needs some investigation on our end.
I do have some potential work-arounds that you could try.
responsiveAnimationDuration to 0 in the config. Call resize() on the chart after creating itresponsiveAnimationDuration to 0 and set animation.duration to 0 in the config. But leave responsive as true.@etimberg, i have an update. Calling resize() seems to fix the low resolution problem, but now the charts are "blank" when we render them using WKHtmlToPdf. Setting responsive: false seems to "fix" WKHtmlToPdf rendering problem, but the charts, as i mentioned above, are rendered low res now (the original problem)... I presume the .resize() is still causing some kind of "lag" after the window.onload was triggered and the tool doesn't pick the rendered image properly. As long as we set responsive to true or call resize() the charts render correctly in the browser, but not in the "headless" browsers like WKHtmlToPdf.
@apryiomka I'm guessing what happens is that the resize tries to render using window.requestAnimationFrame and then WKHtmlToPdf renders as soon as the load is done.
When responsive is false, the first draw is never cleared. Did you have a chance to try my second option from above? I think it has the best chance of success. When the animation duration is 0, the draw will occur synchronously.
@apryiomka i downloaded WKHtmlToPdf and am seeing the same issue as you. I got it working with responsive: false if I set the canvas to a CSS style that was defined in px rather than percent.
Here's what I have so far. It looks 'OK' on a normal (96DPI) screen at 100% zoom. It does not look great when zoomed in far but that's the nature of a bitmapped image.
In the HTML note that the canvas width and height are set as an explicit pixel size rather than a percentage size. This is important since it means that the chart is initially at the correct size when first rendered.
<div id="canvasContainer" style="width: 500px; height: 250px;">
<canvas id="canvas" width="500" height="250"></canvas>
</div>
The configuration and chart setup is
var ctx = document.getElementById("canvas").getContext("2d");
var barChart = new Chart(
type: 'bar',
data: barChartData,
options: {
responsive: false,
responsiveAnimationDuration: 0,
animation: {
duration: 0
}
}
});
The result looks like:

The generated bar.pdf
@etimberg, i tried the solution above and it indeed seems to work. I can use it as a work around. Thank you again for looking into it.
responsive: false,
responsiveAnimationDuration: 0,
animation: {
duration: 0
} with the canvas container "fixed" size :) seems to work. I just wanted to clarify that.
Closing since I think the best solution is to use a fixed size container in this case and it seems to work.
@etimberg this solution is no longer working
@etimberg @apryiomka My solution was slightly different as I couldn't get the above one to work in Phantom 2. I rendered the charts with a large canvas to get the resolution up and then converted it into a png finally destroying and removing the old canvas and replacing them with an image with responsive CSS classes. Adjusting the knobes on the canvas width and height in addition to Chart.js options will get you a perfect render. We were able to get our rendering speed up with the approach (alternative to SVG renders) and the file size down.
HTML:
<div class="container">
<!-- generated images -->
<img id="someIdImage" class="img-responsive"></img>
<!-- temporary canvas -->
<canvas id="someId" width="2000" height="600"></canvas>
</div>
Javascript:
/**
* _plot() plot with Chart.js
*
* @param {Function} callback
*/
function _plot(callback) {
var config = {}; // some Chart.js config
var id = 'someId';
var el = document.querySelector('#' + id);
var el2d = el.getContext('2d');
// plot instance
var instance = new Chart(el2d, config);
// generate and append image
document.querySelector('#' + id + 'Image').setAttribute('src', el.toDataURL('image/png'));
// destroy instance
instance.destroy();
el.parentElement.removeChild(el);
// callback
if (callback) {
callback();
}
}
Does anyone have an alternative solution?
I am using ChartJs V2 and wkhtmltopdf 0.12.2.1 (with patched qt) for rendering a pdf.
I have tried to implement the above solution: responsive: false, responsiveAnimationDuration: 0, animation: { duration: 0 } with the canvas container "fixed" size.
But I still get blurry charts when I open the pdf file. :(
Most helpful comment
@etimberg @apryiomka My solution was slightly different as I couldn't get the above one to work in Phantom 2. I rendered the charts with a large canvas to get the resolution up and then converted it into a png finally destroying and removing the old canvas and replacing them with an image with responsive CSS classes. Adjusting the knobes on the canvas width and height in addition to Chart.js options will get you a perfect render. We were able to get our rendering speed up with the approach (alternative to SVG renders) and the file size down.
HTML:
Javascript: