ChartjsNodeCanvas
ChartjsNodeCanvas copied to clipboard
Any way to use the animation API?
Hi, I understand that this package does not support animations. However, I am trying to achieve animations on node-canvas and I was wondering if you can provide a workaround or point me in the right direction. What I had on my mind was to add a plugin for afterDraw
event (https://codepen.io/elizzk/pen/GRvaQBz?editors=0010) and render each frame into a picture to be used for a video generation later.
I think it is technically possible https://github.com/pankod/canvas2video (It uses fabric.js, and node-canvas)
Hi there,
Interesting update, I was not aware that node-canvas
now supports animations. I had a quick search and I think you are right in looking to use the afterDraw
event to capture the individual frames.
I would think the best approach would be to store the frame buffers in memory until the chart animation is complete then render them to the file system. This way we can check for long-running animations and infinite loops etc. But writing to the file system asynchronously in the loop may also work.
After generating the individual frame images we can do something like this to create gif or videos from the set of frames.
While it is not a common use case I would be interested in adding support for this in this library, so would like to hear if you manage to get this working!
Hi, sorry it took some time to get back to you. I had covid (one of the last people on earth I guess), but I am better now.
Yeah, I had the same idea. Currently, chartjs-node-canvas
sets animation: false
internally, I will try to make it work by updating your library. If it works as expected, maybe we can add this feature to the library to be used by others. I will let you know about the process.
Covid is still fairly rampant in my country (South Africa) and many people I know have come down with the new variant recently, which is particularly unpleasant apparently. My father has managed to contract 4 of the 5 variants over the pandemics timeline, tough as nails that man. Good to hear you have recovered.
Here is an alternative to modifying this lib using OOP (the JS version at least) overrides to re-enable animations, might be useful to test:
const { ChartJSNodeCanvas } = require('./index');
const fs = require('fs');
class CustomChartJSNodeCanvas extends ChartJSNodeCanvas {
renderChart(configuration) {
const canvas = this._createCanvas(this._width, this._height, this._type);
canvas.style = canvas.style || {};
configuration.options = configuration.options || {};
configuration.options.responsive = false;
configuration.options.animation = true;
const context = canvas.getContext('2d');
global.Image = this._image; // Some plugins use this API
const chart = new this._chartJs(context, configuration);
delete global.Image;
return chart;
}
}
async function main() {
const width = 400;
const height = 400;
const configuration = {
...
}
};
const chartJSNodeCanvas = new CustomChartJSNodeCanvas({ width, height });
const buffer = await chartJSNodeCanvas.renderToBuffer(configuration);
await fs.writeFile('./test.png', buffer, 'base64');
}
main();
You will have to implement/mock any browser APIs used by chartjs that nodejs does not provide, eg:
...
const chartJSNodeCanvas = new CustomChartJSNodeCanvas({ width, height });
global.window = {};
window.requestAnimationFrame = setImmediate; // Looks like the only one used, but not sure this works or if there are others
const buffer = await chartJSNodeCanvas.renderToBuffer(configuration);
...
As mentioned the heavy lifting will be in the collating of the image buffers and rendering them into something useful (gif/video/etc). I have no experience with this but I imagine putting the chartJSNodeCanvas.renderToBuffer
in a loop (emulating the fps), collecting the results and using another lib or something to generate the required result.
Good luck and interested in any findings!
Thanks a lot for the nice words. The covid has come down a lot (below 1000 cases) in my country, Turkey. But, I guess I had to get it after 2 years :)
I have experience working with images and videos, so as long as I generate multiple frame buffers, I can convert them to a video easily using fluent-ffmpeg. I made the same changes you provided yesterday, but I couldn't make it run an animation yet. the afterDraw event
is only called once for some reason. I may need to change the requestAnimationFrame function with a custom one to wait and call only 60 times per second (or the framerate that is passed)
Maybe the animation callbacks are a better option? They look to provide timing information also.
From what I know requestAnimationFrame
is synced to monitor refresh rate and guaranteed to be called once per frame to prevent things like shear, flicker, and frame skipping. We will have to watch for that using a *timeout
equivalent on node but I have no reason to expect them. A quick search found this polyfill, maybe it is more robust.
I made it all work except the requestAnimationFrame
part, I am unable to modify the window object so that it is used on chartjs. And since chartjs has a workaround that immediately calls the callback, it does not wait between frames as it should. as a result, it produces an image in between:
Here is the code if you would like to play around: https://github.com/el/ChartjsNodeCanvas
Unfortunately, that polyfill did not work correctly since it uses 16ms between frames instead of 1000/60. I was able to generate a given number of frames successfully. You can check the code at https://github.com/el/ChartjsNodeCanvas
Some frames still have issues related to drawing, but overall I think it works :)
Here is the final result: https://codepen.io/elizzk/pen/vYdpEEL?editors=1010
I have successfully generated frame images ☺️
https://codepen.io/elizzk/full/vYdpEEL
Would you like me to create a pr so that it can be part of this package?