Recently, I have studied how to use JavaScript to implement web screenshots, including JS running in the browser and nodeJs running in the background. I mainly looked at the following:
PhantomJS
Puppeteer(chrome headless)
SlimerJS
dom-to-image
html2canvas
The web pages tested use WebGL technology, so the following summary will be related to WebGL.
Noun definition
headless browser
Interfaceless browser, mostly used for web page automation testing, web screenshots, web page network monitoring, etc.
PhantomJS
PhantomJS is a headless browser that can be programmed through JS and uses the QtWebKit kernel.
The code to implement the screenshot, assuming the file name is:
// Create a web page instancevar page = require('webpage').create(); // Load the page('/', function () { // Take a screenshot of the web page and save it to a file (''); (); })
run:
phantomjs
There is no problem with normal pages, but if you run a page containing WebGL, you will find that the screenshot is wrong. After some investigation, it was found that WebGL, github issue is not supported.
Summarize:
PhantomJs has been stopped, so it is not recommended to continue using it. One reason for stopping maintenance is that the headless version released by Chrome has had a certain impact on it.
WebGL is not supported. However, some developers still say that they can add WebGL support to PhantomJS themselves. However, this solution is currently beyond my knowledge and has not continued to study it.
Puppeteer(chrome headless)
Puppeteer is a Node library that provides an API for controlling chrome and chromium. The headless mode is run by default and the interface is also supported.
Code to implement screenshots:
const puppeteer = require('puppeteer'); (async () => { const browser = await (); const page = await (); await ({ // Set the window size width: 600, height: 800 }); await (''); // Open the page await ({path: ''}); // path: save the screenshot file await (); })();
run:
node
Let's take a look at it nextscreenshot
The implementation principle of the method:
screenshot
The source code is located inlib/cjs/puppeteer/common/
In the file, it is an asynchronous method:
async screenshot(options = {}) { // ... return this._screenshotTaskQueue.postTask(() => this._screenshotTask(screenshotType, options)); } async _screenshotTask(format, options) { // ... const result = await this._client.send('', { format, quality: , clip, }); // ... }
thisthis._client.send
What's it? Don't worry, let's re-read the definition of Puppeteer:
“Puppeteer is a Node library which provides a high-level API to control Chrome or Chromium over the DevTools Protocol.
”
Have you seen the DevTools Protocol at the end? What is this:
“The Chrome DevTools Protocol allows for tools to instrument, inspect, debug and profile Chromium, Chrome and other Blink-based browsers.
”
For detailed explanations, please read this blog.
Simply put, Puppeteer sends data following Chrome Devtools Protocol to the browser through WebSocket, and commands the browser to perform some operations. Then, the browser returns the result to Puppeteer through WebSocket. This process is asynchronous, so looking at the source code you will find a lot of async/await.
soscreenshot
The method is to call the captureScreenshot of Chrome Devtools Protocol.
Summarize:
Supports WebGL.
If the web page is more complicated, the screenshot time is quite long. The page I tested was several hundred milliseconds.
Puppeteer is a package of (CDP) Chrome Devtools Protocol functionality. Most of the functions are transmitted to CDP through WebSocket.
SlimerJS
SlimerJS is similar to PhantomJS. The difference is that SlimerJS is based on Firefox's browser engine Gecko, not Webkit.
SlimerJS can be installed through npm, the latest version is. However, the compatible version of Firefox is 53.0 to 59.0. I think the latest version of Firefox is now 82. Because I have installed the latest version of Firefox in my machine, I also have to install an old version of Firefox, such as 59.0. You can refer to this article to install the old version of Firefox browser. I have a Mac system, and it feels that installation is quite easy.
Code to implement screenshots:
var page = require('webpage').create(); ("", function (status) { = { width:1024, height:768 }; (''); });
run
// Set Firefox path for mac operating systemexport SLIMERJSLAUNCHER=/Applications//Contents/MacOS/firefox ./node_modules/.bin/slimerjs // I'm a locally installed slimer package
It should be noted thatSLIMERJSLAUNCHER=/Applications//Contents/MacOS/firefox
The startup is the default installation path of Firefox. Because I had Firefox browser from the beginning, I started the latest version of the browser, and then I reported an error, saying it was incompatible. I installed a version of Firefox 59 in the previous section, so what is the path to this Firefox browser?
In the application, I named this old version of Firefox Firefox59, and then this path is/Applications//Contents/MacOS/firefox
. ResetSLIMERJSLAUNCHER
After the 59 version of Firefox browser, I found that it was successful.
However, Puppeteer will open the browser interface by default, which is non-headless mode. If you want to use headless mode, you can
./node_modules/.bin/slimerjs --headless
However, WebGL is not supported in headless mode.
When I was writing the example, one of the obvious differences I found is that Puppeteer screenshots are asynchronous functions, while SlimerJS screenshots are synchronous functions? Driven by curiosity, I looked at the source code (src/modules/slimer-sdk/):
render: function(filename, options) { // ... let canvas = ( , , , this); } (function(blob) { let reader = new (); = function() { content = ; } (blob); }, , ); // ... }
(src/modules/):
getScreenshotCanvas : function(window, ratio, onlyViewport, webpage) { // ... // create the canvas let canvas = ("http:///1999/xhtml", "canvas"); = canvasWidth; = canvasHeight; let ctx = ("2d"); (ratio, ratio); (window, , , , , "rgba(0,0,0,0)"); (); return canvas; }
The key code is that line. What? Does JS native API also support direct screenshots?
(): Only Firefox supports non-standard definition standard API that has been abandoned.
Summarize
The Firefox versions supported by version 1.0 are 53.0 to 59.0. The latest version of Firefox is not guaranteed to be available.
In headless mode, WebGL is not supported.
dom-to-image
dom-to-image: an open source library for front-end screenshots. The working principle is:
SVG's foreignObject tag can wrap any html content. Then, in order to render a node, the following steps are mainly carried out:
Recursively copy the original dom node and descendant node;
Recursively apply the styles of the original node and descendant node to the corresponding copied nodes and descendant nodes;
Font processing;
Image processing;
Serialize the copied node, insert it into foreignObject, then form an svg, and then generate a data URL;
If you want to get PNG content or original pixel values, you can first create an image using the data URL, render the image using an off-screen canvas, and then get the desired data from the canvas.
During the test, I found that external resources could not be loaded, so I gave up after a simple understanding.
html2canvas
html2canvas. I checked the Internet and felt that there is an article written well: a brief analysis of two ways to implement web screenshots in js. If you are interested, you can take a look.
Unverified conjecture
Although the two latter are front-end implementation methods, combined with the headless library mentioned above, back-end screenshots can also be implemented. Taking Puppeteer's API as an example, you can use it first(options)
Add a library of front-end screenshots to the web page, and then(pageFunction[, ...args])
In-housepageFunction
Just write the corresponding screenshot code in the function, because the execution context of the pageFunction is the web page context, so you can get itdocument
etc.
This is the end of this article about 5 methods to implement web screenshots in JavaScript. For more related JavaScript web screenshots, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!