1

I recently started to use the Konva library to create some images online from data being fed by a Web API. The images typically consist of some text, some rectangles, and some small images. The images also come from the same Web API. I convert them into Base64 strings and assign those to the img.src tags. All of this works just fine and when it is done, I see a canvas that has all the text, rects and images as I expect to see it.

The problem comes in when I try to create a PNG from the stage/canvas. The canvas is rendering to an image and all the text and lines are there, but the images are not. At first I was getting an all black background, but then I found the following article that mentions the need to render as a PNG and/or make a non-transparent background. I mention that only because I'm wondering if something similar is going on here?

canvas.toDataURL results in solid black image?

Anyway, here are some code snippets, maybe they will be of help in figuring this out?

<div id="container"></div>

const stage = new Konva.Stage({
    container: 'container',
    width: width,
    height: height
});

const layer = new Konva.Layer();

switch...

    case 'Picture':
    {
        // Get the image from the Web API
        const image = this.store.images.getThumb(
            f.fldData.fldDataValue
        );

        const img = new Image();
        const t = this;

        // Create the Konva.Image() and add it to the stage
        img.onload = function() {
        // resize image keeping aspect ration
            response = t.calculateAspectRatioFit(img, w, h);
            const imgWidth = response.width;
            const imgHeight = response.height;

            const chPicture = new Konva.Image({
                x: x,
                y: y,
                image: img,
                width: imgWidth,
                height: imgHeight
            });

            // add the image to the layer
            layer.add(chPicture);

            // add the layer to the stage
            stage.add(layer);
        };

        image.subscribe(results => {
            // Convert image to Base64
            const fileReader = new FileReader();
            fileReader.readAsDataURL(results);
            // When it's ready, assign Base64 string to img src
            fileReader.onloadend = () => {
                img.src = fileReader.result;
            };
        });
    }
    break;

...

// Convert the image from the Web API to Base64
const dataURL = stage.toDataURL({
    mimeType: 'image/png',
    quality: 1.0
});

// Resize the image made from the canvas and display it on the screen
const container = document.getElementById('displayImage');
const i = new Image();
i.style.width = '320px';
i.style.height = '196px';
i.src = dataURL;
container.appendChild(i);
Todd Davis
  • 5,855
  • 10
  • 53
  • 89
  • Image.onload will fire asynchronously. So at the tine you call toDataURL it ha not been called yet. – Kaiido Jul 21 '18 at 00:21

1 Answers1

1

img.onload works asynchronously. It means the callback function will be executed later when image is loaded.

As I can see you are saving to base64 synchronously after you created a whole stage:

// Convert the image from the Web API to Base64
const dataURL = stage.toDataURL({
    mimeType: 'image/png',
    quality: 1.0
});

You just need to make sure that ALL images are loaded. And only then conver to dataURL.

lavrton
  • 18,973
  • 4
  • 30
  • 63
  • The stage doesn't seem to have an event that fires when it is fully loaded? How can I tell when that has happened so that I can call the .toDataUrl() at the appropriate time? – Todd Davis Jul 25 '18 at 18:26
  • @ToddDavis The stage doesn't have such event. But each `new Image();` has `onload` method. Check inside it that all images are loaded. – lavrton Jul 26 '18 at 06:34