Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Screenshot functionality #80

Closed
TomasHubelbauer opened this issue May 1, 2021 · 8 comments
Closed

Screenshot functionality #80

TomasHubelbauer opened this issue May 1, 2021 · 8 comments
Labels
enhancement New feature or request open for vote Vote at https://wokwi.com/features

Comments

@TomasHubelbauer
Copy link

Hi, I'd like a request for a new feature to be added to capture and download a screenshot of the simulator circuit view at the moment the action is invoked (so during simulation).

I'd like to suggest that the dimensions of the screenshot are determined the same way the dimensions of the circuit are based on diagram.json when figuring out the initial zoom. This way, the screenshot size will be the same as long as diagram.json has not changed.

I think a button / menu item for this feature would be well suited to go to a dropdown menu next to the Simulation tab, similar to the dropdown menu after the file tabs, where items like Rename, Delete and New Item are.

Thank you

@urish urish added the enhancement New feature or request label May 1, 2021
@TomasHubelbauer
Copy link
Author

TomasHubelbauer commented May 14, 2021

I tried a few quick hacks with html2canvas. The snippets below can be pasted into the DevTools Console and ran. I tried these with my QR Code sketch.

Attempt 1

void async function() {
  const { default: html2canvas } = await import('https://cdn.jsdelivr.net/npm/html2canvas@1.0.0-rc.7/dist/html2canvas.esm.js');
  const element = document.querySelector('[class^="diagram-viewer"]');
  const canvas = await html2canvas(element);
  const url = canvas.toDataURL();
  window.open(url);
}()

image

Same result in both Firefox and Chrome. It looks like html2canvas might have a problem with SVG. In the latest version SVG should be supported, but I think it is not production ready yet. The other option is it might not support web components and shadow DOM. I looked around quickly and found something:

Attempt 2

This SO post contains a snippet which is supposed to "fix-up" SVG elements such that they work:

void async function() {
  // https://stackoverflow.com/a/59162619/2715716
  const elements = document.body.querySelectorAll('svg');
  elements.forEach(element => {
    element.setAttribute('width', element.getBoundingClientRect().width);
    element.setAttribute('height', element.getBoundingClientRect().height);
    element.style.width = null;
    element.style.height= null;
  });

  const { default: html2canvas } = await import('https://cdn.jsdelivr.net/npm/html2canvas@1.0.0-rc.7/dist/html2canvas.esm.js');
  const element = document.querySelector('[class^="diagram-viewer"]');
  const canvas = await html2canvas(element);
  const url = canvas.toDataURL();
  window.open(url);
}()

image

Same difference, so either SVG is not the problem or it is not easily solvable.

I tried looking into using canvg to pre-render SVGs into canvas elements so that html2canvas would not have to deal with SVGs, but I was not able to reference CanVG by either a script tag or an ESM async import without running into errors.

Status

In the end, I ended up using this minimal snippet:

void async function() {
  const { default: html2canvas } = await import('https://cdn.jsdelivr.net/npm/html2canvas@1.0.0-rc.7/dist/html2canvas.esm.js');
  const element = document.querySelector('[class^="diagram-viewer"]');
  const canvas = await html2canvas(element);
  const url = canvas.toDataURL();
  const a = document.createElement('a');
  a.download = 'wokwi.png';
  a.href = url;
  a.click();
}()

It does not render the Arduino in my particular sketch, but it does render the dot matrix displays which is enough for my use-case. Someone else might find use for this.

I don't think using html2canvas is the best way to implement this feature, I think the most reliable way would for Wokwi to have two renderers, one DOM based, and one canvas-based. However, this would add a lot of complexity for a single feature.

So, weighting pros and cons, html2canvas does make some sense if it can be successfully combined with CanVG or if in the future the library is improved to be able to handle the way Wokwi renders the SVGs without problems. For now it is not there yet, so the screenshot functionality cannot be implemented using it alone.

@TomasHubelbauer
Copy link
Author

Also, this does not help the implementation of this feature, but in case anyone is looking for a way to screenshot the diagram without having to manually select the area (introducing impreciseness), you can use your browser's DevTools screenshot-node feature:

@urish
Copy link
Contributor

urish commented May 15, 2021

Wow, thank you so much for looking into this!

Seems like taking the screen shot client-side isn't trivial indeed. I could probably do some setup with server-side screenshots (similar to how I generate the thumbnails), but it seems like the dev tools screenshots provide a pretty good solution for the time being?

@TomasHubelbauer
Copy link
Author

