背景

这是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在该浏览器中才可被肉眼所见。

什么行为会触发重排重绘?

  1. 添加、删除、更新DOM节点

  2. 通过display: none隐藏一个DOM节点-触发重排和重绘

  3. 通过visibility: hidden隐藏一个DOM节点-只触发重绘,因为没有几何变化

  4. 移动或者给页面中的DOM节点添加动画

  5. 添加一个样式表,调整样式属性

  6. 用户行为,例如调整窗口大小,改变字号,或者滚动。