前端组件做全屏展示的踩坑分析

149 阅读4分钟

需求描述:页面局部区域全屏显示功能

一、需求背景

在一些展示页面中,用户在浏览页面内容时,会遇到需要更专注查看某个局部区域的情况。用户希望将某个局部区域切换为全屏显示,以获得更沉浸式的体验。为了满足这一需求,我们计划在前端页面中实现局部区域的全屏切换功能。

二、需求目标

  1. 功能实现:允许用户通过操作(如点击按钮)将页面上的某一部分切换为全屏显示,并能够随时退出全屏模式。点击全屏区域内的“退出全屏”按钮,或者使用浏览器默认的快捷键(如 Esc 键)来退出全屏模式。退出全屏后,页面恢复到之前的显示状态,用户可以继续浏览其他内容。

  2. 兼容性:支持主流浏览器(包括但不限于 Chrome、Firefox、Safari、Edge)。

  3. 样式适配:全屏模式下,目标区域的样式应根据屏幕尺寸自动调整,确保内容展示清晰、美观。

三、技术实现

  1. 使用 Fullscreen API

    1. 利用 HTML5 提供的 Fullscreen API 实现全屏切换功能。
    2. 通过 JavaScript 检测浏览器是否支持全屏功能,并调用 requestFullscreen() 方法将目标元素切换到全屏模式。
    3. 提供 exitFullscreen() 方法以便用户随时退出全屏模式。
  2. 基于 Fullscreen API 的第三方库 screenfull

    1. 为了简化 Fullscreen API 的使用,一些第三方库如 screenfull 提供了封装后的接口,帮助开发者更方便地实现全屏功能。

    2. 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; /* 确保在最顶层 */
}

技术背景

  1. requestFullscreen 的作用与局限性

    • 该 API 允许指定的 DOM 元素进入全屏模式,但浏览器会严格限制它的作用域,导致 z-index 高于该元素的浮层无法正常显示。
    • 例如,在 el-select 这类组件中,popover 是挂载在 body 下的,而 requestFullscreen 作用于局部 DOM 时,body 之外的内容无法显示,导致弹出层无法正常渲染。
  2. 全屏模式下的层级管理问题

    • 许多 UI 组件的浮层(如 ModalTooltipDropdown 等)通常使用 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 UIAnt Design Vue 等组件库的场景。