本博客 hjy-xh,转载请申明出处
探究起因
之前使用canvas
做了一个绘制水印的功能,可以简单理解成进入A页面后,从接口获取绘制水印的配置,然后在A页面的某个区域使用canvas
加盖一层水印
开发和测试都在chrome浏览器上进行,期间均表现正常,直到交付给客户几个月后接到反馈,其APP内嵌我们开发的H5页面,先后出现了以下问题:
- Android机型正常进入页面,偶尔水印不加载
- 在IOS下打开页面后无法正常加载,控制台有报错
- 在华为鸿蒙系统下APP直接闪退
嚯,问题还挺多,一个比一个严重- -!那接下来看看,是什么问题导致以上现象
下文使用我另外写的一个demo做演示
排查过程
第一个问题相对来说比较简单,这里简单说说
使用Android机型进行测试,确实是偶现不加载的情况,经过反复调试,发现在弱网情况下,没有请求接口,这里调整了请求的顺序就解决了
第二个问题
一开始在chrome和Firefox下均未复现,后来发现仅在iPhone真机Safari下才能复现,报错如下:
难道ctx是null吗,结合部分代码调试后查看确实没有获取到渲染上下文
1 | const canvas = document.getElementById("canvas"); |
这时想起来Safari对于canvas的使用有一些限制(或者说是优化),印象中该浏览器对于canvas的数量有一定限制,emmm,我的代码中只有一个canvas,应该和这个限制没有关系
然后我开始搜索Safari对于使用canvas有什么限制/webkit内核对于canvas的限制,好的,没找到什么蛛丝马迹。于是开始面向google编程,翻一翻有没有人遇到过相似的问题
在一篇文章中有人提到 Safari对于画布大小有限制,于是我修改宽高,均缩小10倍,也就是渲染的区域缩小了100倍,刷新页面后正常加载了!
1 | canvas.width = 1000; |
问题解决了是针不戳啊,那具体的尺寸是多少等解决完所有问题再研究下
第三个问题
APP闪退,刚开始初步判断可能是客户环境webview做了某些处理,和我们H5页面没有什么关系,于是请教APP开发大佬,大佬一顿排查后得出结论:在华为鸿蒙系统下,H5页面渲染太久了,导致闪退
好家伙,那这个情况还是因为受到了画布大小影响?!
于是让测试同学在响应系统上通过APP打开H5页面,确实是没有再发生闪退…
那至此bug全部解决
探究Safari对于canvas的限制
已知:Safari采用webkit内核
解:去github上clone一份源码瞅瞅
1 | git clone https://github.com/WebKit/WebKit.git |
(下面贴的源码均在Source/WebCore/html/HTMLCanvasElement.cpp
文件中,可以直接翻阅,clone下来太久啦)
Safari 在实现canvas
时对内存进行了限制,在不同设备上允许使用的内存不同,具体根据设备 RAM 的大小计算,一旦超出限制,使用 getContext(‘2d’) 将会返回 null
C++源码如下:
1 | CanvasRenderingContext2D* HTMLCanvasElement::createContext2d(const String& type, CanvasRenderingContext2DSettings&& settings) |
可以看到 canvas
的内存占用空间为 4 * width * height
,这里的4指每个像素的RGBA
允许使用的最大内存空间由 maxActivePixelMemory
这个函数计算,这个函数计算规则如下:
1 | size_t HTMLCanvasElement::maxActivePixelMemory() |
再查找源码对于画布大小的限制:
1 | static inline size_t maxCanvasArea() |
经过实际测试,在iPhone的Safari中,canvas
的最大可用宽高确实是4096
具体可以看参考文章中的Safari难道是下一个IE?兼容性这么“差”
其它
另外在chrome、Firefox中,过大的宽高(100000),也无法正常展示,如下所示:
没有探讨释放canvas
占用的内存,大家有兴趣可以尝试一下
参考
MDN