📣 背景

最近在工作中, 遇到了用户如果缩放浏览器窗口, 或者使用 mac 笔记本的触摸板缩放浏览器窗口时, canvas 会模糊的问题,
原因很简单, 缩放之后, 浏览器的 window.devicePixelRatio 已经发生改变, 所以要用最新的 devicePixelRatio 去绘制

window.resize 为什么不行?

对于用户使用键盘, 比如 commond + + 和 common + - 缩放时, 事情很好办, 使用 resize 事件即可

1
2
3
4
5
6
7
8
<body>
<h1>VisualViewport 测试</h1>
</body>
<script>
window.addEventListener('resize', () => {
console.log('resize', window.devicePixelRatio);
});
</script>

2021-03-31 16.42.38.gif

###

那么如果换成 mac 的触摸板双指缩放呢?
2021-03-31 16.44.01.gif
尴尬了, window.resize 表示: 大哥, 超纲了? 从图中可以看出来, 事件没有相应

给力的 VisualViewport


面向 Google 编程了一段时间, 总算找到了 浏览器的一个实验性功能, 根据 MDN 的描述:

Visual Viewport API 提供了当前页面的视觉视口接口,即 VisualViewport 。对于每个页面容器来说(如 iframe),都存在有一个独立的 window 对象。每个页面容器的 window 对象都有一个独立的 VisualViewport 属性。你可以使用 Window.visualViewport 获得对应 window 的视觉视口 API。


看起来似乎 mac 的触控板缩放也是一种视觉改变, 现在来试试, 根据文档, 它有两个接口

image.png
resize 似乎可以满足我们的需求, 编写一点简单的代码, 测试一下

1
2
3
4
5
6
7
8
<body>
<h1>VisualViewport 测试</h1>
</body>
<script>
window.visualViewport.addEventListener('resize', (e) => {
console.log(e.target.scale, window.devicePixelRatio);
});
</script>


首先还是使用键盘进行缩放, 发现能正常响应, 但是 e.target.scale 始终是 12021-03-31 16.50.49.gif

###

接下来使用 mac 触摸板进行双指缩放, 效果如下:
2021-03-31 16.53.17.gif

###

可以看到 e.target.scale 随着双指的缩放在改变, window.devicePixelRatio 始终是 1, 看起来的效果就像, 使用了 css3 的 transform: scale 一样, 元素的实际大小没变.

经过测试,可以看出, 双指缩放 可以使用 e.target.scale 获取缩放比, 键盘缩放, 可以使用 window.devicePixelRatio , 使用 Math.max 取最大字就可以兼容两种情况

1
2
3
4
window.visualViewport.addEventListener('resize', (e) => {
const scale = Math.max(e.target.scale, window.devicePixelRatio);
console.log('scale: ', scale);
});

###

兼容性

还是有点恐怖, 任重而道远!

image.png

🔭 总结

这种冷门的 API (仅对我而言), 看似没啥用, 有时却能解决大问题, 看来平时还是要多积累知识才行, 多看看 web.dev, 涨涨知识!