老板让我用JavaScript实现网页复制成图片到剪贴板

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

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

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

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

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

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

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

小王思考了片刻...

功能拆解:

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

  1. 获得选中内容所属的 div
  2. 把选中内容的div 转换成canvas
  3. 转换canvas到二进制图像
  4. 复制二进制图像到剪贴板

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

jsx 复制代码
  const element = document.getElementById("target");

转换div成 canvas:

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

  1. 递归遍历 DOM 树:
    • 会从指定的根元素开始,递归遍历整个 DOM 树。
    • 对于每个遇到的元素, 分析其样式、位置、大小等属性。
  2. 处理样式和布局:
    • 通过读取元素的 CSS 样式,如颜色、背景、边框等, 复制元素的视觉表现。
    • 它会计算元素的盒模型、定位、层叠等布局信息,以确定元素在最终图片中的位置。
  3. ...

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

jsx 复制代码
html2canvas(document.body).then(function(canvas) {
    document.body.appendChild(canvas);
});

它正是小王需要的!

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

bash 复制代码
npm install --save html2canvas

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

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

jsx 复制代码
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 :

jsx 复制代码
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时没有偷懒,才能在关键时刻派上用场,赢得了老板的青睐。

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

相关推荐
吃杠碰小鸡5 分钟前
高中数学-数列-导数证明
前端·数学·算法
kingwebo'sZone11 分钟前
C#使用Aspose.Words把 word转成图片
前端·c#·word
xjt_090131 分钟前
基于 Vue 3 构建企业级 Web Components 组件库
前端·javascript·vue.js
我是伪码农42 分钟前
Vue 2.3
前端·javascript·vue.js
夜郎king1 小时前
HTML5 SVG 实现日出日落动画与实时天气可视化
前端·html5·svg 日出日落
辰风沐阳1 小时前
JavaScript 的宏任务和微任务
javascript
夏幻灵2 小时前
HTML5里最常用的十大标签
前端·html·html5
冰暮流星2 小时前
javascript之二重循环练习
开发语言·javascript·数据库
Mr Xu_2 小时前
Vue 3 中 watch 的使用详解:监听响应式数据变化的利器
前端·javascript·vue.js
未来龙皇小蓝2 小时前
RBAC前端架构-01:项目初始化
前端·架构