使用html2canvas.js实现页面截图并显示或上传
最近写项目有用到html2canvas.js,可以实现页面的截图功能,但遭遇了许多的坑,特此写一篇随笔记录一下。
在使用html2canvas时可能会遇到诸如只能截取可视化界面、截图没有背景色、svg标签无法截取等问题,下面详细的说明一下。
一、导入html2canvas.js
这个不需要多说,可以从github上获取:https://github.com/niklasvh/html2canvas
也可以直接导入链接: <script src="https://cdn.bootcss.com/html2canvas/0.5.0-beta4/html2canvas.js"></script>
使用起来也非常简单,具体的API可以去网上查找,生成png图片使用“image/png”即可。
其中$("#xxx")为你想要截取的div,外面可以通过jquery获取它,当然document获取也是可以的。
html2canvas($("#xxx"), {
onrendered: function (canvas) {
var url = canvas.toDataURL("image/png");
window.location.href = url;
}
});
其它类型的图片如jpg,为image/jpeg等等,可自行查询API。
到这里其实简单的截图已经完成了,如果界面稍微复杂一点的话,可能就会出现各种坑,下面一个一个解决。
二、svg无法截取的问题
当我们截取一个div时,如果这个div中存在svg标签,一般情况下是截取不到的,比如截取一个流程图,得到的是下面这个样子:

可以看到,流程图的线没有截取到,也就是svg没有截取到,这时的解决方法是把svg转换成canvas再进行截图即可,直接上代码。
这里的each循环是循环所有的svg标签,将它们全部转换为canvas
if (typeof html2canvas !== 'undefined') {
//以下是对svg的处理
var nodesToRecover = [];
var nodesToRemove = [];
var svgElem = cloneDom.find('svg');
svgElem.each(function (index, node) {
var parentNode = node.parentNode;
var svg = node.outerHTML.trim();
var canvas = document.createElement('canvas');
canvas.width = 650;
canvas.height = 798;
canvg(canvas, svg);
if (node.style.position) {
canvas.style.position += node.style.position;
canvas.style.left += node.style.left;
canvas.style.top += node.style.top;
}
nodesToRecover.push({
parent: parentNode,
child: node
});
parentNode.removeChild(node);
nodesToRemove.push({
parent: parentNode,
child: canvas
});
parentNode.appendChild(canvas);
});
}
这里需要用到canvg.js,以及它的依赖文件rgbcolor.js,网上可以直接下载,也可以直接导入。
三、背景透明的问题
这个其实很简单,因为它默认是透明的,html2canvas中有一个参数background就可以添加背景色,如下:
html2canvas(cloneDom, {
onrendered: function(canvas) {
var url =canvas.toDataURL("image/png");
},
background:"#fafafa"
});
四、只能截取可视部分的问题
如果需要截取的div超出了界面,可能会遇到截取不全的问题,如上图,只有一半的内容,这是因为看不到的部分被隐藏了,而html2canvas是无法截取隐藏的dom的。
所以此时的解决办法是使用克隆,将需要截取的部分克隆一份放在页面底层,再使用html2canvas截取这个完整的div,截取完成后再remove这部分内容即可,完整代码如下:
function showQRCode() {
scrollTo(0, 0);
//克隆节点,默认为false,即不复制方法属性,为true是全部复制。
var cloneDom = $("#d1").clone(true);
//设置克隆节点的z-index属性,只要比被克隆的节点层级低即可。
cloneDom.css({
"background-color": "#fafafa",
"position": "absolute",
"top": "0px",
"z-index": "-1",
"height": 798,
"width": 650
});
if (typeof html2canvas !== 'undefined') {
//以下是对svg的处理
var nodesToRecover = [];
var nodesToRemove = [];
var svgElem = cloneDom.find('svg');//divReport为需要截取成图片的dom的id
svgElem.each(function (index, node) {
var parentNode = node.parentNode;
var svg = node.outerHTML.trim();
var canvas = document.createElement('canvas');
canvas.width = 650;
canvas.height = 798;
canvg(canvas, svg);
if (node.style.position) {
canvas.style.position += node.style.position;
canvas.style.left += node.style.left;
canvas.style.top += node.style.top;
}
nodesToRecover.push({
parent: parentNode,
child: node
});
parentNode.removeChild(node);
nodesToRemove.push({
parent: parentNode,
child: canvas
});
parentNode.appendChild(canvas);
});
//将克隆节点动态追加到body后面。
$("body").append(cloneDom);
html2canvas(cloneDom, {
onrendered: function(canvas) {
var url =canvas.toDataURL("image/png");
window.location.href = url ;
cloneDom.remove(); //清空克隆的内容
},
background:"#fafafa"
});
}
}
这里外面首先将要截取的div克隆一份,并将z-index设置为最小,避免引起界面的不美观,然后是对svg进行的处理,上面已经分析过了,最后将克隆节点追加到body后面即可。
在onrendered中,我们可以直接使用location.href跳转查看图片,可以进行保存操作,也可以将url写入img的src中显示在界面上,如 $('#imgId').attr('src',url);
最后可以在界面展示刚刚截取到的图片:

