资讯专栏INFORMATION COLUMN

自己动手实现一个html2canvas

rickchen / 1842人阅读

摘要:前言昨天写了新手引导动画的种实现方式里面用到了于是就顺便了解了一下实现思路大概就是利用的标签嵌入最后再利用绘制从而实现最终目的先让大家看看效果示例示例其实写的很清楚不过也相对比较简单一点是已经构建好的字符串其实我觉得整个过程里面最麻烦

前言

昨天写了新手引导动画的4种实现方式,
里面用到了 html2canvas 于是就顺便了解了一下实现思路.

大概就是 利用 svgforeignObject 标签, 嵌入 dom, 最后再利用 canvas 绘制 svg. 从而实现最终目的.

先让大家看看效果

MDN示例

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

var data = "" +
           "" +
           "
" + "I like" + "" + "cheese" + "
" + "
" + "
"; var DOMURL = window.URL || window.webkitURL || window; var img = new Image(); var svg = new Blob([data], {type: "image/svg+xml;charset=utf-8"}); var url = DOMURL.createObjectURL(svg); img.onload = function () { ctx.drawImage(img, 0, 0); DOMURL.revokeObjectURL(url); } img.src = url;

MDN示例其实写的很清楚,不过也相对比较简单一点, dom 是已经构建好的字符串, 其实我觉得整个过程里面最麻烦的就是构建 dom. 所以接下来,我们就来看看具体怎么实现吧

第一步 遍历目标节点的所有子元素,并构建对应的字符串
/**
 * 递归遍历所有子节点
 * @param element Document Element 要计算的元素
 * @param isTop Boolean 是否是最外层元素
**/
function renderDom (element, isTop) {
    let tag = element.tagName.toLowerCase()
    let str = `<${tag} `
    // 最外层的节点,需要加 xmlns 命名空间
    isTop && (str += `xmlns="http://www.w3.org/1999/xhtml" `)
    str += ` style="${getElementStyles(element)}">
`

    if (element.children.length) {
        // 递归子元素
        for (let el of element.children) {
            str += renderDom(el)
        }
    } else {
        str += element.innerHTML
    }
    str += `
`
    return str
}
这里只做了一个最简单的处理,由于是简单实现,很多特殊情况没考虑进去(如:单标签, img等),有兴趣的童鞋可以自己尝试实现看看.

最外层的元素, 需要加命名空间,否则无法识别

这里用到的 getElementStyles 就是获取元素的最终渲染样式,下一步会实现.

第二步, 获取元素的最终渲染样式,并拼接成行内样式

正常的 dom 元素, 是无法直接放在 foreignObject 里面准确地渲染的, 因为还要涉及到父子元素直接的属性继承, 元素默认属性, 非行内样式无法渲染等问题.
所以我们要获取每个元素的最终渲染样式, 然后拼接成行内样式.

如何获取元素的最终渲染样式呢? 刚好,浏览器有提供一个 window.getComputedStyle() 方法可以做到.

// 计算每个 dom 的样式
// 这里本来应该直接用 Object.keys + forEach 遍历取出的
// 但是不知道为什么,遍历取出的,会渲染不出来,应该是某些属性有问题
// 暂时没空去排查那些有问题,所以目前先把常用的直接写死.
function getElementStyles (el) {
    let css = window.getComputedStyle(el)
    let style = ""
    // 尺寸相关
    style += `width:${css.width};`
    style += `height: ${css.height};`
    style += `line-height: ${css.lineHeight};`
    style += `max-height: ${css.maxHeight};`
    style += `min-height: ${css.minHeight};`
    style += `max-width: ${css.maxWidth};`
    style += `min-width: ${css.minWidth};`

    style += `font-size: ${css.fontSize};`
    // 颜色相关
    style += `color: ${css.color};`
    style += `background: ${css.background};`
    // 边框相关
    style += `border: ${css.border};`
    style += `box-sizing: ${css.boxSizing};`
    // 位置相关
    style += `margin: ${css.margin};`
    style += `padding: ${css.padding};`
    style += `position: ${css.position};`
    style += `left: ${css.left};`
    style += `right: ${css.right};`
    style += `top: ${css.top};`
    style += `bottom: ${css.bottom};`
    // 布局相关
    style += `display: ${css.display};`
    style += `flex: ${css.flex};`
    return style
}
第三步, 渲染 svg

