html2canvas
介绍
直接在用户浏览器上截取网页或其一部分的 “屏幕快照” 。屏幕截图基于DOM,因此可能无法真实表示100%的准确度,因为它无法生成实际的屏幕截图,而是根据页面上的可用信息构建屏幕截图。通过读取 DOM 和应用于元素的不同样式,将当前页面呈现 canvas 画布图像。
GitHub:https://github.com/niklasvh/html2canvas/
文档地址:https://github.com/niklasvh/html2canvas/blob/master/docs/configuration.md
官网:https://html2canvas.hertzen.com/
示例
下面是 demo,我直接通过他们官网的下载地址引用的
<script src="https://html2canvas.hertzen.com/dist/html2canvas.min.js" ></script>
<body>
<div id="capture"> <p>Hello World</p> </div>
</body>
<script>
html2canvas(document.querySelector("#capture")).then(canvas => {
document.body.appendChild(canvas)
});
</script>
接下来我可以把得到的 canvas 转换成 imageBase64
var imageBase64;
html2canvas(document.querySelector("#capture")).then(canvas => {
imageBase64 = canvas.toDataURL("image/png");
// 输出Canvas到网页,查看最终输出结果
var pHtml = new Image();
pHtml.src = imageBase64;
pHtml.setAttribute('crossorigin', 'anonymous');
document.body.appendChild(pHtml);
});
到这里其实基本差不多了,但是当我的页面中有图片的时候出现了错误,不能跨域引用图片,这里除了将图片源设置允许跨域的标识之外,还需要设置一下 html2canvas
var imageBase64;
html2canvas(document.querySelector("#capture"), {
allowTaint: true,
useCORS: true,
}).then(canvas => {
imageBase64 = canvas.toDataURL("image/png");
// 输出Canvas到网页,查看最终输出结果
var pHtml = new Image();
pHtml.src = imageBase64;
pHtml.setAttribute('crossorigin', 'anonymous');
document.body.appendChild(pHtml);
});
就是在后面传入一个配置对象,上面我配置了 allowTaint: true,useCORS: true ,相关文档地址:https://github.com/niklasvh/html2canvas/blob/master/docs/configuration.md (下面我根据自己的理解简单翻译了一下部分参数。
-
allowTaint :默认值:false 。描述:是否允许跨域图像加载到 Canvas 画布上
-
backgroundColor,默认值:#FFFFFF ,描述:Canvas 画布的背景颜色,如果传入的 DOM 有背景颜色的话就会被那个颜色覆盖,设置为 null 的话可以显示透明
-
foreignObjectRendering,默认值:false ,描述:如果浏览器支持,是否使用 ForeignObject ( SVG 方式 )渲染
-
imageTimeout,默认值:15000 ,描述:加载图像的超时时间(以毫秒为单位)。设置 0 为禁用超时。
-
ignoreElements,默认值:(element) => false ,描述:如果浏览器支持,是否使用 ForeignObject ( SVG 方式 )渲染
-
logging,默认值:true ,描述:如果浏览器支持,是否使用 ForeignObject ( SVG 方式 )渲染
-
onclone,默认值:null ,描述:如果浏览器支持,是否使用 ForeignObject ( SVG 方式 )渲染
-
proxy,默认值:null ,描述:如果浏览器支持,是否使用 ForeignObject ( SVG 方式 )渲染
-
removeContainer,默认值:true ,描述:是否清除 html2canvas 临时创建的克隆 DOM 元素
-
scale,默认值:window.devicePixelRatio ,描述:用于渲染的比例,默认为浏览器设备像素比率。
-
useCORS,默认值:false ,描述:是否尝试使用 CORS 从服务器加载图像
-
width,默认值:Element width ,描述:Canvas 画布的宽度,不设置的话会默认用你传入的 DOM 宽度
-
height,默认值:Element height ,描述:Canvas 画布的高度,不设置的话会默认用你传入的 DOM 高度
-
x,默认值:Element x-offset ,描述:裁剪画布 X 坐标
-
y,默认值:Element y-offset ,描述:裁剪画布 Y 坐标
-
scrollX,默认值:Element scrollX ,描述:渲染元素时要使用的x滚动位置(例如,如果元素使用position: fixed)也就是你传入的 DOM 可以横向滚动时加载时候的初始位置
-
scrollY,默认值:Element scrollY ,描述:呈现元素时要使用的y-scroll位置(例如,如果 Element 使用position: fixed)也就是你传入的 DOM 可以竖向滚动时加载时候的初始位置
-
windowWidth,默认值:Window.innerWidth ,描述:渲染时使用的窗口宽度,这可能会影响媒体查询之类的内容
-
windowHeight,默认值:Window.innerHeight,描述:渲染时要使用的窗口高度,这可能会影响媒体查询之类的内容
解决图片资源跨域问题后,我发现它如果超过屏幕范围的 div( 带滑动条的 )就不会去截取了,这个问题我搜索了好久才找到解决思路,将要截取的 div 复制一个把它的 scrollHeight 获取到,然后设置到复制出来的 height 上,宽度也是如此,最后在把宽高比例优化一下就好了,看代码:
var targetMod = document.getElementById('outputCtt');
var copyDom = targetMod.cloneNode(true); // 克隆节点
copyDom.id = "outputCttCp";
copyDom.style.width = targetMod.offsetWidth + 'px';
copyDom.style.height = targetMod.scrollHeight + 'PX'; // 获得高度
copyDom.style.paddingTop = "20px";
document.body.appendChild(copyDom); // 插入节点
var targetBlock = document.getElementById('outputCttCp');
var canvasEle = document.createElement("canvas");
var scaleBy = 10; //放大倍数
var imageBase64; //存放生成Canvas图片的Base64码
//获取目标DOM的宽高度 * 放大倍数
canvasEle.width = targetBlock.offsetWidth*scaleBy;
canvasEle.height = targetBlock.scrollHeight*scaleBy;
//输出调试,看看尺寸是否正确
console.log(canvasEle.width+"||"+canvasEle.height);
console.log(targetBlock.offsetWidth+"||"+targetBlock.scrollHeight);
//使用canvasEle.context进行放大
var context = canvasEle.getContext('2d');
context.scale(scaleBy, scaleBy);
html2canvas(targetBlock, {
allowTaint: true,
useCORS: true,
}).then(canvas => {
imageBase64 = canvas.toDataURL("image/png");
// 输出Canvas到网页,查看最终输出结果
var pHtml = new Image();
pHtml.src = imageBase64;
pHtml.id = "test-img";
pHtml.setAttribute('crossorigin', 'anonymous');
if(document.getElementById(pHtml.id)) {
// 清理预览节点
document.body.removeChild(document.getElementById(pHtml.id));
}
document.body.appendChild(pHtml);
document.body.removeChild(copyDom); // 清理插入节点
});
到这里其实图片已经截取生成好了,但是我功能还没做完,还有一个点击下载 imageBase64 的功能,我先在这简单的贴出实现的代码:
<button onclick="downloadDocs(imageBase64)" >下载图片</button>
script>
function downloadDocs(imageBase64) {
var aLink = document.createElement('a');
// 创建一个 a 标签
var evt = document.createEvent("HTMLEvents");
// 创建一个 HTMLEvents 类型的事件
evt.initEvent("click", false, false);
// 设置点击
aLink.download = data_test + "test.png";
// 设置下载的文件名
aLink.href = imageBase64;
// 设置下载链接
// aLink.click();
// aLink.dispatchEvent(evt);
aLink.dispatchEvent(new MouseEvent('click', {bubbles: true, cancelable: true, view: window}));
//触发点击事件,兼容火狐
}
</script>
上面的截图实现尽量不要用在复杂的页面上,毕竟是高度依赖于浏览器的。
还有上面贴的 JavaScript 下载 imageBase64( data:image/png )的方法肯定存在浏览器兼容问题的,不过在 Chrome 上应该没问题