自己动手实现一个html2canvas

前言

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

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

先让大家看看效果

MDN示例

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');var data = '<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">' +'<foreignObject width="100%" height="100%">' +'<div xmlns="http://www.w3.org/1999/xhtml" style="font-size:40px">' +'<em>I</em> like' +'<span style="color:white; text-shadow:0 0 2px blue;">' +'cheese</span>' +'</div>' +'</foreignObject>' +'</svg>';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)}">\n`if (element.children.length) {// 递归子元素for (let el of element.children) {str += renderDom(el)}} else {str += element.innerHTML}str += `</${tag}>\n`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 `<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">\n<foreignObject width="100%" height="100%">\n${renderDom(element, 1)}</foreignObject>\n</svg>`
}复制代码

这里顺便给个绘制到 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/537810.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Git fetch pull 详解

1、简单概括 先用一张图来理一下git fetch和git pull的概念&#xff1a; 可以简单的概括为&#xff1a; git fetch是将远程主机的最新内容拉到本地&#xff0c;用户在检查了以后决定是否合并到工作本机分支中。 而git pull 则是将远程主机的最新内容拉下来后直接合并&#x…

linux 安装redis2.8.3,Linux及Windows安装Redis(详细)

标签&#xff1a;Linux及Windows安装Redis1.Windows安装教程1.1下载https://github.com/MSOpenTech/redis/releases进入github里下载redis Windows版压缩包将我们下载好的文件放进新建的一个Redis文件夹(我在C盘创建了一个redis的文件夹)1.2启动服务打开我们Windows版的dos命令…

Git 少用 Pull 多用 Fetch 和 Merge

转自&#xff1a;http://www.oschina.net/translate/git-fetch-and-merge --------------------------------------------------------------------------------- 本文有点长而且有点乱&#xff0c;但就像Mark Twain Blaise Pascal的笑话里说的那样&#xff1a;我没有时间让…

IDEA生成可运行jar包

方式1: maven打包 maven 包中添加如下配置 <build><plugins><plugin><artifactId>maven-assembly-plugin</artifactId><configuration><appendAssemblyId>false</appendAssemblyId><descriptorRefs><descriptorRef&…

linux packet socket,linux Packet socket (1)简单介绍

本文主要来自于linux自带的man packet手冊&#xff1a;http://man7.org/linux/man-pages/man7/packet.7.html平时常常使用的INET套接字提供的是7层的抓包能力&#xff0c;抓上来的data直接就是tcp或者udp的payload&#xff0c;无需关心L3和L4的头部信息。Packet套接字提供的是L…

TortoiseGit 修改密码

当TortoiseGi默认设置了凭证助手为“管理器-所有windows用户”&#xff0c;每次向远程git推送时&#xff0c;都会去windows的凭证管理器里读取值&#xff0c;然后推送。 如果密码修改了&#xff0c;或者密码不小心输入错了&#xff0c;每次提交都会报错&#xff1a;HTTP Basic:…

linux系统常见操作,Linux系统基本操作

我们可以认为Linux是一套自由使用的类Unix操作系统&#xff0c;与Windows相比较而言&#xff0c;Linux具有安全、开源、稳定等特点。下面我来介绍Linux中一些的登录登出基本操作。1. 启动系统通常LILO是安装在MBR上的&#xff0c;计算机启动后&#xff0c;MBR上的程序被执行&am…

字符串常见处理

mystr hello world itcast and itcastcpps mystr.find(hello)print(s)ind mystr.index(world)print(ind)cou mystr.count(c)print(cou)rep mystr.replace(c,)print(rep)spl mystr.split( )print(spl)转载于:https://www.cnblogs.com/zxt-cn/p/9714841.html

git 无法访问

git分2种访问方式&#xff1a;ssh&#xff0c;https ssh模式&#xff0c;需要在github或gitlab上配置公钥&#xff0c;本地要生成秘钥。 举例&#xff1a; 公司使用gitlab 张工需要访问公司李工的代码库。 步骤&#xff1a; 1、李工要在gitlab他的项目里&#xff0c;给张…

