4

I have a project where there are two canvases that need to be combined, one inside p5 and one outside. The first is a p5js sketch (created via createCanvas that is essentially a drawing application) and the second is auto-created via an API call to a map service (our use case is to basically annotate a map).

For reference, the API looks for a div with a particular ID and then appends the new canvas element.

What we'd like to do is stitch together the two images into a savable image for the user. The bottom layer would be coming from the map API canvas and the top layer would be the user-drawn annotations.

The issue we're facing is that the automatically-created canvas is coming in as a webgl context, so we don't seem to be able to use any of the basic methods for getting canvas data in a 2d context.

Is there an easy way to do this that I'm overlooking, or do I need to start figuring out how to parse out webgl data into a graphics object's pixels array?

The picture below is what we have so far - the drawing works, the map is loaded fine, now we just need to save them as a full image for the user.

screenshot

VC.One
  • 14,790
  • 4
  • 25
  • 57
erik
  • 3,810
  • 6
  • 32
  • 63
  • did you try **html2canvas** library – Dulan Jayawickrama Jul 28 '23 at 06:23
  • I haven't - does that just take a screenshot of the canvas? – erik Jul 28 '23 at 21:14
  • @erik I suggest you put some **minimum testable code** that reproduces your problem. You don't have to share your real project, just some code to recreate the situation itself. **For example**: Why not show code of a basic P5/3D canvas (_ie:_ 200x200 pixels) with a blue bg and then also add a white circle in some 2D canvas where the challenge is to put the white circle (2D) on top of the blue background (3D). The solution is then applied later to your real code (that you don't need to show in a public forum). Thanks – VC.One Jul 31 '23 at 10:21
  • 1
    @erik PS: Most likely your answer is in this one command: [webgl.readPixels](https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/readPixels) where you get back a 2D snapshot of the 3D canvas. – VC.One Jul 31 '23 at 10:26
  • Sorry @VC.One - I missed the bounty expiration. – erik Aug 02 '23 at 20:33
  • @erik Don't worry about the bounty, I need some example code from you to start with. Otherwise this is the [kind of webgl code setup](https://stackoverflow.com/a/68037735/2057709) that I typically work with. I don't want to write all that just to access the output canvas with one extra line (_eg:_ readPixels)... On the other hand, P5JS "sketches" seem short. Can you make such [a basic sketch](https://editor.p5js.org/p5/sketches) that I can edit myself with extra code? – VC.One Aug 08 '23 at 11:24
  • 1
    @VC.One - I think we actually figured out the answer in the interim and it explains a lot. With the mapping sw we were using (mapbox) there was a flow area that set permissions based on your account level. The download/read flows were disabled with our 'free' use of the API, so that explained why every single thing we tried showed up as blank. I'm going to vote to close since the solution wasn't related to the question. – erik Aug 09 '23 at 16:03
  • 1
    I’m voting to close this question because the solution ended up being unrelated to the problem. – erik Aug 09 '23 at 16:03

1 Answers1

0

Combining content from different canvases with different contexts can indeed be a bit tricky, especially when one of the canvases is using a WebGL context. Since p5.js provides limited support for working with WebGL contexts directly in the same way you can with 2D contexts, you'll need to find an alternative approach.

html2canvas library is something, which allows you to capture the contents of an HTML element (including canvases) and convert them into an image. This way, you can capture both canvases (p5.js and the map API canvas), combine them into a single image, and provide it as a download link for the user.

  1. Add the html2canvas library to your project.

    <script src="https://html2canvas.hertzen.com/dist/html2canvas.min.js"></script>

    (Make sure both the p5.js canvas and the map API canvas are loaded and visible on the page.)

  2. In the event handler function for the button, you can use the html2canvas library to capture both canvases and then combine them into a single image. Once that's done, you can offer the combined image as a download link or perform any other action you desire.

example of what the event handler function might look like:

// Event handler function for the button to save the combined image
function saveCombinedImage() {
  // Get references to the p5.js canvas and the map API canvas
  const p5Canvas = document.getElementById('your-p5-canvas-id');
  const mapApiCanvas = document.getElementById('your-map-api-canvas-id');

  // Use html2canvas to capture both canvases
  html2canvas(p5Canvas).then((p5CanvasCapture) => {
    html2canvas(mapApiCanvas).then((mapApiCanvasCapture) => {
      // Create a new canvas to combine the captured canvases
      const combinedCanvas = document.createElement('canvas');
      combinedCanvas.width = p5CanvasCapture.width;
      combinedCanvas.height = p5CanvasCapture.height;
      const ctx = combinedCanvas.getContext('2d');

      // Draw the map API canvas as the bottom layer
      ctx.drawImage(mapApiCanvasCapture, 0, 0);

      // Draw the p5.js canvas as the top layer
      ctx.drawImage(p5CanvasCapture, 0, 0);

      // Now the `combinedCanvas` contains the merged image of both canvases
      // You can offer this as a download link or use it as needed.

      // For example, create a link for the user to download the image
      const downloadLink = document.createElement('a');
      downloadLink.href = combinedCanvas.toDataURL();
      downloadLink.download = 'combined_image.png';
      downloadLink.click();
    });
  });
}

In this example, replace your-p5-canvas-id and your-map-api-canvas-id with the actual IDs of your p5.js canvas and the map API canvas, respectively.

  • 1
    Interestingly when I go this route I end up with a tiny little white box and no data – erik Aug 02 '23 at 20:33