五、上传图片保存到数据库,并在界面中获取该图片显示
现在得到url了,需要上传到后端,并存到数据库中,再另一个展示的界面中加载该图片。我一般习惯于使用url来存储图片路径,而不是用blob存储。
因为需要在另一个界面中获取图片,所以我把图片存在了与webapp同级的一个resource目录下,代码如下:
//存储图片并返回图片路径
BASE64Decoder decoder = new BASE64Decoder();
byte[] b = decoder.decodeBuffer(product.getProPic().substring("data:image/png;base64,".length()));
ByteArrayInputStream bais = new ByteArrayInputStream(b);
BufferedImage bi1 = ImageIO.read(bais);
String url = "user_resource" + File.separator + "img" + File.separator + "product_"+UUID.randomUUID().toString().replace("-", "")+".png";
String totalUrl = System.getProperty("root") + url;
File w2 = new File(totalUrl);
ImageIO.write(bi1, "png", w2); product.setProPic(url); //将图片的相对路径存储到数据库中 int res = productMapper.insertSelective(product); //添加到数据库
这里因为涉及到其它逻辑,所以只放一部分代码。
这里使用的是BASE64Decoder来存储图片,我们获取到图片后,需要使用substring将“data:image/png;base64,”的内容截取掉,因为“,”后面才是图片的url, url.substring("data:image/png;base64,".length()) 。
对于路径,上面代码中的url是我存储到数据库中的内容,而totalUrl就是实际进行ImageIO的write操作时存储的真实路径,getProperty()方法获取的项目的根目录,可以在web.xml中配置如下内容,然后 System.getProperty("root") 即可。
<!-- 配置系统获得项目根目录 -->
<context-param>
<param-name>webAppRootKey</param-name>
<param-value>root</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.util.WebAppRootListener
</listener-class>
</listener>
现在图片的url就存到数据库里了,而图片本身就存储在tomcat下该项目的这个目录下。

最后外面在界面上获取,只需要在当前的url前面加上项目名即可 <img class="depot-img" src="<%=request.getContextPath()%>/`+e.proPic+`"> 。
然后就可以看到界面上显示的图片了:

