canvas与截图自动标注

280 阅读2分钟

前言

最近遇到了一个比较骚的需求, 难点在于在网页中, 每点一次按钮要截图并且标注出所点击的按钮.

解决思路

查找资料发现较为常见的前端截图库是 html2canvas, 暂时不清楚是否能截到 iframe 中的内容. 好消息是需求允许使用静态截图 (不考虑动态资源).

到这个时候实现起来就容易了:

  1. 准备好一张静态资源图 (自己电脑截一张)
  2. 创建 canvas 画入静态图
  3. btn -> click事件时获取元素的坐标 ( e.target.getBoundingClientRect() )
  4. 把坐标传给 canvas 在指定位置标注出来
  5. 把 canvas 转成 img
  6. 上传

实现

export default function useAutoMark(x, y, width, height) {
  function createDom(name) {
    return document.createElement(name)
  }
  
  function showImage(canvas) {
    const image = createDom("img")
    // 转为 png 格式的图片
    image.src = canvas.toDataURL("image/png")
    image.style.width = "100%"
    document.body.appendChild(image)
  }
  
  const canvas = createDom("canvas")
  const ctx = canvas.getContext("2d")
  const img = new Image()
  img.src = require("...") // 由于是静态图片 这里直接写死
  img.onload = function() {
    canvas.width = this.width
    canvas.height = this.height
    // 画入图片
    ctx.drawImage(this, 0, 0, this.width, this.height)
    // strokeRect() 方法绘制矩形(无填充)。笔触的默认颜色是黑色
    // 使用 strokeStyle 属性来设置笔触的颜色、渐变或模式
    ctx.strokeStyle = "#FF0000"
    ctx.strokeRect(x, y, width, height)
    
    // 开发环境在页面中展示方便调试, 
    if (process.env.NODE_ENV.includes("dev")) showImage(canvas)
    
    // 图片传给后端...
  }
}

btn click事件

handClick(e) {
  const { left, top, width, height } = e.target.getBoundingClientRect()
  useAutoMark(left - 5, top - 5, width + 10, height + 10)
}

BUG与解决方法

代码写出来后 canvas 根据坐标自动标注出来的矩形 和 图片上按钮的实际位置 有些许偏差, 于是我开始怀疑 getBoundingClientRect 获取出来的坐标不对.

查找资料并且手动计算后发现我的怀疑是多余的.

那这就很奇怪了 为什么会有偏差, 既然通过 API 获取的结果是正确的, 那问题只可能出在静态截图上.

于是, 我开始测量截图上的元素到边距的距离 大概是 128px, 而通过 API 获取的是 102px.

这就很有意思了 (128 - 102) / 102 * 100 ≈ 25%

看到这个结果, 已经知道是哪出了问题. 在 win10 上, 显示比例默认是 125%, 去设置里把它调整到 100% 后重新截图即可. ( 截完图记得把比例调回去 )