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 上应该没问题