Search code examples
node.jscanvashtml5-canvaskonvajskonva

Using konva on a nodejs backend without konva-node


We are a team of 5 developers working on a video rendering implementation. This implementation consists out of two parts.

  1. A live video preview in the browser using angular + konva.
  2. A node.js (node 14) serverless (AWS lambda container) implementation using konva-node that pipes frames to ffmpeg for rendering a mp4 video in higher quality for later download.

Both ways are working for us. Now we extracted the parts of the animation that are the same for frontend and backend implementation to an internal library. We imported them in BE and FE. That also works nicely for most parts.

We noticed here that konva-node is deprecated since a short time. Documentation says to use canvas + konva instead on node.js. But this just doesn't work. If we don't use konva-node we cannot create a stage without a 'container' value. Also we cannot create a raw image buffer anymore, because stage.toCanvas() actually returns a HTMLCanvas, which does not have this functionality.

  • So what does konva-node actually do to konva API?
  • Is node.js still supported after deprecation of konva-node?
  • How can we get toBuffer() and new Stage() functionality without konva-node in node.js?

backend (konva-node)

import konvaNode = require('konva-node');  
this.stage = new konvaNode.Stage({
     width: stageSize.width,
     height: stageSize.height
});

// [draw stuff on stage here]

// create raw frames to pipe to ffmpeg
const frame = await this.stage.toCanvas();
const buffer: Buffer = frame.toBuffer('raw');

frontend (konva)

import Konva from 'konva';
this.stage = new Konva.Stage({
     width: stageSize.width,
     height: stageSize.height,
     // connect stage to html element in browser
     container: 'container'
});

// [draw stuff on stage here]

Finally in an ideal world (if we could just Konva in frontend and backend without konva-node the following should be possible for a shared code.

loading images

public static loadKonvaImage(element, canvas): Promise<any> {
    return new Promise(resolve => {
        let image;
        if (canvas) {
            // node.js canvas image
            image = new canvas.Image();
        } else {
            // html browser image
            image = new Image();
        }
        image.src = element.url;
        image.onload = function () {
            const konvaImage = new Konva.Image(
            {image, element.width, element.height});
            konvaImage.cache();
            resolve(konvaImage);
        };
    });
}

Many props to the developer for the good work. We would look forward to use the library for a long time, but how can we if some core functionality that we rely on is outdated shortly after we started the project?

Another stack overflow answer mentioned Konva.isBrowser = false;. Maybe this is used to differentiate between a browser and a node canvas?


Solution

  • We have solved the problems we had the following way:

    create stage (shared between Be+FE)

    public static createStage(stageWidth: number, stageHeight: number, canvas?: any): Konva.Stage {
        const stage = new Konva.Stage({
            width: stageWidth,
            height: stageHeight,
            container: canvas ? canvas : 'container'
        });
        return stage;
    }
    

    create raw image buffer (BE)

    const frame: any = await this.stage.toCanvas();
    const buffer: Buffer = frame.toBuffer('raw');
    

    loading images (shared between Be+FE)

    public static loadKonvaImage(element, canvas?: any): Promise<Konva.Image> {
       return new Promise(resolve => {
           const image = canvas ? new canvas.Image() : new Image();
           image.src = element.url;
           image.onload = function () {
               const konvaImage = new Konva.Image(
               {image, element.width, element.height});
               konvaImage.cache();
               resolve(konvaImage);
           };
       });
    }
    

    Two things we had to do.

    1. We have rewritten our whole backend and library code to use ESM modules and we got rid of konva-node and konva 7 in general.
    2. We defined the node module canvas in all places as any. It seems like Konva accepts more inputs than expected and like specified in the type interfaces of the classes. canvas is only installed in the backend and inserted in some library methods like shown above.

    @lavrton nice to hear from you. Your answer might also work for getting the Buffer, but you didn't answer on how to create the stage. Luckily we found a solution for both issues.