最近研究了下如何利用JavaScript实现网页截屏,包括在浏览器运行的JS,以及在后台运行的nodeJs的方法。主要看了以下几个:
创新互联2013年至今,先为蒙阴等服务建站,蒙阴等地企业,进行企业商务咨询服务。为蒙阴企业网站制作PC+手机+微官网三网同步一站式服务解决您的所有建站问题。
测试的网页使用了WebGL技术,所以下面的总结会和WebGL相关。
名词定义
headless browser
无界面浏览器,多用于网页自动化测试、网页截屏、网页的网络监控等。
PhantomJS
PhantomJS是可以通过JS进行编程的headless浏览器,使用的是QtWebKit内核。
实现截屏的代码,假设文件名为github.js:
- // 创建一个网页实例
- var page = require('webpage').create();
- // 加载页面
- page.open('http://github.com/', function () {
- // 给网页截屏,保存到github.png文件中
- page.render('github.png');
- phantom.exit();
- })
运行:
- phantomjs github.js
普通的页面没有问题,但是如果运行包含WebGL的页面,发现截屏不对。经过一些调查,发现不支持WebGL,github issue。
总结:
Puppeteer(chrome headless)
Puppeteer是一个Node库,提供了控制chrome和chromium的API。默认运行headless模式,也支持界面运行。
实现截屏的代码example.js:
- const puppeteer = require('puppeteer');
- (async () => {
- const browser = await puppeteer.launch();
- const page = await browser.newPage();
- await page.setViewport({ // 设置视窗大小
- width: 600,
- height: 800
- });
- await page.goto('https://example.com'); // 打开页面
- await page.screenshot({path: 'example.png'}); // path: 截屏文件保存路径
- await browser.close();
- })();
运行:
- node example.js
接下来看下screenshot方法的实现原理:
screenshot的源码位于lib/cjs/puppeteer/common/Page.js文件中,是一个异步方法:
- async screenshot(options = {}) {
- // ...
- return this._screenshotTaskQueue.postTask(() => this._screenshotTask(screenshotType, options));
- }
- async _screenshotTask(format, options) {
- // ...
- const result = await this._client.send('Page.captureScreenshot', {
- format,
- quality: options.quality,
- clip,
- });
- // ...
- }
这个this._client.send又是个什么东西?别急,我们重新看下Puppeteer的定义:
Puppeteer is a Node library which provides a high-level API to control Chrome or Chromium over the DevTools Protocol.
看到最后面那个DevTools Protocol了吗?这是个什么东西:
The Chrome DevTools Protocol allows for tools to instrument, inspect, debug and profile Chromium, Chrome and other Blink-based browsers.
详细的解释可以看这篇博客。
简单来说,Puppeteer就是通过WebSocket给浏览器发送遵循Chrome Devtools Protocol的数据,命令浏览器去执行一些操作。然后,浏览器再通过WebSocket把结果返回给Puppeteer。这个过程是异步的,所以看源代码会发现好多async/await。
所以screenshot方法是调用了Chrome Devtools Protocol的captureScreenshot。
总结:
SlimerJS
SlimerJS和PhantomJS类似。不同点是SlimerJS是基于火狐的浏览器引擎Gecko,而不是Webkit。
SlimerJS可以通过npm安装,最新版本是1.x。不过兼容的火狐版本是53.0到59.0。我看现在火狐最新版本都82了。因为我本机是安装了火狐最新版本的,所以我还得安装一个老版本的火狐,比如59.0。可以参考这篇安装旧版本的火狐浏览器。我是mac系统,感觉安装还是挺容易的。
实现截屏的代码screenshot.js:
- var page = require('webpage').create();
- page.open("http://slimerjs.org", function (status) {
- page.viewportSize = { width:1024, height:768 };
- page.render('screenshot.png');
- });
运行
- // mac操作系统设置火狐路径
- export SLIMERJSLAUNCHER=/Applications/Firefox.app/Contents/MacOS/firefox
- ./node_modules/.bin/slimerjs screenshot.js // 我是局部安装的slimer包
需要注意的是SLIMERJSLAUNCHER=/Applications/Firefox.app/Contents/MacOS/firefox启动的是火狐默认的安装路径,因为我一开始就有火狐浏览器,所以启动的是最新版本的浏览器,然后就报错了,说不兼容。在前面我安装过一个59版本的火狐,那么这个火狐浏览器的路径是什么?
在应用程序里面我把这个旧版本的火狐命名为Firefox59,然后这个路径就是/Applications/Firefox59.app/Contents/MacOS/firefox。重新设置SLIMERJSLAUNCHER为59版本的火狐浏览器之后,发现就能成功了。
不过,Puppeteer默认会打开浏览器界面,也就是non-headless模式。如果要使用headless模式,可以
- ./node_modules/.bin/slimerjs --headless screenshot.js
不过,headless模式下,不支持WebGL。
我在写例子的时候,发现的一个明显的不同就是Puppeteer截屏是异步函数,而SlimerJS截屏是同步函数?好奇心驱使下,看了下源码(src/modules/slimer-sdk/webpage.js):
- render: function(filename, options) {
- // ...
- let canvas = webpageUtils.getScreenshotCanvas(
- browser.contentWindow,
- finalOptions.ratio,
- finalOptions.onlyViewport, this);
- }
- canvas.toBlob(function(blob) {
- let reader = new browser.contentWindow.FileReader();
- reader.onloadend = function() {
- content = reader.result;
- }
- reader.readAsBinaryString(blob);
- }, finalOptions.contentType, finalOptions.quality);
- // ...
- }
webpageUtils.getScreenshotCanvas(src/modules/webpageUtils.jsm):
- getScreenshotCanvas : function(window, ratio, onlyViewport, webpage) {
- // ...
- // create the canvas
- let canvas = window.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
- canvas.width = canvasWidth;
- canvas.height = canvasHeight;
- let ctx = canvas.getContext("2d");
- ctx.scale(ratio, ratio);
- ctx.drawWindow(window, clip.left, clip.top, clip.width, clip.height, "rgba(0,0,0,0)");
- ctx.restore();
- return canvas;
- }
关键代码就是那行ctx.drawWindow。what?JS原生API还支持直接截屏?
CanvasRenderingContext2D.drawWindow():只有火狐支持,已经被废弃掉的非规范定义的标准API。
总结
dom-to-image
dom-to-image:前端截屏的开源库。工作原理是:
SVG的foreignObject标签可以包裹任意的html内容。那么,为了渲染一个节点,主要进行了以下步骤:
测试的时候,发现外部资源不能加载,所以简单的了解了后就放弃了。
html2canvas
html2canvas。网上查了下感觉有一篇文章写的挺好的:浅析 js 实现网页截图的两种方式。感兴趣的可以看下。
未验证的猜想
虽然后面这两种是前端的实现方式,但是结合前面讲的headless库,也是可以实现后端截屏的。以Puppeteer的API为例,可以首先使用page.addScriptTag(options)往网页中添加前端截屏的库,然后在page.evaluate(pageFunction[, ...args])中的pageFunction函数里面写相应的截屏代码就可以了,因为pageFunction的执行上下文是网页上下文,所以可以获取到document等对象。
总结
对截屏的一个简单研究,写篇博客整理下思路。
网站栏目:JavaScript实现网页截屏方法总结
URL分享:http://www.shufengxianlan.com/qtweb/news11/530361.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联