把拼接好的 svg 字符串用 Blob 对象 new 出来(Blob真的是个很强大的对象啊), 然后用 DOMURL.createObjectURL() 转换为 url,
有了url, 接下来就看大家自由发挥了. 可以直接下载,也可以在 canvas 里绘制. 或者当作图片直接插入到文档...

// 主入口函数
function shotScreen () {
    let target = document.querySelector(".content")
    let data = getSvgDomString(target)

    let DOMURL = window.URL || window.webkitURL || window;

    let img = new Image();
    let svg = new Blob([data], {type: "image/svg+xml;charset=utf-8"});
    let url = DOMURL.createObjectURL(svg);

    img.src = url;
    document.body.appendChild(img)
}

// 计算 svg 的字符串
function getSvgDomString (element) {
    return `
    

       

          ${renderDom(element, 1)}
       

   `
}

这里顺便给个绘制到 canvas 里的代码

//  如果想画到 canvas 里面
let canvas = document.getElementById("canvas");
let ctx = canvas.getContext("2d");
let img = new Image();

img.onload = function () {
   ctx.drawImage(img, 0, 0);
   DOMURL.revokeObjectURL(url);
}
最后

参考文档:

MDN: 将 DOM 对象绘制到 canvas 中

MDN: foreignObject

完整的代码在这里,可以直接运行看效果.

本文地址在->个人技术帖合集, 欢迎给个 start 或 follow

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/100062.html

相关文章

  • Javascript 将html转成pdf,下载,支持多页哦(html2canvas 和 jsPDF

    摘要:最近碰到个需求,需要把当前页面生成,并下载。但这并不是真的截图,而是通过遍历页面结构,收集所有元素信息及相应样式,渲染出。由于只能将它能处理的生成,因此渲染出来的结果并不是与原来一致。 最近碰到个需求,需要把当前页面生成pdf,并下载。弄了几天,自己整理整理,记录下来,我觉得应该会有人需要 :) 项目源码地址:https://github.com/linwalker/... html2...

    macg0406 评论0 收藏0
  • Javascript 将html转成pdf,下载,支持多页哦(html2canvas 和 jsPDF

    摘要:最近碰到个需求,需要把当前页面生成,并下载。但这并不是真的截图,而是通过遍历页面结构,收集所有元素信息及相应样式,渲染出。由于只能将它能处理的生成,因此渲染出来的结果并不是与原来一致。 最近碰到个需求,需要把当前页面生成pdf,并下载。弄了几天,自己整理整理,记录下来,我觉得应该会有人需要 :) 项目源码地址:https://github.com/linwalker/... html2...

    codecraft 评论0 收藏0
  • 实现Google带截图功能的web反馈插件

    摘要:谷歌是通过来实现这个组件的,比较复杂谷歌的工具加载文件和截图文件有兴趣的同学可以看一下。高亮区域核心部分截图搞定了,接下来就是高亮区域了。 几乎所有的APP应用包括Web应用都需要一个意见反馈,这样才能了解用户对产品的意见和建议,以便于不断提升完善自己的产品。目前的反馈组件一般有两种,一种是打开一个反馈页面填写表单,另一种则是通过弹窗来完成,相比较而言第二种更加方便,而且更加容易组件化...

    freecode 评论0 收藏0
  • html2canvas以及domtoimage的使用踩坑总结

    摘要:本人建议第二种,更保险三转换的位图不能被以上版本所识别。客户使用的时候发现了这个问题,没法。五离成功只有一步之遥了,使用了之后安卓手机不能将图片分享给朋友。识别不了问题分析安卓能识别但不能识别矢量图片解决办法自己手写咯。判断手机为安卓还是。 前言 首先做个自我介绍,我是成都某企业的一名刚刚入行约一年的前端,在之前的开发过程中,遇到了问题,也解决了问题,但是在下一次解决相同问题的时候,只...

    邹强 评论0 收藏0
  • html2canvas以及domtoimage的使用踩坑总结

    摘要:本人建议第二种,更保险三转换的位图不能被以上版本所识别。客户使用的时候发现了这个问题,没法。五离成功只有一步之遥了,使用了之后安卓手机不能将图片分享给朋友。识别不了问题分析安卓能识别但不能识别矢量图片解决办法自己手写咯。判断手机为安卓还是。 前言 首先做个自我介绍,我是成都某企业的一名刚刚入行约一年的前端,在之前的开发过程中,遇到了问题,也解决了问题,但是在下一次解决相同问题的时候,只...

    MageekChiu 评论0 收藏0

发表评论

0条评论

rickchen

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<