joewalnes / smoothie

Smoothie Charts: smooooooth JavaScript charts for realtime streaming data
http://smoothiecharts.org
Other
2.24k stars 230 forks source link

Rendering onto an OffscreenCanvas using Web Workers #153

Open isaac-nls opened 1 year ago

isaac-nls commented 1 year ago

Hi,

I have a number of fairly high refresh rate charts all being rendered at once. To take pressure off the main thread, I was wondering if I could use web workers to offload the rendering.

Below is the code that I've tried so far. As you can see, I had to mock the window object and the getAttribute() calls for height and width. I'm now facing an error: Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The image source is detached, which I understand to mean that no frame is ready to draw.

Any insights would be greatly appreciated.

<!-- src/components/SmoothieChart.vue -->
<template>
  <div>
    <canvas ref="onscreenCanvas" :width="width" :height="height"></canvas>
  </div>
</template>

<script>
import { onMounted, ref } from 'vue';
import Smoothie from 'smoothie';

export default {
  props: {
    width: {
      type: Number,
      required: true,
    },
    height: {
      type: Number,
      required: true,
    },
  },
  setup(props) {
    const onscreenCanvas = ref(null);

    onMounted(() => {
      const offscreenCanvas = new OffscreenCanvas(props.width, props.height);
      const worker = new Worker(new URL('./smoothieWorker.js', import.meta.url));
      worker.postMessage({ offscreenCanvas }, [offscreenCanvas]);

      const ctx = onscreenCanvas.value.getContext('2d');

      worker.onmessage = (event) => {
        if (event.data.type === 'frameReady') {
          ctx.drawImage(offscreenCanvas, 0, 0);
        }
      };
    });

    return { onscreenCanvas };
  },
};
</script>
// src/components/smoothieWorker.js
self.window = self;
importScripts('https://cdn.jsdelivr.net/npm/smoothie@1.37.0/smoothie.js');

self.addEventListener('message', (event) => {
  const { offscreenCanvas } = event.data;
  createSmoothieChart(offscreenCanvas);
});

function createSmoothieChart(offscreenCanvas) {
  const smoothieChart = new Smoothie.SmoothieChart({
    grid: { strokeStyle: 'rgba(255, 255, 255, 0.1)', lineWidth: 1 },
    labels: { fillStyle: 'rgba(255, 255, 255, 0.8)' },
    millisPerPixel: 20,
    responsive: true,
  });

  const timeSeries = new Smoothie.TimeSeries();
  smoothieChart.addTimeSeries(timeSeries, { lineWidth: 2, strokeStyle: 'rgb(0, 255, 0)' });

  smoothieChart.streamTo(offscreenCanvas, 1000 / 60);

  setInterval(() => {
    timeSeries.append(new Date().getTime(), Math.random() * 100);
    self.postMessage({ type: 'frameReady' }); // Send a message back when a frame is ready
  }, 1000);
}
WofWca commented 1 year ago

Have you managed to make it work without smoothie?

isaac-nls commented 1 year ago

Yes I have, I have it working with Chart.js.

On Thu, 6 Apr 2023 at 13:52, WofWca @.***> wrote:

Have you managed to make it work without smoothie?

— Reply to this email directly, view it on GitHub https://github.com/joewalnes/smoothie/issues/153#issuecomment-1498527718, or unsubscribe https://github.com/notifications/unsubscribe-auth/AWDI6IE6RHZFSHL2PRVRBWDW7ZKYVANCNFSM6AAAAAAWU33VVY . You are receiving this because you authored the thread.Message ID: @.***>

-- Isaac Monteath

Nonlinear Solutions 3/63 Knutsford Avenue Rivervale WA 6103 M: (+61) 0479 128 404 www.nonlinear.com.au

WofWca commented 1 year ago

I mean try commenting out the smoothie code above and see if the setup works.