Search code examples
reactjshtml5-canvaskonvajsreact-konva

React Konva custom context/canvas (for use with canvas2svg)


We're using react-konva, which renders a <Stage> component that manages an underlying canvas/context.

We want to use canvas2svg with it, which is basically a wrapper around a canvas context that tracks all draws to the canvas and maintains an svg representation for when you're ready to access it. It doesn't modify the canvas api, so in theory, Konva would be unaffected in that its canvas draw calls would be the same - they'd just need to be on the context generated by canvas2svg, rather than the one konva automatically generates.

We're looking for something like this, but it doesn't seem to exist. Are there any ways we could use a ref or otherwise hack Konva into using a C2S context? Or maybe we're missing a built-in property.

var c2sContext = new C2S(500,500);

<Stage context={c2sContext}>

Solution

  • canvas2svg doesn't actually work alongside a normal rendered canvas (it's an alternative to one - for example, the arc() method just renders to svg, not svg + canvas), so you need to replace the render context with a c2s instance, call render so the methods like arc() get called, and then set it back.

    You can use a ref on the konva Layer and run code like the following on a button press:

    setTimeout(() => {
      var oldContext = this.layerRef.canvas.context._context;
    
      var c2s = this.layerRef.canvas.context._context = C2S({...this.state.containerSize, ctx: oldContext});
    
      this.forceUpdate();
    
      setTimeout(() => {
        console.log(c2s.getSerializedSvg());
    
        this.layerRef.canvas.context._context = oldContext;
    
        this.forceUpdate();
      })
    }, 5000);
    

    Two possible solutions that skirt around the need for canvas2svg:

    1. Generating an SVG string manually without rendering svg elements
    2. Rendering svg elements rather than canvas elements, and converting the dom to an html string

    Obviously #2 has performance issues and #1 requires messier code - and #2 still requires some additional code/messiness, but both are good options to be aware of.