这篇文章发布于 2021年02月15日,星期一,22:17,归类于 JS实例。 阅读 30555 次, 今日 25 次 14 条评论
by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=9832
本文欢迎分享与聚合,全文转载就不必了,尊重版权,圈子就这么大,若急用可以联系授权。
介绍我自己知道的几个检测用户是否缩放了页面的方法,这些方法都不算完美,大家根据实际场景自行抉择该使用哪个方法。
一、resize事件 + devicePixelRatio
如果希望知道用户是否执行了缩放行为,则可以在resize事件事件中检测设备像素比的变化,也就是window.devicePixelRatio
的返回值。
代码示意:
let lastPixelRatio = window.devicePixelRatio; window.addEventListener('resize', function () { let currentPixelRatio = window.devicePixelRatio; if (currentPixelRatio !== lastPixelRatio) { console.log('页面缩放变化了'); } lastPixelRatio = currentPixelRatio; });
当用户缩放页面的时候会触发窗体的resize事件,以及设备的devicePixelRatio也会跟着变化,于是,我们可以在resize事件中检测devicePixelRatio和缩放事件之前的devicePixelRatio值进行对比,如果变化,则可以认为页面发生了缩放。
但是,上面的方法是有明显的不足的,那就是只能知道页面缩放变化了,但是页面究竟缩放了多少并不知道。
例如,页面原来放大到了120%,然后用户还原到了100%,此时,也认为页面缩放变化了,实际上,此时页面1:1显示,是正常状态,需求上往往是不需要做处理的,但是上面的判断无法识别当前是100%缩放。
那有没有什么法子判断页面真实的缩放比例呢?
还挺难的,因为浏览器会记住当前域名下浏览器的缩放比例,并没有固定的基准devicePixelRatio参照值,因此,使用devicePixelRatio是没法计算页面的缩放比例的。
只能一定程度上进行判断
如果不是追求100%的精准,其实可以配合localStorage本地存储一定程度上判断浏览器的缩放比例,完整JS代码如下:
// 初始缩放比例 let originPixelRatio = localStorage.devicePixelRatio; if (!originPixelRatio) { originPixelRatio = window.devicePixelRatio; // 整数才保存 if (Number.isInteger(originPixelRatio)) { localStorage.devicePixelRatio = originPixelRatio; } } let lastPixelRatio = originPixelRatio; window.addEventListener('resize', function () { let currentPixelRatio = window.devicePixelRatio; if (currentPixelRatio !== lastPixelRatio) { console.log('缩放比例是:' + Math.round(1000 * (currentPixelRatio / originPixelRatio)) / 10 + '%'); } // 记住上一次的设备像素比 lastPixelRatio = currentPixelRatio; });
上面的JS代码效果可以狠狠地点击这里进行体验:resize事件加devicePixelRatio检测是否缩放demo
例如我自己电脑的Chrome浏览器下有如下所示的效果(Ctrl+/Ctrl-改变页面的比例试试):
可以准备识别用户进行了页面缩放,并准确显示了缩放比例。
但是,上面的实现并不严谨。
如果用户首次进入页面的时候,浏览器的视窗本身就是缩放状态,且此时的设备像素比是整数,则相关的判断就会出问题;虽然这种情况发生概率并不大,但是,理论上,就是会有这样的问题。
所以说,这个方法只能在一定程度上进行缩放比例的判断。
二、matchMedia检测devicePixelRatio变化
resize事件进行判断页面是否缩放会伴随一些不必要的消耗,因为当用户改变窗体的尺寸,或者设备发生旋转,或者设备打开了开发者工具等,都会触发resize事件,但是,显然,这些触发resize事件的场景和页面缩放是没有关系的,而且这些场景才是真正的常态发生的,缩放才是小众发生的。
那有没有其他什么办法更高效地检测浏览器是否发生的页面缩放呢?
此时可以试试matchMedia()
方法,语法如下:
let mql = window.matchMedia(mediaString);
mql这个对象包含若干属性和方法,例如:
- mql.matches
- 布尔值,表示当前是否匹配指定的媒体查询。
- mql.media
- 字符串,返回编译后的媒体查询字符串。
- mql.onchange
- 媒体查询改变时候的回调。
- mql.addListener(fn)
- fn参数是事件函数,媒体查询匹配状态变化时候执行。(此API已经不推荐使用)
- mql.removeListener(fn)
- fn参数是事件函数,移除绑定的事件。(此API已经不推荐使用)
在本例中,我们使用onchange
方法,代码如下所示,初始设备像素比的处理逻辑和上面的resize事件处理一致,大家注意力可以从let mqListener…这行语句开始:
let originPixelRatio = localStorage.devicePixelRatio; if (!originPixelRatio) { originPixelRatio = window.devicePixelRatio; if (Number.isInteger(originPixelRatio)) { localStorage.devicePixelRatio = originPixelRatio; } } // 设备相似比变化时候的处理函数 let mqListener = function () { let currentPixelRatio = window.devicePixelRatio; console.log('缩放比例是:' + currentPixelRatio / originPixelRatio); // 移除之前的查询检测 this.removeEventListener('change', mqListener); // 使用新的查询检测 matchMedia(`(resolution: ${currentPixelRatio}dppx)`).addEventListener('change', mqListener); }; // 添加媒体查询匹配变化事件 matchMedia(`(resolution: ${originPixelRatio}dppx)`).addEventListener('change', mqListener);
为何要不断移除事件?
matchMedia()
绑定的change事件,只会在查询状态改变时候触发,例如,原始的设备像素比(单位就是dppx)是1,则浏览器第一次放大和缩小的时候,change事件会执行,因为状态从匹配变成了不匹配,但是,如果继续放大和缩小,则change事件不再触发,因为状态一直是不匹配。
因此,逻辑实现的时候,需要试试更新媒体查询语句,绑定新的change事件,这样,任意的设备像素比变化都可以被检测到。
眼见为实,您可以狠狠地点击这里:matchMedia + dppx查询检测是否缩放demo
例如,在我的Windows 10 Firefox 85浏览器下的效果:
//zxx: 如果你看到这段文字,说明你现在访问是体验糟糕的垃圾盗版网站,你可以访问原文获得很好的体验:https://www.zhangxinxu.com/wordpress/?p=9832(作者张鑫旭)
三、outerWidth/innerWidth方法
浏览器的缩放比例还可以使用下面的公式:
let zoom = window.outerWidth / window.innerWidth;
此方法的JS代码比较简洁,如下示意:
window.addEventListener('resize', function () { console.log('当前页面缩放比例应该是:' + Math.round(1000 * (outerWidth / innerWidth)) / 10 + '%'); });
也就是把浏览器外部尺寸和窗体内部尺寸的比例作为缩放比例。
这个方法靠谱吗?
我特意整了个demo,您可以狠狠地点击这里:outerWidth/innerWidth与页面缩放demo
在我的Chrome浏览器下,缩放效果如下GIF所示:
再看看Safari浏览器,如下图所示:
卧槽,好像很牛逼的样子,居然都可以识别。
然而……致命的缺陷
首先是小缺陷,那就是Firefox浏览器此方法无效,页面缩放的时候,innerWidth尺寸似乎没有明显变化,例如下图是页面放大150%时候的效果,但是outerWidth / innerWidth
计算值还是接近与1
.
考虑到Firefox用户在国内的占比,此问题算是小缺陷,真正的致命的缺陷是下面这个:
当开发开发者工具,同时浏览器侧面定位的时候,缩放比例计算会有严重的偏差,因为此时innerWidth的尺寸严重缩水。
比方说下图所示的场景,Chrome浏览器下,页面并未缩放,但是开发者工具在侧边框打开,显示的缩放比例远远大于100%:
然后有些国产浏览器会有自己的侧边栏,里面放了些收藏夹或者其他乱七八糟东西,也会影响innerWidth
的尺寸。
因此outerWidth/innerWidth
方法虽然简单,但是却有使用风险。
活跃在移动端?
如果不需要考虑Firefox浏览器,以及浏览器绝不会出现侧边栏,则outerWidth/innerWidth
方法真是一个上上之选。
嘿,貌似移动端项目就符合这一点。
手机屏幕本来宽度就有限,是不可能多出侧边栏的,也不用考虑Firefox浏览器。
于是我就自己扫码测了下,结果是……resize事件没触发?
Android 微信、原生浏览器、Chrome和iOS中的微信浏览器都是如此,只有iOS Safari浏览器双指缩放页面时候可以触发计算。
算了算了,移动端应该没戏,移动端不要多想了,还是老老实实使用下面的visualViewport
API吧。
因此,此方法不建议单独使用,可以配合前面的matchMedia()
方法进行交叉验证,提高用户缩放行为判断的准确性。
四、visualViewport与手势缩放识别
实际上,页面的缩放比例,浏览器是提供了原生的API的,那就是window.visualViewport.scale
。
visualViewport
对象包括很多属性,例如宽高、偏移大小等,其中,我认为最稀缺的属性就是scale,可以返回当前页面因为双指缩放带来的缩放比例。
移动设备专享
visualViewport.scale
看起来给浏览器缩放识别带来了曙光,确实如此,但是,如果大家在PC端进行测试,会发现visualViewport.scale
永远发挥的是1
,无论页面通过 Ctrl+/Ctrl-组合键如何的缩小放大,都是1
。
这是bug吗?
不是的,因为返回的虚拟视区的pinch-zoom缩放比例,表示手势缩放。
因此,visualViewport.scale
只能在移动端使用,正好上面的几个方法均不适用于移动端。
整了个demo,您可以狠狠地点击这里:visualViewport与手势缩放比例demo
也可以扫码体验:
例如,在我的Android微信中,缩放页面就可以看到实时的比例变化,截屏效果如下:
相应的JS很简单:
window.visualViewport.addEventListener('resize', function () { result.innerHTML = '手势缩放比例:' + this.scale; });
兼容性
从实用角度讲,兼容性还是可以的,除了Firefox暂时不能使用外,其他现代浏览器均可以使用VisualViewport这个API,详见下图:
由于在国内移动端开发不考虑Firefox,以及visualViewport.scale
就是给移动端用的,因此,只要项目可以不支持iOS 12,此API都是可以用起来的。
五、最后总结一下
在桌面端,用户缩放浏览器页面的时候,devicePixelRatio设备像素比的值是会跟着变化的,因此,我们可以通过观察devicePixelRatio的变化判断用户是否缩放了浏览器。
如何观察呢?
可以使用resize事件,或者使用matchMedia()
方法返回的change事件进行观察,其中,后者针对性更强。
除此之外,如果不考虑Firefox浏览器以及认为用户不会以侧边栏的方式打开开发者工具,则还可以使用outerWidth/innerWidth
方法判断当前页面的缩放比例。
以上这些判断均指适用于桌面端浏览器,移动端页面的缩放判断可以使用visualViewport.scale
这个只读属性完成,目前iOS和Android操作系统均支持。
好,以上就是我知道的检测页面缩放的方法。
一个人所积累的知识毕竟有限,如果有其他更好的方法,希望不吝赐教!
如果文中有表述不准确的地方,也欢迎以评论形式进行指正。
感谢阅读,如果您觉得本文内容还挺不错,欢迎分享到朋友圈或者各类咨询群,感谢感谢!
本文为原创文章,欢迎分享,勿全文转载,如果实在喜欢,可收藏,永不过期,且会及时更新知识点及修正错误,阅读体验也更好。
本文地址:https://www.zhangxinxu.com/wordpress/?p=9832
(本篇完)
- 你不知道的CSS media查询与用户体验 (0.307)
- 小tip:CSS vw让overflow:auto页面滚动条出现时不跳动 (0.266)
- 检测DOM尺寸变化JS API ResizeObserver简介 (0.155)
- JS之我用单img元素实现了图像resize拉伸效果 (0.155)
- 设备像素比devicePixelRatio简单介绍 (0.106)
- 近期手机网页项目一些杂碎心得分享 (0.106)
- 视网膜New iPad与普通分辨率iPad页面的兼容处理 (0.106)
- CSS蛋疼应用之:数据上报和HTML验证 (0.106)
- HTML5 localStorage本地存储实际应用举例 (0.089)
- zSlide-基于CSS3/HTML5演示文档jQuery插件 (0.089)
- 小tips: zoom和transform:scale的区别 (RANDOM - 0.076)
不准,我说csdn每次打开都会提示我页面被缩放了。看来他们也用了相同的方法。我的笔记本上浏览器本没有缩放,但是打开demo显示缩放150%
前两种方法好像都不兼容mac双倍视网膜显示器。
outerWidth/innerWidth貌似是兼容Mac显示器的。
而且好像不支持检测windows的缩放
总结:
在PC端开局初始状态就是缩放过的并且开着devTool在侧边栏
则无法正确计算出状态
这个resizeObserver是不是也可以哦?
nice
以前QQ空间用一个flash来监测zoom
请问下,有办法直接操作浏览器缩放功能吗?比如chrome
检测页面缩放算不算某种反模式实践?感觉能兼容各种缩放比例是最好的。
某些网站的所谓”自适应rem布局”已经开始阻止缩放:无论缩放多大,显示都一样,因为所有元素的尺寸实质为视图宽度百分比。
回归IE6吧,只要把字体尺寸固定为px单位,就再也没有缩放一说了。
回归诺基亚,就再也没有手机续航不够用问题了
mac视网膜显示器的貌似缩放比例判断会有写问题吧
实测这个属性对于我的标称 Chrome55 WebView 套壳浏览器无用。
如果做Web应用的保守的开发估计还要再等一年(不过我这厮连MDN都不要了)
预测在我的Windows笔记本上visualViewport.scale有管用的希望,因为触摸板缩放和Ctrl+/-的相互独立(像移动端)
高DPI屏幕,上网必须开缩放,结果各大网站都弹框说我的页面缩放了,它们不支持你缩放(恶心人)
因此就算未来升级了我也会找个脚本把这个APIHook掉。