浏览器全屏与否展示不同效果

2021/7/14 js全屏

最近碰到一个需求,希望整个页面在全屏和非全屏的情况下表现形式有所不同。由于不需要过多考虑兼容性问题,还以为通过 Fullscreen_API 就能进行判断处理了,事实证明还是天真了。

# 全屏基本操作

通过 document.fullscreenEnabled 返回的一个布尔值,表明浏览器是否支持全屏模式。 全屏模式只在那些不包含窗口化的插件的页面中可用,对于一个 <iframe> 元素中的页面,则它必需拥有 mozallowfullscreen 属性。

Element.requestFullscreen() 方法用于发出异步请求使元素进入全屏模式。调用这个 API 并不能保证元素一定能够进入全屏模式。如果元素被允许进入全屏幕模式,返回的 Promise 会 resolve,并且该元素会收到一个 fullscreenchange 事件。如果全屏请求被拒绝,返回的 promise 会变成 rejected 并且该元素会收到一个 fullscreenerror 事件。

Document.exitFullscreen() 方法用于让当前文档退出全屏模式。

document.fullscreenElement 返回当前文档中正在以全屏模式显示的 Element 节点,如果没有使用全屏模式,则返回 null。

// 切换全屏的例子
function toggleFullscreen() {
  let elem = document.querySelector("video");

  if (!document.fullscreenElement) {
    elem.requestFullscreen().catch(err => {
      alert(`Error attempting to enable full-screen mode: ${err.message} (${err.name})`);
    });
  } else {
    document.exitFullscreen();
  }
}
1
2
3
4
5
6
7
8
9
10
11
12

# 判断是否全屏

看完上面是不是觉得判断一下 fullscreenElement 是不是 null 不就知道是否全屏了?如果是手动通过上述操作调用 API 进入全屏的确是如此,但是如果是通过浏览器自带的快捷键 F11 进入的呢?这个时候 fullscreenElement 肯定还是 null,因为全屏的是整个页面窗口而不是某个 Element。

如果是 F11 倒也不难搞,直接把 F11 的默认事件干掉,手动调用全屏 API 进行全屏就好。

document.body.onkeydown = (event) => {
  if (event.code === "F11") {
    // 屏蔽默认的全屏快捷键
    event.preventDefault?.();
    // 手动调用全屏
    toggleFullscreen();
  }
};
1
2
3
4
5
6
7
8

在 Windows 下试了一下基本上解决了判断全屏的问题,不过新的问题在于如果是 Mac 全屏快捷键是不一样的,还有一种更坑的情况是如果点击 Chrome 浏览器右侧全屏按钮的话连该拦截什么的都不知道。

Chrome 全屏按钮.png

找了半天最后在 stackoverflow 上发现一个思路,监听页面的 resize 事件然后通过宽高判断,如果页面的宽高等于屏幕的宽高那么就认为是全屏状态了。

// 输入window.screen返回的部分属性
{
    availHeight: 1400
    availLeft: 0
    availTop: 0
    availWidth: 2560
    height: 1440
    width: 2560
}
1
2
3
4
5
6
7
8
9

默认情况下页面的 innerHeight 是会比 screen.height 要小的,大概等于 screen.availHeight,原因在于浏览器在非全屏状态下会显示工具栏和 Tab 标签,全屏状态下一般没有(ps:Mac 表示不服,下述方法的确不适用 Mac 显示工具栏的情况的全屏)。

// https://stackoverflow.com/questions/34422052/how-to-detect-browser-has-gone-to-full-screen
// 不能加上devicePixelRatio,系统缩放和2倍屏下有问题
// 这边舍弃了浏览器缩放的情况,如果要兼顾缩就得考虑是2倍屏呢还是系统有缩放呢
export const isSystemFullscreen = () => {
  const width = window.innerWidth;
  const height = window.innerHeight;
  // 这边是判断全等,建议实际使用过程中留下1px左右作为精度差
  return window.screen.width === width && window.screen.height === height;
};

// 如果是手动全屏就好判断了,直接看有没有fullscreenElement
export const isElementFullscreen = () => {
  if (document.fullscreenElement || document.webkitFullscreenElement) {
    return true;
  }
  return false;
};

// 监听页面缩放,是手动的可以排除一下
window.addEventListener("resize", () => {
  if (!isElementFullscreen()) {
    // 调用isSystemFullscreen判断是不是全屏
  }
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 后记

新的全屏 API 使用起来还是比较方便的,各种接口也比较全,但是浏览器自身的全屏没有知道这点就比较坑了。

还有一个 :fullscree 伪类挺有意思的,如果是通过 API 手动进入的全屏还可以在全屏元素上使用 css 伪类 :fullscree

#fs-toggle:fullscreen {
  background-color: #faa;
}
1
2
3

# 参考