0

I have a React app that displays a toolbar and 2 canvases built-in Three.js. I would like to take a screenshot of the entire app. I tried already niklasvh/html2canvas

        const element = document.getElementsByClassName("contentContainer-5")[0] as HTMLElement;
        const url = html2canvas(element)
            .then((canvas)=> {
                return canvas.toDataURL();
            })
        }
        const screenshotObject = {
            url: url,
            width: 128,
            height: 64,
        }

        return screenshotObject
    }

and BLOB html5

takeScreenshot() {
        const screenshot = document.documentElement
            .cloneNode(true) as Element;
        const blob = new Blob([screenshot.outerHTML], {
            type: 'text/html'
        });
        return blob;
    }
    
    generate() {
        window.URL = window.URL || window.webkitURL;
        window.open(window.URL
            .createObjectURL(this.takeScreenshot()));
    }

In the first case, the screenshot's URL is very long but the image is empty. In the second case, HTML and CSS is perfectly snapshotted but canvases are empty.

sakul
  • 124
  • 2
  • 12

2 Answers2

0

.cloneNode() only copies the DOM and canvasses are not a part of DOM. A workround could be done with the cloneNode function (as done here)

const takeScreenshot = () => {
  const _cloneNodeFn = HTMLCanvasElement.prototype.cloneNode;

  HTMLCanvasElement.prototype.cloneNode = function () {
    const customCloneNodeFn = _cloneNodeFn.apply(this, arguments);

    if (this.getContext("2d")) {
      customCloneNodeFn.getContext("2d").drawImage(this, 0, 0);
    }

    return customCloneNodeFn;
  };
};

const _canvas = document.querySelector("canvas");
_canvas.getContext("2d").fillRect(20, 20, 20, 20);

for (let i = 0; i < 20; i++) {
  document.body.appendChild(_canvas.cloneNode());
}

Note: cloneNode has to be called on the canvas directly, and not the parent node.

Aahan Agarwal
  • 391
  • 2
  • 6
0

In order to take a screenshot of a Three.js scene using Javascript, you need to create a new canvas context with the original canvas' properties, draw the image onto the context, convert it to a blob, then make it available on an invisible a tag which is automatically click and downloads a screenshot of your Three.js scene.

I created the following barebones code that takes screenshots of scenes:

export const screenshot = async (idx) => {
    const element = document.getElementById('existing-canvas-id');
    const canvas = document.createElement('new-canvas-id');
    
    canvas.width = element.width;
    canvas.height = element.height;

    const context = canvas.getContext('2d');

    let url = element.toDataURL();
    let img = new Image();
    
    await new Promise(r => img.onload=r, img.src=url);
    
    context.drawImage(img, 0, 0);

    const blob = canvas.toBlob();
    const link = document.createElement('a');

    link.href = URL.createObjectURL(blob);
    link.download = `${index}.png`;
    link.dispatchEvent(new MouseEvent('click'));
  }
};
Wesley LeMahieu
  • 2,296
  • 1
  • 12
  • 8