背景
最近在工作中,遇到了用户如果缩放浏览器窗口,或者使用 Mac 笔记本的触摸板缩放浏览器窗口时,Canvas 会模糊的问题,
原因很简单,缩放之后,浏览器的 window.devicePixelRatio 已经发生改变,所以要用最新的 devicePixelRatio 去绘制
window.resize 为什么不行?
对于用户使用键盘,比如 commond + + 和 common + - 缩放时,事情很好办,使用 resize 事件即可
<body>
<h1>VisualViewport 测试</h1>
</body>
<script>
window.addEventListener('resize', () => {
console.log('resize', window.devicePixelRatio);
});
</script>
那么如果换成 Mac 的触摸板双指缩放呢?
尴尬了,window.resize 表示:大哥,超纲了?从图中可以看出来,事件没有相应
给力的 VisualViewport
面向 Google 编程了一段时间,总算找到了 浏览器的一个实验性功能,根据 MDN 的描述:
Visual Viewport API 提供了当前页面的视觉视口接口,即 VisualViewport 。对于每个页面容器来说(如 iframe),都存在有一个独立的 window 对象。每个页面容器的 window 对象都有一个独立的 VisualViewport 属性。
你可以使用 Window.visualViewport 获得对应 window 的视觉视口 API。
看起来似乎 Mac 的触控板缩放也是一种视觉改变,现在来试试,根据文档,它有两个接口
Events
通过使用 addEventListener () 或者将监听回调函数赋值给对应的 oneventname 属性,可以为对应的视口事件添加监听。
- resize (en-US): 当视觉视口被改变时触发。也可以为 VisualViewport.onresize (en-US) 属性赋值来添加监听。
- scroll (en-US): 当视觉视口滑动时触发。也可以为 VisualViewport.onscroll (en-US) 属性赋值来添加监听。
resize 似乎可以满足我们的需求,编写一点简单的代码,测试一下
<body>
<h1>VisualViewport 测试</h1>
</body>
<script>
window.visualViewport.addEventListener('resize', (e) => {
console.log(e.target.scale, window.devicePixelRatio);
});
</script>
首先还是使用键盘进行缩放,发现能正常响应,但是 e.target.scale 始终是 1
接下来使用 Mac 触摸板进行双指缩放,效果如下:
可以看到 e.target.scale 随着双指的缩放在改变,window.devicePixelRatio 始终是 1, 看起来的效果就像,使用了 css3 的 transform: scale 一样,元素的实际大小没变.
经过测试,可以看出,双指缩放 可以使用 e.target.scale 获取缩放比,键盘缩放,可以使用 window.devicePixelRatio , 使用 Math.max 取最大字就可以兼容两种情况
window.visualViewport.addEventListener('resize', (e) => {
const scale = Math.max(e.target.scale, window.devicePixelRatio);
console.log('scale: ', scale);
});
兼容性
还是有点恐怖,任重而道远!
总结
这种冷门的 API (仅对我而言), 看似没啥用,有时却能解决大问题,看来平时还是要多积累知识才行,多看看 web.dev, 涨涨知识!。