Are you using Puppeteer to generate the thumbnails? That sounds much more reliable than trying to do this client side. Yesterday I was inspired to look into an alternative way to do this client side and came up with https://github.com/TomasHubelbauer/selfie but I don't think it can be made smooth enough to be a viable option either.

@urish
Copy link
Contributor

urish commented May 16, 2021

and came up with https://github.com/TomasHubelbauer/selfie

Okay, I'm impressed! Does this mean you need the screen shot feature so badly that you were so motivated to make it happen?

@TomasHubelbauer
Copy link
Author

I didn't think to use the Screenshot Node browser DevTools functionality before I started looking into this issue. With it, I pretty much have all I need: an easy, fast and reliable way to update screenshots of the Wokwi simulation running that I have in a readme of my project. Cropping the simulation area out by hand is prone to imperfect dimensions and with the screenshots side by side, this becomes apparent. Check out the readme to see what I mean: https://github.com/TomasHubelbauer/arduino-qr. The screenshots that are there now are obtained using the Screenshot Node feature and they are perfectly aligned.

The Selfie project came about through me being disappointed with html2canvas, trying to contribute to it and midway having this idea to use the getDisplayMedia API popping into my head - I basically nerdsniped myself with it. From then on I had to get it to work to scratch that itch.

For Wokwi to have the feature, it is more complex than I initially thought:

Doing it server-side sounds really easy, Puppeteer or Playwright essentially give you an API to the Screenshot Node feature, however, there is no good way that I could think of that would enable the server side to take into an account the local state of the client side to make sure the screenshot is a faithful representation of what the user sees. Maybe it is possible to somehow dump the memory of the program and recover it on the server to reach the same point of the simulation (including all of the side effects and crucially, side causes). A concrete example: say I used your new mic part you introduced recently and I built a VU meter with it. How could the server side be implemented to display the VU meter exactly to what it looks like on the client side as I'm talking to the mic?

Doing it client-side, we have the html2canvas option which doesn't look mature enough, we could do what I do in Selfie, but the problem there is at most you'll get a texture of the tab, not just the simulation area. On top of it, Chrome will give you just the page content, but Firefox will include the browser UI even if you select just your tab (I'm working on a postprocessing step which would further crop this but the point still stands). Safari doesn't even allow sharing just a tab, it will share your whole screen. And - it is weird to ask your user to share their screen just to be able to export a screenshot of your own tab. So the only thing that remains on the client side is to maintain two renders, one DOM based and one canvas based, and like I said in my previous post, that's just too much of a maintenance burden for a single niche feature.

In conclusion, despite how much I'd like to see this feature, I don't think there is a good way to do it. Maybe the UI could have a screenshot button that would tell people to use the DevTools Screenshot Node feature (and how to locate the right node), but that's inelegant similarly to Selfie having the user select your own tab.

Maybe in the future if they self-screenshot web API proposal is actually implemented by browsers, it could be used, because that would give you the surface of just your tab, no user interaction needed, no browser UI (with possibly variable, unpredictable size to confidently automatically crop) - and you could crop the simulation area out because it is easy to determine its position and side relative to the tab surface origin.

What do you think? I'm thinking I'll close this issue because I have my solution and due to the above considerations, I don't think it is worth it to try featurizing this until and if ever we have the self-screenshot browser API.

@TomasHubelbauer
Copy link
Author

Update: I have more of less finished what I believe is the smoothest possible flow in my approach of using the screen share API: https://github.com/TomasHubelbauer/selfie. If you decide to implement screenshot functionality and wanted to do it using selfie, the flow would be like this:

  • Tell the user that to take the screenshot, you're going to ask them to approve a screen share request
  • Direct them to select either the current tab or the full screen in the browser selector UI
  • Call the library similarly to how is shown in selfie.js: make sure it is called from a user gesture (button click) and provide selector for the desired element to crop out of the screenshot

This is not ideal IMO, it asks too much of the user, but it was fun to see how far the idea can be pushed. I'll make the author of the self-screenshot browser API proposal aware of this experiment so they can use it to support the argument for an inclusion of the self-screenshot API in the browser and maybe something will come of that. :-)

@urish urish added the open for vote Vote at https://wokwi.com/features label Sep 16, 2021
@urish
Copy link
Contributor

urish commented Mar 26, 2022

Closing as this issue hasn't received any votes in 6 months

@urish urish closed this as completed Mar 26, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request open for vote Vote at https://wokwi.com/features
Projects
None yet
Development

No branches or pull requests

2 participants