背景
这是19年三月份客户反馈的一个问题,说产品图片没有正常显示。有图有视频。并且竞争对手知道了这个消息,也准备拿这个事情大肆渲染。
解决
按老套路,面向谷歌编程,找到一种在图片请求中加时间戳的方法,加上之后复现频率确实少了,赶紧上线。
好景不长,过了两周客户又开始反馈了,看来没彻底解决啊。再次谷歌,找不到什么相关问题。静下心来分析的时候,记起在上一家公司的时候,有同事遇到过一个问题,他当时的做法是用了一些方法来重绘重排整个文档来解决。我就试了一下,
// 伪代码
canvas.show()
setTimeout(() => {
canvas.hide()
}, 1000)
居然生效了。
继续测试得出:在安卓内置微信浏览器中,如果canvas元素从渲染完成后计时30秒未曾达到【肉眼可见程度】,图片数据就会被清空,变成一张透明图片。因为我们的产品样式刚开始是默认隐藏的,需要用户手动触发才会展示。
我用了下面的方法
// 伪代码
canvas.show()
setTimeout(() => {
canvas.hide()
}, 4)
这样人眼应该察觉不到。确实,人眼看不到,然而也没有生效,bug还是存在,canvas会被清空。当我手动加到6毫秒的时候,人眼可以察觉到了,bug也解决了,可是这样用户体验很差,会感觉到闪烁一下。
// 伪代码
canvas.show()
setTimeout(() => {
canvas.hide()
}, 6)
刚好下班,和同事一起回家,聊到这个问题,他提醒我可以用透明度呀。姜的还是老的辣...
代码就变成了这样
// 伪代码
canvas.show()
canvas.setOpacity(0)
setTimeout(() => {
canvas.hide()
canvas.setOpacity(1)
}, 6)
再测试发现,之前的bug又出现了...也就是说,只要人眼看不到这个canvas,就会存在这个bug。继续测试,元素透明度至少为0.033在该浏览器中才可被肉眼所见。代码就变成了这样
canvas.show()
canvas.setOpacity(0.033)
setTimeout(() => {
canvas.hide()
canvas.setOpacity(1)
}, 6)
不过,这样还是会闪烁一下,比之前稍微好了一些。最后,我设置了宽高属性来解决这个问题。
canvas.show()
canvas.setOpacity(0.033)
canvas.setWidth('1px')
canvas.setOpacity('1px')
setTimeout(() => {
canvas.hide()
canvas.setOpacity(1)
canvas.setWidth('origin width')
canvas.setWidth('origin height')
}, 6)
搞定~
总结
测试得出:在安卓内置微信浏览器中,如果canvas元素从渲染完成后计时30秒未曾达到【肉眼可见程度】,图片数据就会被清空,变成一张透明图片。(怀疑是引擎层面做的优化)
透明度为0.033
: 测试发现,元素透明度0.033
在该浏览器中才可被肉眼所见。
显示为6ms
: 测试发现,停留时间为6ms
在该浏览器中才可被肉眼所见。
什么行为会触发重排重绘?
添加、删除、更新DOM节点
通过display: none隐藏一个DOM节点-触发重排和重绘
通过visibility: hidden隐藏一个DOM节点-只触发重绘,因为没有几何变化
移动或者给页面中的DOM节点添加动画
添加一个样式表,调整样式属性
用户行为,例如调整窗口大小,改变字号,或者滚动。