飞书一键复制网页内容为图片原理

点击上方 前端Q,关注公众号

回复加群,加入前端Q技术交流群

李经理在使用飞书时无意中发现,飞书竟然支持一键复制网页内容到剪贴板的功能。

他立即叫来了公司的前端开发小王,兴致勃勃地说:

"小王啊,你看,飞书的这个功能多方便!我们公司的协同办公系统是不是也可以实现类似的功能?这样用户体验一定能得到很大提升!"

小王看着李经理充满expectant的眼神, 虽然内心已经吐槽"就这点功能至于吗", 但表面上还是恭恭敬敬地回答:

"老板英明,这个功能确实很实用。技术上应该不难实现,主要就是用Clipboard API写几行代码的事。我这就去安排!"

回到工位后,小王苦笑着摇摇头,找来相关文档开始翻阅,暗暗发誓一定要把这个"划时代"的功能做好.

小王找来了领导说的飞书文档复制网页内容的功能, 如下:

小王思考了片刻…

功能拆解:

要实现这个功能, 要拆分为4个步骤:

  1. 获得选中内容所属的 div

  2. 把选中内容的div 转换成canvas

  3. 转换canvas到二进制图像

  4. 复制二进制图像到剪贴板

由于小王的业务只需要复制固定区域的div, 所以第一步可以忽略, 简化成:


const element = document.getElementById("target");

转换div成 canvas:

时间已经很晚了, 小王咳了一杯咖啡, 继续奋战. 小王苦思冥想, 要怎么把div转换成 canvas. 他琢磨:

  1. 递归遍历 DOM 树:

    • 会从指定的根元素开始,递归遍历整个 DOM 树。

    • 对于每个遇到的元素, 分析其样式、位置、大小等属性。

  2. 处理样式和布局:

    • 通过读取元素的 CSS 样式,如颜色、背景、边框等, 复制元素的视觉表现。

    • 它会计算元素的盒模型、定位、层叠等布局信息,以确定元素在最终图片中的位置。

小王这时候已经觉得很累了, 于是索性打开浏览器搜索, 结果第一页就看到了: html2canvas. 他看了一眼, github 29K stars. 他查看了一下调用api:


html2canvas(document.body).then(function(canvas) {
document.body.appendChild(canvas);
});

它正是小王需要的!

于是小王在项目中命令行输入:


npm install --save html2canvas

然后小王在业务代码中敲下了:


function copyDivToImage() {
const element = document.getElementById("target");
html2canvas(element).then(canvas => {
// canvas 拿到了, 然后呢
}
}

转换canvas到二进制图像

小王犹豫, 为什么要转成二进制图像呢, 我直接复制 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) => {
// 复制文件到剪贴板
},
"image/jpeg", // 文件的格式
1 // 图像压缩质量 0-1
);
});
}

复制二进制图像到剪贴板

这一步小王已经先前看过 MDN 文档了, ClipboardItem - Web APIs | MDN (mozilla.org) 可以直接调用浏览器的 navigator api :



function copyDivToImage() {
const element = document.getElementById("target");
html2canvas(element).then(canvas => {
canvas.toBlob(
(blob) => {
// 复制文件到剪贴板
try {
await navigator.clipboard.write([
// eslint-disable-next-line no-undef
new ClipboardItem({
[blob.type]: blob
})
]);
console.log("图像已成功复制到剪��板");
} catch (err) {
console.error("无法复制图像到剪贴板", err);
}
},
"image/jpeg", // 文件的格式
1 // 图像压缩质量 0-1
);
});
}

小王遇到挫折

所有代码已经就绪, 小王随即启动项目, 运行他刚刚编写好的完美的代码. 不出所料,  他遇到了挫折:

小王看到这个报错, 完全没有头绪, 幸好有多年的开发经验, 他遇到这种问题的时候并没有慌张, 内心想, “第一次跑通常这样!”. 随即他打开百度搜索, 有一个回答引起了小王的注意:

原来, 小王是在 http 环境调试的, 他修改了代理的配置, 换成了 https 环境下调试本地代码.

然而让小王没有想到的是, 程序还是没有如期运行, 小王遇到了第二个挫折:

小王崩溃了 “这是什么鬼. 明明都是按照API文档写的!”

原来, 浏览器剪贴板对 jpeg的支持不大好, 于是小王把 canvas.toBlob() 的参数改成了 "image/png”.

他再次运行代码, 他成功了:

小王欣喜地把这个消息告诉了李经理.

功夫不负有心人,凭借扎实的JavaScript功底,小王很快就实现了一个简洁优雅的"一键复制"功能,并成功集成到公司的协同办公系统中。

李经理在看到小王的杰作后非常满意,当即表扬了小王的能力和效率,并承诺会在年终绩效考核中给予小王优秀评级,同时还暗示未来会给小王升职加薪的机会。小王听后喜上眉梢,他明白自己的努力和才能得到了老板的认可。

这次经历不仅巩固了小王在公司中的地位,更坚定了他在前端开发领域继续钻研的决心。他暗自庆幸,幸亏当初学习JavaScript时没有偷懒,才能在关键时刻派上用场,赢得了老板的青睐。

从此以后,小王在技术方面更加勤奋刻苦,也更加善于捕捉用户需求和痛点,设计出更多优秀的功能和体验。他逐渐成长为团队中不可或缺的核心成员,并最终如愿晋升为高级前端开发工程师,走上了实现自我价值和理想的康庄大道。

往期推荐


你不知道的 async、await 魔鬼细节
13个漂亮的登录页面,附源代码地址
React 渲染流程可视化,有大佬实现了!

最后

  • 欢迎加我微信,拉你进技术群,长期交流学习...

  • 欢迎关注「前端Q」,认真学前端,做个专业的技术人...

点个在看支持我吧

相关推荐

  • 大脑一片空白:难倒 90% 前端的 Vue 面试题!
  • 万万没想到,用浏览器打开终端竟这么容易实现
  • 萝卜快写、萝卜快画来了?自动写小说、自动画漫画,两个最新的开源项目
  • 高手必知的Linux三剑客 (grep、sed、awk)
  • 列表是怎么实现的?解密列表的数据结构
  • 一文读懂数据血缘分析原理与建设方法
  • 橙单,一个免费的代码生成神器
  • Git版本管理工具,每个工程师都应该知道的基础操作!
  • Obsidian插件:Make.md为你量身打造一个完美的个人系统。
  • 从零预训练LLAMA3的完整指南:一个文件,探索Scaling Law
  • 开源仅 1 天就斩获近万星!超越 RAG、让大模型拥有超强记忆力的 Mem0 火了!
  • 拿下NeurIPS 2024金牌。
  • 博士申请 | 香港理工大学石杰明老师招收大数据/机器学习方向全奖博士/博后
  • 文末送书 | 连续25年美国统计类教材排名第一,这本统计学神书中文版来啦!
  • ICML 2024 | 图上的泛化挑战:从不变性到因果性
  • 转行跳槽做量化一定要注意的几个大坑。
  • 超强图解 Pandas 18 招!
  • 不是付费订阅用不起,而是“开源平替”更有性价比
  • [开源]自主研发基于SpringBoot + Activiti 开发的轻量级工作流框架
  • 终于有人讲明白了,数据资产、标签体系、指标体系、数据体系与用户画像(附案例+资料下载)