翼度科技»论坛 编程开发 JavaScript 查看内容

浅谈两种前端截图方式:Canvas截图 vs SVG截图

6

主题

6

帖子

18

积分

新手上路

Rank: 1

积分
18
背景

如今很多网站都引入截图功能,可用于问题反馈、内容分享等实用需求,而前端截图也不知不觉成为了首选。今天为大家推荐两种前端截图方式,虽然有些局限,但是也能应付大部分项目需求。

  • Canvas截图:html2canvas
  • SVG截图:rasterizehtml
原理

首先来谈下两种前端截图方式的原理,虽然实现方式不太一致,但是核心思想是相同的。
以html2canvas为代表的Canvas截图,通过遍历DOM克隆一份副本,将此副本在Canvas上重新绘制,并根据DOM的样式应用在对应的绘制元素上,再通过Canvas生成图片。转换过程可理解成:DOM→Canvas→Image。
以rasterizehtml为代表的SVG截图,通过遍历DOM克隆一份副本,利用SVG的foreignObject把DOM作为外部资源嵌套在SVG中,将此SVG在Canvas上重新绘制,并根据DOM的样式应用在对应的绘制元素上,再通过Canvas生成图片。转换过程可理解成:DOM→SVG的ForeignObject→Canvas→Image。
两种前端截图方式最后都是通过把DOM绘制到Canvas,再通过Canvas输出图片。
限制

虽然两种前端截图方式都有这两个封装得比较完善的第三方库html2canvas和rasterizehtml,但是由于在转换过程中存在一些自身的局限性,所以也导致截图可能出现一些不完美的问题。
Canvas截图的限制性


  • 无法渲染跨域资源(支持同域)
  • 无法渲染iFrame和Flash内容(支持SVG)
SVG截图的限制性


  • 无法渲染跨域资源(支持同域)
  • 无法渲染如lazyload等通过JS加载的资源
  • 无法渲染内联background-image或JS操作background-image
方案

不多废话,直接上两种前端截图方式的代码,小伙伴们可根据项目需求自行优化代码和增加功能哈。
准备
  1. Hello World
  2. <button id="save-btn">保存</button>
复制代码
  1. // 渲染图片
  2. function Render(src, width, height, cb) {
  3.     const img = new Image();
  4.     img.src = src;
  5.     img.width = width;
  6.     img.height = height;
  7.     img.crossOrigin = ""; // 图像跨域时配置
  8.     cb && cb(img);
  9. }
  10. // 下载图片
  11. function Download(url, name) {
  12.     const target = document.createElement("a");
  13.     target.href = url;
  14.     target.download = name;
  15.     const event = document.createEvent("MouseEvents");
  16.     event.initEvent("click", true, true);
  17.     target.dispatchEvent(event);
  18. }
复制代码
Canvas截图
  1. import Html2canvas from "html2canvas";
  2. const btn = document.getElementById("save-btn");
  3. btn.addEventListener("click", () => {
  4.     const screenshot = document.getElementById("screenshot");
  5.     // allowTaint: true, // 不能与useCORS共用
  6.     const opts = {
  7.         logging: false,
  8.         scale: 2,
  9.         useCORS: true
  10.     };
  11.     Html2canvas(screenshot, opts).then(res => {
  12.         const { height, width } = res;
  13.         const base64 = res.toDataURL("image/png", 1);
  14.         Render(base64, width, height, img => {
  15.             document.body.appendChild(img);
  16.             Download(base64, "screenshot.png");
  17.         });
  18.     }, err => alert("截图失败,请重新尝试"));
  19. });
复制代码
SVG截图
  1. import Rasterizehtml from "rasterizehtml";
  2. const btn = document.getElementById("save-btn");
  3. btn.addEventListener("click", () => {
  4.     // drawURL()加载的URL必须是同域名URL或支持跨域的URL
  5.     // 下面的URL是随便写的,记得换成同域名URL或支持跨域的URL
  6.     const url = "https://www.baidu.com";
  7.     const canvas = document.createElement("canvas");
  8.     const opts = {
  9.         executeJs: true,
  10.         height: screen.height,
  11.         width: screen.width
  12.     };
  13.     Rasterizehtml.drawURL(url, canvas, opts).then(res => {
  14.         const base64 = "data:image/svg+xml;base64," + btoa(unescape(encodeURIComponent(res.svg)));
  15.         Render(base64, opts.width, opts.height, img => {
  16.             document.body.appendChild(img);
  17.             Download(base64, "screenshot.png");
  18.         });
  19.     }, err => alert("截图失败,请重新尝试"));
  20. });
复制代码
另外还有几点需要注意一下:

  • 使用Canvas截图兼容低版本浏览器时,不能使用CSS3属性和带有前缀的属性
  • 使用SVG截图可获取同域内容进行渲染
  • 截图不能包含跨域获取的内容,否则不会渲染跨域内容
总结

浅谈两种前端截图方式就到此为止啦,相信小伙伴们对前端截图也有一个比较清晰的概念了,可结合自身项目尝试一下两种前端截图方式,探究下其相同点和不同点。如果对其截图原理感兴趣,可剖析下html2canvas和rasterizehtml的源码,相信你会有意外的收获喔!

来源:https://www.cnblogs.com/onesea/p/17327656.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

举报 回复 使用道具