最近我在研究开发一个火狐插件, 具体的功能是将网页内容截屏并分享到微博上. 目前基本功能已经实现, 大家可以在 @程序师视野 http://weibo.com/computerworld 里看到用这个截图插件分享的微博的效果.
之前我曾写过如何将 canvas 图形转换成图片和下载 canvas 图像的方法, 这些都是在为这个插件做技术准备.
技术路线很清晰, 将网页的某个区域的内容生成图像, 保持到 canvas 里, 然后将 canvas 内容转换成图片, 保存到本地, 最后上传到微博.
我在网上搜寻到 https://github.com/niklasvh/html2canvas 这个能将指定网页元素内容生成 canvas 图像的 JavaScript 工具. 这个 JS 工具的用法很简单, 你只需要将它的 JS 文件引入到页面里, 然后调用 html2canvas()函数:
- html2canvas(document.body, {
- onrendered: function(canvas) {
- /* canvas is the actual canvas element,
- to append it to the page call for example
- document.body.appendChild( canvas );
- */
- }
- });
这个 html2canvas()函数有个参数, 上面的例子里传入的参数是 document.body, 这会截取整个页面的图像. 如果你想只截取一个区域, 比如对某个 div 或某个 table 截图, 你就将这个 div 或某个 table 当做参数传进去.
我最终并没有选用 html2canvas 这个 JS 工具, 因为在我的实验过程中发现它有几个问题.
首先, 跨域问题. 我举个例子说明这个问题, 比如我的网页网址是 http://www.webhek.com/about/, 而我在这个页面上有个张图片, 这个图片并不是来自 www.webhek.com 域, 而是来自 CDN 图片服务器 www.webhek-cdn.com/images/about.jpg, 那么, 这张图片就和这个网页不是同域, 那么 html2canvas 就无法对这种图片进行截图, 如果你的网站的所有图片都放在单独的图片服务器上, 那么用 html2canvas 对整个网页进行截图是就会发现所有图片的地方都是空白.
这个问题也有补救的方法, 就是用代理:
- <!DOCTYPE HTML>
- <HTML>
- <head>
- <meta charset="utf-8">
- <title>
- html2canvas PHP proxy
- </title>
- <script src="html2canvas.js">
- </script>
- <script>
- //<![CDATA[
- (function() {
- Windows.onload = function() {
- html2canvas(document.body, {
- "logging": true,
- //Enable log (use Web Console for get Errors and Warnings)
- "proxy": "html2canvasproxy.php",
- "onrendered": function(canvas) {
- var img = new Image();
- img.onload = function() {
- img.onload = null;
- document.body.appendChild(img);
- };
- img.onerror = function() {
- img.onerror = null;
- if (Windows.console.log) {
- Windows.console.log("Not loaded image from canvas.toDataURL");
- } else {
- alert("Not loaded image from canvas.toDataURL");
- }
- };
- img.src = canvas.toDataURL("image/png");
- }
- });
- };
- })();
- //]]>
- </script>
- </head>
- <body>
- <p>
- <img alt="google maps static" src="http://maps.googleapis.com/maps/api/staticmap?center=40.714728,-73.998672&zoom=12&size=800x600&maptype=roadmap&sensor=false">
- </p>
- </body>
- </HTML>
这个方法只能用在你自己的服务器里, 如果是对别人的网页截图, 还是不行.
试验的过程中还发现用 html2canvas 截屏出来的图像有时会出现文字重叠的现象. 我估计是因为 html2canvas 在解析页面内容, 处理 CSS 时不是很完美的原因.
最后, 我在火狐浏览器的官方网站上找到了 drawWindow()这个方法, 这个方法和上面提到 html2canvas 不同之处在于, 它不分析页面元素, 它只针对区域, 也就是说, 它接受的参数是四个数字标志的区域, 不论这个区域中什么地方, 有没有页面内容.
- void drawWindow(
- in nsIDOMWindow Windows,
- in float x,
- in float y,
- in float w,
- in float h,
- in DOMString bgColor,
- in unsigned long flags [optional]
- );
这个原生的 JavaScript 方法看起来非常的完美, 正是我需要的, 但这个方法不能使用在普通网页中, 因为火狐官方发现这个方法会引起有安全漏洞, 在这个 bug 修复之前, 只有具有 "Chrome privileges" 的代码才能使用这个 drawWindow()函数.
虽然有很大的限制, 但周折一下还是可以用的, 在我开发的火狐 addon 插件中, main.JS 就是具有 "Chrome privileges" 的代码. 我在网上发现了一段火狐插件 SDK 里自带代码样例:
- var Windows = require('window/utils').getMostRecentBrowserWindow();
- var tab = require('tabs/utils').getActiveTab(Windows);
- var thumbnail = Windows.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
- thumbnail.mozOpaque = true;
- Windows = tab.linkedBrowser.contentWindow;
- thumbnail.width = Math.ceil(Windows.screen.availWidth / 5.75);
- var aspectRatio = 0.5625; // 16:9
- thumbnail.height = Math.round(thumbnail.width * aspectRatio);
- var ctx = thumbnail.getContext("2d");
- var snippetWidth = Windows.innerWidth * .6;
- var scale = thumbnail.width / snippetWidth;
- ctx.scale(scale, scale);
- ctx.drawWindow(Windows, Windows.scrollX, Windows.scrollY, snippetWidth, snippetWidth * aspectRatio, "rgb(255,255,255)");
- // thumbnail now represents a thumbnail of the tab
这段代码写的非常清楚, 只需要依据它做稍微的修改就能适应自己的需求.
我是第一次接触火狐插件开发, 是边学习, 边研究, 边开发. 所以开发速度很慢, 这个小小的插件用了整整一周才基本上达到能用的程度. 你可以在 @程序师视野 http://weibo.com/computerworld 微博里看到用它上传的图片效果还是不错的.
先能用, 然后使用的过程中慢慢做改进, 这是我的软件开发理念.
希望和对火狐插件有兴趣的朋友一起探讨, 一起学习.
来源: http://www.webhek.com/post/drawwindow.html