JavaScript从入门到精通之JavaScript实现网页截屏方法总结
小职 2020-12-28 来源 : 阅读 714 评论 0

摘要:本篇主要介绍了如何利用Javascript实现网页截屏,以及在浏览器运行的JS,以及在后台运行的nodeJs的方法。希望对于前端框架Javascript的学习有所帮助。

本篇主要介绍了如何利用Javascript实现网页截屏,以及在浏览器运行的JS,以及在后台运行的nodeJs的方法。希望对于前端框架Javascript的学习有所帮助。

JavaScript从入门到精通之JavaScript实现网页截屏方法总结



主要看了以下几个:

 

 PhantomJS

 Puppeteer(chrome headless)

 SlimerJS

 dom-to-image

 html2canvas

测试的网页使用了WebGL技术,所以下面的总结会和WebGL相关。

 

名词定义

 

headless browser

 

无界面浏览器,多用于网页自动化测试、网页截屏、网页的网络监控等。

 

PhantomJS

 

PhantomJS是可以通过JS进行编程的headless浏览器,使用的是QtWebKit内核。

 

实现截屏的代码,假设文件名为github.js:

 

// 创建一个网页实例  

var page = require('webpage').create();  

// 加载页面  

page.open('//github.com/', function () {  

  // 给网页截屏,保存到github.png文件中  

  page.render('github.png');  

  phantom.exit();  

})

运行:

 

phantomjs github.js

普通的页面没有问题,但是如果运行包含WebGL的页面,发现截屏不对。经过一些调查,发现不支持WebGL,github issue。

 

总结:

 

 PhantomJs已经停止维护了,所以不太建议继续使用。停止维护的一个原因是chrome发布的headless版本对它造成了一定冲击。

 不支持WebGL。但是,还是有开发者说可以自己给PhantomJS添加WebGL支持,不过,这个方案目前超出我的知识范围了,就没有继续研究。

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。

 

总结:

 

 支持WebGL。

 网页比较复杂的话,截屏时间也挺长的,我测试的页面是几百毫秒。

 Puppeteer是对(CDP)Chrome Devtools Protocol功能的封装。大部分功能都是通过WebSocket传输给CDP处理的。

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("//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("//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。

 

总结

 

 1.0版本支持的火狐版本是53.0到59.0。不保证最新版本火狐可用。

 headless模式下,不支持WebGL。

dom-to-image

 

dom-to-image:前端截屏的开源库。工作原理是:

 

SVG的foreignObject标签可以包裹任意的html内容。那么,为了渲染一个节点,主要进行了以下步骤:

 

 递归地拷贝原始dom节点和后代节点;

 把原始节点以及后代节点的样式递归的应用到对应的拷贝后的节点和后代节点上;

 字体处理;

 图片处理;

 序列化拷贝后的节点,把它插入到foreignObject里面,然后组成一个svg,然后生成一个data URL;

 如果想得到PNG内容或原始像素值,可以先使用data URL创建一个图片,使用一个离屏canvas渲染这张图片,然后从canvas中获取想要的数据。

测试的时候,发现外部资源不能加载,所以简单的了解了后就放弃了。

 

html2canvas

 

html2canvas。网上查了下感觉有一篇文章写的挺好的:浅析 js 实现网页截图的两种方式。感兴趣的可以看下。

 

未验证的猜想

 

虽然后面这两种是前端的实现方式,但是结合前面讲的headless库,也是可以实现后端截屏的。以Puppeteer的API为例,可以首先使用page.addScriptTag(options)往网页中添加前端截屏的库,然后在page.evaluate(pageFunction[, ...args])中的pageFunction函数里面写相应的截屏代码就可以了,因为pageFunction的执行上下文是网页上下文,所以可以获取到document等对象。

 

总结

 

以上是对截屏的一个简单研究,希望对大家有所帮助。



关注“职坐标在线”(Zhizuobiao_Online)公众号,免费获取学习视频资料、技术就业咨询

JavaScript从入门到精通之JavaScript实现网页截屏方法总结

本文由 @小职 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程