为实现前端截图功能,我的dom-to-image踩坑之旅!

需求背景

最近产品要求做一个截图的功能,其背景是我们做的是一个教育类的网页,我们会统计学员的做题、听课、任务等数据,然后按照班级进行排名,然后班主任需要把这些数据发到班级群里,所以需要一个截图的功能。

为什么不让班主任自己搞一个截屏软件截图呢?

  1. 并不是所有班主任都有一个好的截屏软件,截出来效果不一定理想,
  2. 如果数据量比较大,出现了滚动条,这时候截屏操作起来比较麻烦,
  3. 自己实现的截屏功能可以实现截图的定制化,比如优化样式,往里面增加一些元素,比如二维码啥的等等。

我这里是采用了前端dom-to-image截图方案,至于为什么用这个方案,主要原因有两点:

  1. 在项目中其它场景中已经有过这个截图功能,用的就是dom-to-image,属于是直接吃现成的了,
  2. dom-to-image基本能满足我的截图要求,没必要再用其它方案,再引入一个其它的npm包进来了!

于是开始了我的dom-to-image踩坑记录!

问题1:图片生成比较模糊

我用了dom-to-image的toPng去生成图片,虽然生成的图片比较小,只有100多kb,但是清晰度比较差,我尝试把quality参数调到最大值1,然而并没什么效果,还是模糊。最终在网上找到了一个方案,那就是修改源码,增加scale参数。

我直接把dom-to-image的源码下载下来,找到newCanvas方法,然后添加了下面这些代码。

diff 复制代码
function newCanvas (domNode) {
    var canvas = document.createElement('canvas');
    var ctx = canvas.getContext('2d');
    ctx.mozImageSmoothingEnabled = false;
    ctx.webkitImageSmoothingEnabled = false;
    ctx.msImageSmoothingEnabled = false;
    ctx.imageSmoothingEnabled = false;
+    var scale = options.scale || 1; 
+    canvas.width = (options.width * scale) || util.width(domNode);
+    canvas.height = (options.height * scale) || util.height(domNode);
+    ctx.scale(scale, scale)
    if (options.bgcolor) {
        ctx.fillStyle = options.bgcolor;
        ctx.fillRect(0, 0, canvas.width, canvas.height);
    }

    return canvas;
}

加完后在使用的时候就可以传入scale参数了:

js 复制代码
const box = document.getElementById('box');
const node = document.getElementById('output');
domToImage.toPng(node, { bgcolor: '#fff', scale: 5, width: box.offsetWidth, height: box.scrollHeight });

这样虽然图片生成的大了点,但清晰度很高。

注意:如果遇到某些设备出现生成图片失败的问题,可能是这些设备性能比较低,这时候需要适当降低scale的值。

问题2:图片url无法访问时,导致截图失败

由于某些业务原因,后端返回的用户头像图片中,有一个url无法访问,导致截图失败了。于是我做了下兼容,在图片访问失败的时候,给一张默认图片,然后用一个变量统计图片正常加载的个数,等所有图片都正常加载完后再渲染图片。

html 复制代码
<template>
  <div v-for="item in list" :key="item.id">
    <img class="avatar" :src="item.mediumAvatar || defaultAvatar"  @error="onImageError" @load="onImageLoad"/>
  </div>
</template>

<script>
export default {
  data() {
    return {
      defaultAvatar: 'https://xxx.jpg',
      list: [],
      imageCount: 0,
    }
  },
  methods: {
    onImageError(e) {
      e.target.src = this.defaultAvatar;
    }
    onImageLoad() {
      if (++this.imageCount === this.list.length) {
        this.transformImage();
      }
    },
    // 截图
    transformImage() {
      // ...
    }
  }
}
</script>

问题3:某些ios手机下会生成图片失败

经测试发现,某些ios手机下会生成图片失败,于是也在网上找了下方案,最后通过连续调用两次toPng方法解决了问题,代码如下:

js 复制代码
const ios = ios: /iPad|iPhone|iPod/.test(navigator.userAgent) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1), //ios终端
const SCALE = 2;
const box = document.getElementById('box');
if (ios) {
  domToImage
    .toPng(node, { bgcolor: '#fff', scale: SCALE, width: box.offsetWidth, height: box.scrollHeight })
    .then((imgUrl) => {
      return domToImage.toPng(node, { bgcolor: '#fff', scale: SCALE, width: box.offsetWidth, height: box.scrollHeight  });
    })
    .then((imgUrl) => {
      console.log('imgUrl', imgUrl)
    })
    .catch((error) => {
      console.error('图片生成失败', error);
    })
}  

小结

上面主要记录了我为了实现前端截图功能,而被迫开始的dom-to-image踩坑之旅了,希望能对大家平常开发有帮助!

相关推荐
丨白色风车丨3 分钟前
LangChain Agent 从入门到实战:创建与调用参数全解析
前端·python·langchain
Jmyd01238 小时前
Web端3D营销展厅技术实现路径:从素材上云到线索回流的数据链路
前端·3d·性能优化·三维数字化
半个落月8 小时前
LangChain Agent 中间件实战(上):生命周期、状态、短路与工具包
javascript·langchain
砚凝霜8 小时前
【软考信息安全】第二十二章 Web服务器安全分析与增强——Apache与IIS
服务器·前端·安全
半个落月8 小时前
LangChain Agent 中间件实战(下):DeepAgents文件权限、长期记忆与对话摘要
javascript·langchain
u01110267510 小时前
JPG、PNG、WebP 怎么选?按透明度、体积和使用场景做判断
前端·javascript·vue.js·图像处理·ai作画
jerrywus10 小时前
让 AI 写完代码后交一份报告:我改造了 answer-me-with-html
前端·claude·cursor
派小心.11 小时前
小程序分享链路中来源参数的传递校验
大数据·前端·数据库·人工智能·小程序·数据分析
hhb_61811 小时前
一键生成数据看板,ECharts实战演示
前端·javascript·echarts
Java后端的Ai之路11 小时前
08_React组件API
前端·react.js·前端框架