自己動手實現一個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.pswp.cn/news/537810.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/537810.shtml
英文地址,請注明出處:http://en.pswp.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…