c语言编程统计单词的个数,使用c语言如何统计单词个数

使用c语言如何统计单词个数发布时间&#xff1a;2020-04-21 13:58:58来源&#xff1a;亿速云阅读&#xff1a;207作者&#xff1a;小新使用c语言如何统计单词个数&#xff1f;相信有很多人都不太了解&#xff0c;今天小编为了让大家更加了解Golang&#xff0c;所以给大家总结了…

10 种保护 Spring Boot 应用的绝佳方法

Spring Boot大大简化了Spring应用程序的开发。它的自动配置和启动依赖大大减少了开始一个应用所需的代码和配置量&#xff0c;如果你已经习惯了Spring和大量XML配置&#xff0c;Spring Boot无疑是一股清新的空气。 Spring Boot于2014年首次发布&#xff0c;自那以后发生了很多变…

zkServer.cmd 闪退

调用 zkEnv.cmd 查看下 zkEnv.cmd 在\conf下复制zoo_sample.cfg 重命名为 zoo.cfg 再运行&#xff0c;成功

c语言单字符输入和输出函数分别为,第03章单元总练习-实训-知识拓展.doc

第03章单元总练习-实训-知识拓展《C语言程序设计》单元总结单元练习实训指导知识拓展第三章 最简单的C程序设计——顺序结构设计班级:姓名:学号:单元总结提升本单元中&#xff0c;核心内容有C语言中基本的数据类型、常量和变量、运算符和表达式以及算法的概念。通过本单元的学习…

那些你不知道的 getClientRects()

1.getClientRects()。是可以获取内联元素的内容有多少行 最近一个交互&#xff0c;在限定文字展现是5行&#xff0c;超过5行&#xff0c;则在后面添加。。。展开。如果没有展开二字&#xff0c;我们一般用css就能完成了。但是为了交互更人性化 text-overflow: -o-ellipsis-last…

idea统计代码行数

使用统计代码插件&#xff0c;可以统计代码行数。安装插件 Statistic。&#xff08;这个最好用&#xff09; File----settiing---plugins---browse repositories 重启idea后&#xff0c;底部会多一个 Statistic 点击刷新&#xff0c;行数就出来了。看最后的 Total

数据结构计算c语言数据步骤,数据结构C语言版视频教程-介绍各种最常用的数据结构 分析各种数据结构运算算法的实现过程-电脑网络视频-星火视频教程 21edu8.com...

这部数据结构C语言版视频教程结构清晰&#xff0c;实例丰富&#xff0c;具有很强的操作性和实用性。 它主要为大家介绍各种最常用的数据结构&#xff0c;以及从编程角度出发&#xff0c;分析各种数据结构运算算法的实现过程。数据结构是计算机存储、组织数据的方式。数据结构是…

极光推送小结 - iOS

此次即友盟分享小结(友盟分享小结 - iOS)之后对推送也进行了一版优化.此次分享内容依然基于已经成功集成 SDK 后 code 层级部分. 注:此次分享基于 SDK 3.1.0,若版本相差较大,仅供参考. 极光推送官方文档: https://docs.jiguang.cn/jpush/guideline/intro/ 首先,为分享单独创建了…

word去除所有的空行

申请软著时&#xff0c;需要复制源代码到word里。每行代码不能有换行&#xff0c;要紧凑的80页代码。每页要50~55行代码。 字体可设置为&#xff1a;宋体&#xff0c;5号&#xff0c;行间距固定值12。 演示实例 去除下面word代码里的空行 第一步&#xff1a; word显示隐藏的…

c语言Wndproc未定义,为什么我的老是未定义

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼这是源代码#includeLRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow){static TCHAR szAppName[] TEXT("HelloWin&qu…

spark on yarn

2019独角兽企业重金招聘Python工程师标准>>> spark on yarn 软件安装 当前环境 hadoop环境搭建参考&#xff1a;hadoop集群安装 hadoop2.6spark-2.2.0-bin-hadoop2.6.tgzscala-2.11.12安装scala tar -zxvf scala-2.11.12.tgz vi /etc/profile 添加以下内容 export S…