需求描述:页面局部区域全屏显示功能
一、需求背景
在一些展示页面中,用户在浏览页面内容时,会遇到需要更专注查看某个局部区域的情况。用户希望将某个局部区域切换为全屏显示,以获得更沉浸式的体验。为了满足这一需求,我们计划在前端页面中实现局部区域的全屏切换功能。
二、需求目标
-
功能实现:允许用户通过操作(如点击按钮)将页面上的某一部分切换为全屏显示,并能够随时退出全屏模式。点击全屏区域内的“退出全屏”按钮,或者使用浏览器默认的快捷键(如 Esc 键)来退出全屏模式。退出全屏后,页面恢复到之前的显示状态,用户可以继续浏览其他内容。
-
兼容性:支持主流浏览器(包括但不限于 Chrome、Firefox、Safari、Edge)。
-
样式适配:全屏模式下,目标区域的样式应根据屏幕尺寸自动调整,确保内容展示清晰、美观。
三、技术实现
-
使用 Fullscreen API:
- 利用 HTML5 提供的 Fullscreen API 实现全屏切换功能。
- 通过 JavaScript 检测浏览器是否支持全屏功能,并调用
requestFullscreen()
方法将目标元素切换到全屏模式。 - 提供
exitFullscreen()
方法以便用户随时退出全屏模式。
-
基于 Fullscreen API 的第三方库
screenfull
:-
为了简化 Fullscreen API 的使用,一些第三方库如
screenfull
提供了封装后的接口,帮助开发者更方便地实现全屏功能。 -
screenfull 常用 API
-
screenfull.request(element)
:- 将指定元素(或整个页面)切换到全屏模式。
- 如果不传入
element
,则默认全屏整个页面。
-
screenfull.exit()
:- 退出全屏模式。
-
screenfull.toggle(element)
:- 切换全屏状态(进入或退出全屏)。
- 可以指定元素,也可以不传参数。
-
screenfull.isEnabled
:- 布尔值,表示当前浏览器是否支持全屏功能。
-
screenfull.isFullscreen
:- 布尔值,表示当前是否处于全屏状态。
-
事件监听:
监听全屏状态变化:
screenfull.on('change', () => { console.log(screenfull.isFullscreen ? '全屏中' : '已退出全屏'); });
-
四、遇到的问题
我们之前采用的是vue-fullscreen
这个第三方库。它是基于screenfull
来封装的一个Vue组件。调用方式是这样:
<!-- sticky视口 -->
<fullscreen
v-if="hasCheckPermission"
style="height: 100%; background: #f7f8fa; padding: 16px; overflow-y: hidden"
:fullscreen.sync="fullscreen"
>
<preview
v-if="showMainFlag"
ref="main-preview"
:component-data="mainCanvasComponentData"
:in-screen="!fullscreen"
:screen-shot="dataLoading"
:has-logo="hasLogo"
:auto-refresh="previewAutoRefresh"
:start-time="startTime"
:show-quick-filter="true"
/>
</fullscreen>
在使用 vue-fullscreen
组件实现局部 DOM 全屏展示时,我们遇到了一些问题,尤其是在全屏模式下,某些组件的浮层(如 Select
组件的 popover
弹窗)无法正常显示。这通常是因为 vue-fullscreen
通过 requestFullscreen
让特定元素进入全屏模式,而全屏模式下,浏览器会将该元素及其子元素限制在全屏上下文中,导致浮层等组件无法正确渲染到 body
之外的层级。
优化思路
为了规避这个问题,我们调整了实现方式,不再对特定的 DOM 元素执行 requestFullscreen
,而是直接让 body
进入全屏模式。这样,页面整体进入全屏,浮层组件依然可以正常渲染在 body
之上,避免层级限制的问题。
与此同时,我们通过手动给目标 DOM 元素添加 class="fullscreen"
,然后在 CSS 中定义样式,使其模拟局部全屏的效果。例如:
.fullscreen {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: white; /* 可根据需求调整 */
z-index: 9999; /* 确保在最顶层 */
}
技术背景
-
requestFullscreen
的作用与局限性- 该 API 允许指定的 DOM 元素进入全屏模式,但浏览器会严格限制它的作用域,导致
z-index
高于该元素的浮层无法正常显示。 - 例如,在
el-select
这类组件中,popover
是挂载在body
下的,而requestFullscreen
作用于局部 DOM 时,body
之外的内容无法显示,导致弹出层无法正常渲染。
- 该 API 允许指定的 DOM 元素进入全屏模式,但浏览器会严格限制它的作用域,导致
-
全屏模式下的层级管理问题
-
许多 UI 组件的浮层(如
Modal
、Tooltip
、Dropdown
等)通常使用portal
技术,把浮层挂载到body
之外的div
,而requestFullscreen
只会让选定元素及其子元素进入全屏,导致这些浮层看不到。
-
附关键代码:
<template>
<div>
<el-button @click="() => clickFullscreen()">进入全屏</el-button>
<preview
v-if="hasCheckPermission && showMainFlag"
ref="main-preview"
:class="{ screenfull: fullscreen }"
:style="{ background: backgroundColor }"
:component-data="mainCanvasComponentData"
:in-screen="!fullscreen"
:screen-shot="dataLoading"
:has-logo="hasLogo"
:auto-refresh="autoRefresh"
:start-time="startTime"
:show-quick-filter="true"
/>
</div>
</template>
<script>
import screenfull from 'screenfull';
exporr default {
data() {
return {
fullscreen: false
}
},
mounted() {
if (screenfull.isEnabled) {
screenfull.on('change', this.screenfullChange)
}
},
methods: {
clickFullscreen(origin = 'fullscreenBtn') {
if (!screenfull.isEnabled) {
return
}
const bodyNode = document.querySelector('body')
screenfull.request(bodyNode)
},
screenfullChange() {
this.fullscreen = screenfull.isFullscreen
}
}
}
</script>
<style lang="less" scoped>
.screenfull {
position: fixed !important;
z-index: 1002;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100% !important;
}
</style>
总结
这种优化方案既能确保浮层组件正常显示,又能达到局部全屏的视觉效果,同时避免了 requestFullscreen
带来的层级限制问题。这样,我们既保留了全屏体验,又提升了交互的兼容性,尤其适用于使用 Element UI
、Ant Design Vue
等组件库的场景。