使用html2canvas.js实现页面截图并显示或上传的更多相关文章
- 使用JS实现页面中动态添加文件上传输入项
1. 编写JSP <%@ page language="java" import="java.util.*" pageEncoding="UTF ...
- 如是使用JS实现页面内容随机显示
之前有个客户咨询我,因为他们公司的业务员有多个人,但公司网站的联系方式板块里只够放一个人的信息,所以就想能不能实现这个联系方式信息随机显示,对于业务或客服人员来说也能做到分配均匀公平.本文我们将和大家 ...
- 利用来JS控制页面控件显示和隐藏有两种方法
利用来JS控制页面控件显示和隐藏有两种方法,两种方法分别利用HTML的style中的两个属性,两种方法的不同之处在于控件隐藏后是否还在页面上占空位. 方法一: 1 2 document.getEle ...
- 利用html2canvas截图,得到base64上传ajax
<script type="text/javascript" src="js/html2canvas.js"></script> //布 ...
- Jquery ajaxfileupload.js结合.ashx文件实现无刷新上传
先上几张图更直观展示一下要实现的功能,本功能主要通过Jquery ajaxfileupload.js插件结合ajaxUpFile.ashx一般应用程序处理文件实现Ajax无刷新上传功能,结合NPOI2 ...
- JS兼容各个浏览器的本地图片上传即时预览效果
JS兼容各个浏览器的本地图片上传即时预览效果 很早以前 在工作曾经碰到这么一个需求,当时也是纠结了很久,也是google了很久,没有碰到合适的demo,今天特意研究了下这方面的的问题,所以也就做了个简 ...
- Resumable.js – 基于 HTML5 File API 的文件上传
Resumable.js 是一个 JavaScript 库,通过 HTML5 文件 API 提供,稳定和可恢复的批量上传功能.在上传大文件的时候通过每个文件分割成小块,每块在上传失败的时候,上传会不断 ...
- eclipse上传显示svn上传者名
这里来记录下eclipse上传显示svn上传者名. 如图所示,修改即可.
- js 显示刚刚上传的图片 (onchange事件)
<table> <tr width="100"> <td>上传商场图片:</td> <td> <input typ ...
随机推荐
- ASP.NET Core使用Jaeger实现分布式追踪
前言 最近我们公司的部分.NET Core的项目接入了Jaeger,也算是稍微完善了一下.NET团队的技术栈. 至于为什么选择Jaeger而不是Skywalking,这个问题我只能回答,大佬们说了算. ...
- 浅谈WPF中的MVVM框架--MVVMFoundation
先科普一下:什么是WPF,请看下图 微软对于WPF技术的构想是很宏大的,可惜普及率不高,不过如果你要做Windows客户端开发的话WPF技术还是值得一学的. 什么是MVVM模式 简单来说它是一种高级的 ...
- 【.NET Core项目实战-统一认证平台】第十五章 网关篇-使用二级缓存提升性能
[.NET Core项目实战-统一认证平台]开篇及目录索引 一.背景 首先说声抱歉,可能是因为假期综合症(其实就是因为懒哈)的原因,已经很长时间没更新博客了,现在也调整的差不多了,准备还是以每周1-2 ...
- 第63章 ASP.NET Identity 支持 - Identity Server 4 中文文档(v1.0.0)
提供了基于ASP.NET身份的实现,用于管理IdentityServer用户的身份数据库.此实现是IdentityServer中的扩展点,以便为用户加载身份数据以将声明发送到令牌. 这个支持的仓储位于 ...
- DSAPI多功能组件编程应用-反射相关
[DSAPI.DLL下载地址] 在.Net中,反射技术是一种入门困难,熟用快速的东西,对于没有接触过反射技术的程序员来说的确是头疼的,看一旦自己写过了,上手就非常简单了.在本节,将部分.N ...
- 通过Web API调用Action时各种类型输入参数传递值的方法
本人微信公众号:微软动态CRM专家罗勇 ,回复280或者20180906可方便获取本文,同时可以在第一间得到我发布的最新博文信息,follow me!我的网站是 www.luoyong.me . Dy ...
- 仿9GAG制作过程(二)
有话要说: 这次准备讲述用python爬虫以及将爬来的数据存到MySQL数据库的过程,爬的是煎蛋网的无聊图. 成果: 准备: 下载了python3.7并配置好了环境变量 下载了PyCharm作为开发p ...
- PJSUA2开发文档--第三章 PJSUA2高级API
3. PJSUA2高级API PJSUA2是PJSUA API以上的面向对象抽象.它为构建会话发起协议(SIP)多媒体用户代理应用程序(也称为IP / VoIP软电话)提供高级API.它将信令,媒体和 ...
- 关于 MongoDB 与 SQL Server 通过本身自带工具实现数据快速迁移 及 注意事项 的探究
背景介绍 随着业务的发展.需求的变化,促使我们追求使用不同类型的数据库,充分发挥其各自特性.如果决定采用新类型的数据库,就需要将既有的数据迁移到新的数据库中.在这类需求中,将SQL Server中的数 ...
- c/c++ linux 进程间通信系列4,使用共享内存
linux 进程间通信系列4,使用共享内存 1,创建共享内存,用到的函数shmget, shmat, shmdt 函数名 功能描述 shmget 创建共享内存,返回pic key shmat 第一次创 ...