自己动手实现一个html2canvas

栏目: Html5 · 发布时间: 6年前

内容简介:昨天写了大概就是 利用先让大家看看效果

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

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

先让大家看看效果

自己动手实现一个html2canvas

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);
}
复制代码

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

C Primer Plus

C Primer Plus

Stephen Prata、云巅工作室 / 云巅工作室 / 人民邮电出版社 / 2005-2-1 / 60.00元

《C Primer Plus(第5版)(中文版)》共17章。第1、2章学习C语言编程所需的预备知识。第3到15章介绍了C语言的相关知识,包括数据类型、格式化输入输出、运算符、表达式、流程控制语句、函数、数组和指针、字符串操作、内存管理、位操作等等,知识内容都针对C99标准;另外,第10章强化了对指针的讨论,第12章引入了动态内存分配的概念,这些内容更加适合读者的需求。第16章和第17章讨论了C预处......一起来看看 《C Primer Plus》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具