前端网页截图:Canvas截图 vs SVG截图

HTML5 herman 1220浏览
公告:“业余草”微信公众号提供免费CSDN下载服务(只下Java资源),关注业余草微信公众号,添加作者微信:codedq,发送下载链接帮助你免费下载!
本博客日IP超过2000,PV 3000 左右,急需赞助商。
极客时间所有课程通过我的二维码购买后返现24元微信红包,请加博主新的微信号:codedq,之前的微信号好友位已满,备注:返现
饿了么大量招人,我内推!Java 方向!薪资不设上限,工作年龄不限!工作地点限魔都,可电话面试!简历,发我微信:codedq
所有面试题(java、前端、数据库、springboot等)一网打尽,请关注文末小程序
视频教程免费领

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

  • 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

不多废话,直接上两种前端截图方式的代码,小伙伴们可根据项目需求自行优化代码和增加功能哈。

测试代码如下:

<div id="screenshot">Hello World</div>
<button id="save-btn">保存</button>
// 渲染图片
function Render(src, width, height, cb) {
    const img = new Image();
    img.src = src;
    img.width = width;
    img.height = height;
    img.crossOrigin = ""; // 图像跨域时配置
    cb && cb(img);
}
// 下载图片
function Download(url, name) {
    const target = document.createElement("a");
    target.href = url;
    target.download = name;
    const event = document.createEvent("MouseEvents");
    event.initEvent("click", true, true);
    target.dispatchEvent(event);
}

Canvas截图核心代码如下所示:

import Html2canvas from "html2canvas";
const btn = document.getElementById("save-btn");
btn.addEventListener("click", () => {
    const screenshot = document.getElementById("screenshot");
    // allowTaint: true, // 不能与useCORS共用
    const opts = {
        logging: false,
        scale: 2,
        useCORS: true
    };
    Html2canvas(screenshot, opts).then(res => {
        const { height, width } = res;
        const base64 = res.toDataURL("image/png", 1);
        Render(base64, width, height, img => {
            document.body.appendChild(img);
            Download(base64, "screenshot.png");
        });
    }, err => alert("截图失败,请重新尝试"));
});

SVG截图核心代码如下所示:

import Rasterizehtml from "rasterizehtml";
const btn = document.getElementById("save-btn");
btn.addEventListener("click", () => {
    // drawURL()加载的URL必须是同域名URL或支持跨域的URL
    // 下面的URL是随便写的,记得换成同域名URL或支持跨域的URL
    const url = "https://www.baidu.com";
    const canvas = document.createElement("canvas");
    const opts = {
        executeJs: true,
        height: screen.height,
        width: screen.width
    };
    Rasterizehtml.drawURL(url, canvas, opts).then(res => {
        const base64 = "data:image/svg+xml;base64," + btoa(unescape(encodeURIComponent(res.svg)));
        Render(base64, opts.width, opts.height, img => {
            document.body.appendChild(img);
            Download(base64, "screenshot.png");
        });
    }, err => alert("截图失败,请重新尝试"));
});

另外还有几点需要注意一下:

  • 使用Canvas截图兼容低版本浏览器时,不能使用CSS3属性和带有前缀的属性
  • 使用SVG截图可获取同域< iframe>内容进行渲染
  • 使用SVG截图可获取同域

感兴趣的网友可结合自身项目尝试一下两种前端截图方式,探究下其相同点和不同点。如果对其截图原理感兴趣,可剖析下html2canvas和rasterizehtml的源码,相信你会有意外的收获喔!

业余草公众号

最后,欢迎关注我的个人微信公众号:业余草(yyucao)!可加作者微信号1:xmtxtt(5000人已满),微信号2:xttblog(5000人已满),微信号3:codedq(超3800)。备注:“1”,添加博主微信拉你进微信群。备注错误不会同意好友申请。再次感谢您的关注!后续有精彩内容会第一时间发给您!原创文章投稿请发送至532009913@qq.com邮箱。商务合作也可添加作者微信进行联系!

本文原文出处:业余草: » 前端网页截图:Canvas截图 vs SVG截图