飛書一鍵復(fù)制網(wǎng)頁內(nèi)容為圖片原理
共 3457字,需瀏覽 7分鐘
·
2024-08-16 09:10
很多2C的移動端H5應(yīng)用,都會有邀請好友、分享之類的,通常會在前端實時生成一副海報圖片,用戶可以分享到朋友圈等,這類需求都需要解決前端DOM轉(zhuǎn)圖片的問題。今天帶來一篇文章,作者從飛書的一鍵復(fù)制網(wǎng)頁內(nèi)容為圖片,層層分解前端涉及到的技術(shù)點,希望大家看完有收獲。
下面是正文部分。
李經(jīng)理在使用飛書時無意中發(fā)現(xiàn),飛書竟然支持一鍵復(fù)制網(wǎng)頁內(nèi)容到剪貼板的功能。
他立即叫來了公司的前端開發(fā)小王,興致勃勃地說:
"小王啊,你看,飛書的這個功能多方便!我們公司的協(xié)同辦公系統(tǒng)是不是也可以實現(xiàn)類似的功能?這樣用戶體驗一定能得到很大提升!"
小王看著李經(jīng)理充滿expectant的眼神, 雖然內(nèi)心已經(jīng)吐槽"就這點功能至于嗎", 但表面上還是恭恭敬敬地回答:
"老板英明,這個功能確實很實用。技術(shù)上應(yīng)該不難實現(xiàn),主要就是用Clipboard API寫幾行代碼的事。我這就去安排!"
回到工位后,小王苦笑著搖搖頭,找來相關(guān)文檔開始翻閱,暗暗發(fā)誓一定要把這個"劃時代"的功能做好.
小王找來了領(lǐng)導(dǎo)說的飛書文檔復(fù)制網(wǎng)頁內(nèi)容的功能, 如下:
小王思考了片刻…
功能拆解:
要實現(xiàn)這個功能, 要拆分為4個步驟:
獲得選中內(nèi)容所屬的 div
把選中內(nèi)容的div 轉(zhuǎn)換成canvas
轉(zhuǎn)換canvas到二進制圖像
復(fù)制二進制圖像到剪貼板
由于小王的業(yè)務(wù)只需要復(fù)制固定區(qū)域的div, 所以第一步可以忽略, 簡化成:
const element = document.getElementById("target");
轉(zhuǎn)換div成 canvas:
時間已經(jīng)很晚了, 小王咳了一杯咖啡, 繼續(xù)奮戰(zhàn). 小王苦思冥想, 要怎么把div轉(zhuǎn)換成 canvas. 他琢磨:
遞歸遍歷 DOM 樹:
會從指定的根元素開始,遞歸遍歷整個 DOM 樹。
對于每個遇到的元素, 分析其樣式、位置、大小等屬性。
處理樣式和布局:
通過讀取元素的 CSS 樣式,如顏色、背景、邊框等, 復(fù)制元素的視覺表現(xiàn)。
它會計算元素的盒模型、定位、層疊等布局信息,以確定元素在最終圖片中的位置。
…
小王這時候已經(jīng)覺得很累了, 于是索性打開瀏覽器搜索, 結(jié)果第一頁就看到了: html2canvas. 他看了一眼, github 29K stars. 他查看了一下調(diào)用api:
html2canvas(document.body).then(function(canvas) {
document.body.appendChild(canvas);
});
它正是小王需要的!
于是小王在項目中命令行輸入:
npm install --save html2canvas
然后小王在業(yè)務(wù)代碼中敲下了:
function copyDivToImage() {
const element = document.getElementById("target");
html2canvas(element).then(canvas => {
// canvas 拿到了, 然后呢
}
}
轉(zhuǎn)換canvas到二進制圖像
小王猶豫, 為什么要轉(zhuǎn)成二進制圖像呢, 我直接復(fù)制 base64 字符不行嗎. 不過很快, 小王就意識到了, 剪貼版API 不支持base64字符串的類型. 于是他翻開 mdn 文檔:
HTMLCanvasElement: toBlob() method - Web APIs | MDN (mozilla.org)
function copyDivToImage() {
const element = document.getElementById("target");
html2canvas(element).then(canvas => {
canvas.toBlob(
(blob) => {
// 復(fù)制文件到剪貼板
},
"image/jpeg", // 文件的格式
1 // 圖像壓縮質(zhì)量 0-1
);
});
}
復(fù)制二進制圖像到剪貼板
這一步小王已經(jīng)先前看過 MDN 文檔了, ClipboardItem - Web APIs | MDN (mozilla.org) 可以直接調(diào)用瀏覽器的 navigator api :
function copyDivToImage() {
const element = document.getElementById("target");
html2canvas(element).then(canvas => {
canvas.toBlob(
(blob) => {
// 復(fù)制文件到剪貼板
try {
await navigator.clipboard.write([
// eslint-disable-next-line no-undef
new ClipboardItem({
[blob.type]: blob
})
]);
console.log("圖像已成功復(fù)制到剪??板");
} catch (err) {
console.error("無法復(fù)制圖像到剪貼板", err);
}
},
"image/jpeg", // 文件的格式
1 // 圖像壓縮質(zhì)量 0-1
);
});
}
小王遇到挫折
所有代碼已經(jīng)就緒, 小王隨即啟動項目, 運行他剛剛編寫好的完美的代碼. 不出所料, 他遇到了挫折:
小王看到這個報錯, 完全沒有頭緒, 幸好有多年的開發(fā)經(jīng)驗, 他遇到這種問題的時候并沒有慌張, 內(nèi)心想, “第一次跑通常這樣!”. 隨即他打開百度搜索, 有一個回答引起了小王的注意:
原來, 小王是在 http 環(huán)境調(diào)試的, 他修改了代理的配置, 換成了 https 環(huán)境下調(diào)試本地代碼.
然而讓小王沒有想到的是, 程序還是沒有如期運行, 小王遇到了第二個挫折:
小王崩潰了 “這是什么鬼. 明明都是按照API文檔寫的!”
原來, 瀏覽器剪貼板對 jpeg的支持不大好, 于是小王把 canvas.toBlob() 的參數(shù)改成了 "image/png”.
他再次運行代碼, 他成功了:
小王欣喜地把這個消息告訴了李經(jīng)理.
功夫不負有心人,憑借扎實的JavaScript功底,小王很快就實現(xiàn)了一個簡潔優(yōu)雅的"一鍵復(fù)制"功能,并成功集成到公司的協(xié)同辦公系統(tǒng)中。
李經(jīng)理在看到小王的杰作后非常滿意,當即表揚了小王的能力和效率,并承諾會在年終績效考核中給予小王優(yōu)秀評級,同時還暗示未來會給小王升職加薪的機會。小王聽后喜上眉梢,他明白自己的努力和才能得到了老板的認可。
這次經(jīng)歷不僅鞏固了小王在公司中的地位,更堅定了他在前端開發(fā)領(lǐng)域繼續(xù)鉆研的決心。他暗自慶幸,幸虧當初學(xué)習(xí)JavaScript時沒有偷懶,才能在關(guān)鍵時刻派上用場,贏得了老板的青睞。
從此以后,小王在技術(shù)方面更加勤奮刻苦,也更加善于捕捉用戶需求和痛點,設(shè)計出更多優(yōu)秀的功能和體驗。他逐漸成長為團隊中不可或缺的核心成員,并最終如愿晉升為高級前端開發(fā)工程師,走上了實現(xiàn)自我價值和理想的康莊大道。
作者:ziolau
https://juejin.cn/post/7348634049681293312
