3

如何使用JS检测用户是否缩放了页面?

 3 years ago
source link: https://www.zhangxinxu.com/wordpress/2021/02/js-if-page-zoom/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

byzhangxinxu from https://www.zhangxinxu.com/wordpress/?p=9832

本文欢迎分享与聚合,全文转载就不必了,尊重版权,圈子就这么大,若急用可以联系授权。

i6J7n2q.jpg!mobile

介绍我自己知道的几个检测用户是否缩放了页面的方法,这些方法都不算完美,大家根据实际场景自行抉择该使用哪个方法。

一、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-改变页面的比例试试):

QVVZVrE.gif!mobile

可以准备识别用户进行了页面缩放,并准确显示了缩放比例。

但是,上面的实现并不严谨。

如果用户首次进入页面的时候,浏览器的视窗本身就是缩放状态,且此时的设备像素比是整数,则相关的判断就会出问题;虽然这种情况发生概率并不大,但是,理论上,就是会有这样的问题。

所以说,这个方法只能在一定程度上进行缩放比例的判断。

二、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浏览器下的效果:

j2qaqeu.gif!mobile

//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所示:

QVVZVrE.gif!mobile

再看看Safari浏览器,如下图所示:

Zjqqimn.png!mobile

卧槽,好像很牛逼的样子,居然都可以识别。

然而……致命的缺陷

首先是小缺陷,那就是Firefox浏览器此方法无效,页面缩放的时候,innerWidth尺寸似乎没有明显变化,例如下图是页面放大150%时候的效果,但是 outerWidth / innerWidth 计算值还是接近与 1 .

uI7neir.png!mobile

考虑到Firefox用户在国内的占比,此问题算是小缺陷,真正的致命的缺陷是下面这个:

当开发开发者工具,同时浏览器侧面定位的时候,缩放比例计算会有严重的偏差,因为此时innerWidth的尺寸严重缩水。

比方说下图所示的场景,Chrome浏览器下,页面并未缩放,但是开发者工具在侧边框打开,显示的缩放比例远远大于100%:

JNvMbeY.png!mobile

然后有些国产浏览器会有自己的侧边栏,里面放了些收藏夹或者其他乱七八糟东西,也会影响 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

也可以扫码体验:

22imEf.png!mobile

例如,在我的Android微信中,缩放页面就可以看到实时的比例变化,截屏效果如下:

URvmU3u.png!mobile

相应的JS很简单:

window.visualViewport.addEventListener('resize', function () {
    result.innerHTML = '手势缩放比例:' + this.scale;
});

兼容性

从实用角度讲,兼容性还是可以的,除了Firefox暂时不能使用外,其他现代浏览器均可以使用VisualViewport这个API,详见下图:

qq2IBv.png!mobile

由于在国内移动端开发不考虑Firefox,以及 visualViewport.scale 就是给移动端用的,因此,只要项目可以不支持iOS 12,此API都是可以用起来的。

五、最后总结一下

在桌面端,用户缩放浏览器页面的时候,devicePixelRatio设备像素比的值是会跟着变化的,因此,我们可以通过观察devicePixelRatio的变化判断用户是否缩放了浏览器。

如何观察呢?

可以使用resize事件,或者使用 matchMedia() 方法返回的change事件进行观察,其中,后者针对性更强。

除此之外,如果不考虑Firefox浏览器以及认为用户不会以侧边栏的方式打开开发者工具,则还可以使用 outerWidth/innerWidth 方法判断当前页面的缩放比例。

以上这些判断均指适用于桌面端浏览器,移动端页面的缩放判断可以使用 visualViewport.scale 这个只读属性完成,目前iOS和Android操作系统均支持。

好,以上就是我知道的检测页面缩放的方法。

一个人所积累的知识毕竟有限,如果有其他更好的方法,希望不吝赐教!

如果文中有表述不准确的地方,也欢迎以评论形式进行指正。

感谢阅读,如果您觉得本文内容还挺不错,欢迎分享到朋友圈或者各类咨询群,感谢感谢!

BnyAV3B.png!mobile

本文为原创文章,欢迎分享,勿全文转载,如果实在喜欢,可收藏,永不过期,且会及时更新知识点及修正错误,阅读体验也更好。

本文地址: https://www.zhangxinxu.com/wordpress/?p=9832

(本篇完)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK