热门标签 | HotTags
当前位置:  开发笔记 > 前端 > 正文

浅析js实现网页截图的两种方式

这篇文章主要介绍了浅析js实现网页截图的两种方式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

Web端的截图(生成图片)并不算是个高频的需求,资料自然也不算多,查来查去,也不过Canvas 和 SVG两种实现方案,原理大概相似,都非真正义上的截图而是把DOM转为图片,然而实现方式却截然不同。

Canvas 实现

如何将dom转换成canvas图片?自然是要一点点画到canvas里,想想都是件麻烦事。通过分析github的知名截图库 niklasvh/html2canvas (7k+ star)的源码,梳理了其大致的思路:

  • 递归取出目标模版的所有DOM节点,填充到一个rederList,并附加是否为顶层元素/包含内容的容器 等信息
  • 通过z-index postion float等css属性和元素的层级信息将rederList排序,计算出一个canvas的renderQueue
  • 遍历renderQueue,将css样式转为setFillStyle可识别的参数,依据nodeType调用相对应canvas方法,如文本则调用fillText,图片drawImage,设置背景色的div调用fillRect等
  • 将画好的canvas填充进页面

无论是排序优先级的计算还是从css到canvas的转换,毫无疑问都是些巨麻烦的事,尤其是放在真实的业务场景里,DOM模版中往往会包含复杂的样式与排版,html2canvas 足足用了20多个js来实现这层转换,复杂成度可见一斑。索性,我们不需要再重新造一遍轮子。

使用canvas转化的话灵活性较高,环境依赖上也只需要确保浏览器支持canvas就可以了,但它有个显著的缺点:慢。原因自然是因为大量的计算与递归调用,这是无可避免的。不过html2canvas代码中大量使用了Promise,所以html2canvas 支持异步操作。

限制:

  • 无法跨域跨域资源
  • 无法渲染iframe,flash等内容,但目前支持svg

值得一提的是,尽管html2canvas主页表示它还处于实验室环境,但自14年起便已经被Twitter 等用在了生产环境,所以虽然有诸多限制,稳定性应该还是保障的。

canvas如此复杂,那么有没有一种更简单的方法呢?

自然是有的,那便是SVG

SVG实现

首先,svg本来就是矢量图形;其次,svg是可以用xml描述的;再其次,用来描述svg的标签里有个 foreignObject标签,这个标签可以加载其它命名空间的xml(xhtml)文档。也就是说,如果使用svg的话,我们不再需要一点点的遍历,转换节点;不用再计算复杂的元素优先级,只需要一股脑的将要渲染的DOM扔进就好了,剩下的就交给浏览器去渲染。

让我们理一理思路:

  • 首先,我们要声明一个基础的svg模版,这个模版需要一些基础的描述信息,最重要的,它要有这对标签
  • 将要渲染的DOM模版模版嵌入foreignObject
  • 利用Blob构建svg图像
  • 取出URL,赋值给
  

Hello World

//此代码仅在chrome测试下通过
function html2Svg (domStr) { 
      //创建模版字符串
      var svgXML=
      `
        ${generateXML(html)}
       `
      //利用Blob创建svg
      var svg = new Blob([svgXML], {type: 'image/svg+xml'})
      //利用DOMURL.createObjectURL取出对象
      var url = window.URL.createObjectURL(svg);
      var img = new Image()
      img.src = url
      return img
    }

// 由于`foreignObject`只能引用XML文档,
// 所以我们需要对DOM进行格式化
function generateXML (domStr) { 
    var doc = document.implementation.createHTMLDocument('');
      doc.write(html);
      doc.documentElement.setAttribute('xmlns', doc.documentElement.namespaceURI);
      doc = parseStyle(doc)
      console.log(doc)
      html = (new XMLSerializer).serializeToString(doc).replace('','');
      return html
}

可以看到按这个思路来实现非常简单,并且没有了复杂的计算和递归,渲染速度自然要优于前者。然而使用svg,需要考虑诸多的限制问题。一个最为严肃的问题在于:SVG无法加载外部资源,也就是说,在svg里面,无论是还是 或者css中的背景图,这些资源都是无法加载的。在使用canvas实现时,因为我们是一个node一个node去画,所以不存在资源引用的问题。但使用svg实现,相当于我们把文档交给SVG再来来渲染一遍,这对于我们来说是其实是无法控制的黑盒操作,是受SVG限制的

