chartjs / Chart.js

Simple HTML5 Charts using the <canvas> tag
https://www.chartjs.org/
MIT License
64.91k stars 11.93k forks source link

Support for SharedWorkers #11890

Open tobiu opened 3 months ago

tobiu commented 3 months ago

Expected behavior

I am actually not sure if this is a feature request or a bug report.

Am I the first one trying to use this library inside a SharedWorker?

If I am using a dedicated worker:

https://github.com/user-attachments/assets/c5e9e8ee-d3a5-48a1-aacc-f8ea439bd8fe

this works fine (except for the resize-logic, which i will look into).

Current behavior

If I am switching the dedicated worker to a shared worker, i get:

https://github.com/user-attachments/assets/9f746717-afe0-41f4-ad44-5d044f5d6999

Without looking deeper into the code base, I assume that the chart.update() logic is trying to use requestAnimationFrame().

This API is not available inside SharedWorkers. If there is a fallback, it needs more love.

Reproducible sample

https://github.com/neomjs/chartjs-demo

Optional extra steps/info to reproduce

No response

Possible solution

No response

Context

No response

chart.js version

v4.4.4

Browser name and version

No response

Link to your project

No response

tobiu commented 3 months ago

i did step quite a bit through the code: https://github.com/chartjs/Chart.js/blob/ea88dba68d41d4974c1fff5ce1c60f5d68279c13/src/helpers/helpers.extras.ts#L13

/**
* Request animation polyfill
*/
export const requestAnimFrame = (function() {
  if (typeof window === 'undefined') {
    return function(callback) {
      return callback();
    };
  }
  return window.requestAnimationFrame;
}());

this one is pure evil and deserves a new ticket => dedicated workers do have requestAnimationFrame(). no window => applying the polyfill for no reason in dedicated workers. [update] new ticket: https://github.com/chartjs/Chart.js/issues/11891

but inside my chart demo, there are no chart animations, so this one does not even get called.

if i call this one for 2 charts (bar & line):

    addRandomData(opts) {
        let max   = Neo.isNumber(opts?.max) ? opts.max : 20,
            min   = Neo.isNumber(opts?.min) ? opts.min :  0,
            value = Math.floor(Math.random() * (max - min + 1) + min);

        this.charts.forEach(chart => {
            chart.data.datasets[0].data.push(value);
            chart.data.labels.push('Random ' + (value));
            chart.update()
        })
    }

i get: Screenshot 2024-08-31 at 20 24 28

 update(target, values) {
        if (this._properties.size === 0) {console.log(target, values);
            Object.assign(target, values);
            return;
        }
        const animations = this._createAnimations(target, values);
        if (animations.length) {
            animator.add(this._chart, animations);
            return true;
        }
    }

this feels like too many redundant calls, but i might be wrong.

further drilling in i ended up inside render() which then triggered draw() for several contexts.

the methods were executed very fast inside my SharedWorker, but i am lacking the time to drill down deeper to see which delayed callbacks should get triggered, but won't.

i hope the input helps!

best regards, tobi