前端跨域截图方案(截图通讯)
本篇是流水账, 后面放出html2canvas源库分析
写在前面
从去年11月份node截图服务遇到问题后,一直在鼓捣前端截图,也逐渐搞出一套自己的前端跨域截图方案,4.13号晚上突破10万张/天,还是挺开心的,当即用puppeteer写了脚本,通知到各个群里哈哈.
放张数量图
前端截图统计.png
目前业务里接入量不到一半,全部走前端截图应该在30万/天左右.
背景
现在做的业务有平台形式的,分享场景,大概是把各个业务方的h5页面,加上二维码生成一张图片,之前一直采用的是node截图服务,(类似phantomjs打开个无头浏览器用浏览器的打印能力生成截图,然后下发给调用方),但是我们这个业务线量太大了,20万b端用户,作业量集中(可参考上面的截图),qps高了,node扛不住.
研究了一下想借助html2canvas的能力, 实现 url -> base64.
给我一个链接,还你一张截图.
技术选型与思考
目前成熟的截图方案中,大概有这几种都能用来搞截图.
- 基于node的能力, 最上面那个截图数量就是笔者用puppeteer从统计网站上截的base64,然后调用企微机器人接口进行消息推送. node截图非常简单, 只需要两行
// 通过浏览器实例 Browser 对象创建页面 Page 对象 - puppeteer为例
const page = await browser.newPage();
page.screenshot({
encode: "base64"
});
2.基于端能力截图, 找app组的同学用端能力打开一个webview, 然后用端能力的截图方案进行截图,再jsbrige回调给前端.这种笔者认为非常好的一种方案,奈何app组的同学也有技术需求跟kpi,只能从长计议.
3.基于html2canvas截图,github可以搜到html2canvas的库,作者是尼克拉斯,维护很多年,也有很多问题,本质是把全部dom放到canvas里绘制出来, 然后基于canvas的convertToImage能力转成我们需要的base64格式图片.
优势与缺陷
分析一下node截图和html2canvas两种方案
截图方案 | 优势 | 缺陷 |
---|---|---|
node截图 | 1. 截图效果非常好 2.使用范围广,比如小程序内调用接口进行截图 3.追查方便,可以搞事,加几个暗水印还不是分分钟 |
1.吃服务,qps高了扛不住 2.接口截图需要走流量 3.截图页面需要匿名 4.安全部也要吃饭的,截图页面的外网资源够喝一壶了 |
html2canvas | 1.不占用服务,qps什么的不存在的 2.业务复制性强 3.稳定稳定还是稳定. |
1.截图效果普通 2.部分兼容问题,需要手动改html2cavans适应 3.截图速度依赖用户设备 4.不兼容的写法需要开发注意 |
流程梳理
最后得出方案如下
- 维护一个截图方法, 此方法可以用iframe打开一个跨域的h5, h5链接上有截图标识符,然后开启listening, 等待截图结果的返回
- 维护一个sdk, 跨域的业务方在项目中以script的形式引入sdk入口,此入口监听标识符,只会在命中时注入截图脚本,做到无污染.
3.业务方引入的sdk,会在命中截图逻辑时进行截图,截图会考虑以下内容
a.监听全部ajax , fetch请求
b.监听全部图片加载
c.全部接口与图片加载完毕后,执行截图方法
d.截图后使用postmessage实现跨域, 并传递截图内容. - 截图成功Y.用截图结果搞事
截图失败N.走其他截图服务兜底.
其中业务方需要做的事: 引入一行script
平台方需要做的事: 维护截图方法, 此方法接收一个url, return 截图结果.
问题梳理
- ios截图文字重叠
html2canvas 在ios13+上遇到此问题, 在html2canvas测试函数里添加上例子即可,本质是getBoundingRect获取到0,0,0,0. - 超出打点gg
html2canvas 遇到webkit-box样式发现此问题, 源库未兼容此样式,目前跟inline-block用同样的key来兜底(结果是超出部分没有点,直接被阶段了,还需继续优化) - 文字font-family不对
依赖设备安装字体,node可以在机器安装,html2canvas无处安放,建议搞font-spider之类加个mini字体包才行. - 文字下沉
中文font-family遇到此问题, canvas渲染文字的时候,padding-top跟line-height有默认值,目前缩小成以前的1/4感觉良好. - 淘宝rem写法的项目,截图样式有问题
为了截取高清图,经常会放大截图再缩小,如果rem布局,不要随意更改iframe宽度会导致页面变形,尽量html2canvas去做的时候再动scale.
6.渐变色
百度吃现成的吧
7.伪类元素有问题
::after 跟::before 都是用实际dom模拟出来的,所以会进行appendChild的操作,聪明的你一定能猜到,会打乱原有的nth-child(even, odd, first, last)等伪类元素的顺序!
目前还没有特别好的解决方案, 只能说写法注意吧
8.object-fit不兼容
外层套个div, 加上overflow-hidden去写吧,也欢迎有志之士给html2canvas提pr~
...通用型问题后续会继续补充
code
业务代码就不开源了,有疑难杂症可以q群共同解决793841737
总结
使用html2canvas做前端截图真的有点吃力不讨好,
但是这个方案真的是又老又稳健的选择之一,
如果是toC的业务,千万以上的量级,那么我推荐你用前端截图做主方案,服务端截图兜底.
如果是对设计要求特别高的小众业务,那么我推荐你用服务端截图优先,前端截图兜底的方案.
后记
去年因为种种原因,从老东家离职,放弃了美团入职到现在公司,
又赶上结婚,业务调动,半路接手了现在的项目,用了相当多的时间和精力上手,了解业务边界,梳理流程,焦头烂额.
两年前做的vue版的DAG依然有人在使用,前端工程化群里也时不时有新同学加入,同腰间的赘肉一并提醒着:"不能倦怠,多多搞事",然而实在是力不从心.
好在业务慢慢稳定,去年下半年到今年上半年的一些思考,可以慢慢写出来,以供各位同仁阅读,以窥明径,沿辙而行~