万幸,一个昵称为Christoph Burgmer的小哥写了一个名为rasterizeHTML.js 的库,通过一系列的hack技巧替我们绕过了许多限制。我知道你很好奇他是怎么做到的。 简单来讲,rasterizeHTML.js在我们的基础实现上做了这些hack:

  • 的url 转为 dataURI
  • 将background-color从style中取出,修改url后重新插入样式表
  • 将link的的样式通过ajax down下来然后注入
  • 详见源码...

当然, rasterizeHTML.js能帮我们做的也不过是处理资源引用问题和浏览器兼容问题,更多的SVG的限制是无法绕过的,该库的文档正式列出了足足一整页的限制,让人读完后心中一凉。比如:

  • 跨域资源无法加载
  • 如lazyload等通过js加载的资源无法加载
  • 内联或js操作background-image无法加载
  • 详见文档

思考下rasterizeHTML.js的原理便可理解这些限制无法避免的原因: rasterizeHTML.js只能对已经存在的静态资源进行处理,而对js动态生成并不能实时处理。

目前rasterizeHTML.js已经被用于知乎-意见反馈功能。

参考

源码https://developer.mozilla.org/en-US/docs/Web/API/CanvasAPI/DrawingDOMobjectsintoacanvas

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


推荐阅读
  • 原文:http:blog.linjunhalida.comblogpjaxgithub:https:github.comdefunktjquery-pjax ... [详细]
  • Java验证码——kaptcha的使用配置及样式
    本文介绍了如何使用kaptcha库来实现Java验证码的配置和样式设置,包括pom.xml的依赖配置和web.xml中servlet的配置。 ... [详细]
  • 在springmvc框架中,前台ajax调用方法,对图片批量下载,如何弹出提示保存位置选框?Controller方法 ... [详细]
  • Servlet多用户登录时HttpSession会话信息覆盖问题的解决方案
    本文讨论了在Servlet多用户登录时可能出现的HttpSession会话信息覆盖问题,并提供了解决方案。通过分析JSESSIONID的作用机制和编码方式,我们可以得出每个HttpSession对象都是通过客户端发送的唯一JSESSIONID来识别的,因此无需担心会话信息被覆盖的问题。需要注意的是,本文讨论的是多个客户端级别上的多用户登录,而非同一个浏览器级别上的多用户登录。 ... [详细]
  • Allegro总结:1.防焊层(SolderMask):又称绿油层,PCB非布线层,用于制成丝网印板,将不需要焊接的地方涂上防焊剂.在防焊层上预留的焊盘大小要比实际的焊盘大一些,其差值一般 ... [详细]
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • EPICS Archiver Appliance存储waveform记录的尝试及资源需求分析
    本文介绍了EPICS Archiver Appliance存储waveform记录的尝试过程,并分析了其所需的资源容量。通过解决错误提示和调整内存大小,成功存储了波形数据。然后,讨论了储存环逐束团信号的意义,以及通过记录多圈的束团信号进行参数分析的可能性。波形数据的存储需求巨大,每天需要近250G,一年需要90T。然而,储存环逐束团信号具有重要意义,可以揭示出每个束团的纵向振荡频率和模式。 ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • 本文介绍了使用AJAX的POST请求实现数据修改功能的方法。通过ajax-post技术,可以实现在输入某个id后,通过ajax技术调用post.jsp修改具有该id记录的姓名的值。文章还提到了AJAX的概念和作用,以及使用async参数和open()方法的注意事项。同时强调了不推荐使用async=false的情况,并解释了JavaScript等待服务器响应的机制。 ... [详细]
  • Java实战之电影在线观看系统的实现
    本文介绍了Java实战之电影在线观看系统的实现过程。首先对项目进行了简述,然后展示了系统的效果图。接着介绍了系统的核心代码,包括后台用户管理控制器、电影管理控制器和前台电影控制器。最后对项目的环境配置和使用的技术进行了说明,包括JSP、Spring、SpringMVC、MyBatis、html、css、JavaScript、JQuery、Ajax、layui和maven等。 ... [详细]
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • 本文介绍了Java的集合及其实现类,包括数据结构、抽象类和具体实现类的关系,详细介绍了List接口及其实现类ArrayList的基本操作和特点。文章通过提供相关参考文档和链接,帮助读者更好地理解和使用Java的集合类。 ... [详细]
  • 本文介绍了Android 7的学习笔记总结,包括最新的移动架构视频、大厂安卓面试真题和项目实战源码讲义。同时还分享了开源的完整内容,并提醒读者在使用FileProvider适配时要注意不同模块的AndroidManfiest.xml中配置的xml文件名必须不同,否则会出现问题。 ... [详细]
  • 以数据驱动品牌,为出海强势护航
                    原创
    原标题:以数 ... [详细]
author-avatar
lippor
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有