可视化大屏的全屏功能,看起来是个小需求,但真正做过大屏项目的人都知道,这里面藏着一堆让人抓狂的细节。我最近刚交付了一个数据可视化大屏项目,全屏这块踩了不少坑,今天就把整个实现过程完整拆解一遍,从浏览器原生Fullscreen API的使用,到全屏后ECharts图表的自适应处理,再到各种浏览器兼容性和交互细节,全部讲透。
这篇文章适合正在做大屏项目的开发者,尤其是那些刚接触可视化大屏、对全屏功能实现还没有完整思路的朋友。如果你已经做过几个大屏项目但全屏这块总是出bug,这篇文章也能帮你补齐盲区。我会从最基础的原生API讲起,逐步深入到图表自适应、多组件联动、异常处理等实战细节,每个环节都附上可直接复用的代码。
1. 为什么大屏项目不能只靠CSS做全屏
很多人第一反应是:全屏不就是把容器宽高设成100vw和100vh吗?这个思路在普通页面上勉强能用,但在可视化大屏场景下问题很多。
1.1 CSS伪全屏的三个致命缺陷
第一个缺陷是浏览器UI无法隐藏。你用CSS把容器撑满视口,但浏览器的地址栏、标签栏、书签栏依然占着屏幕空间。大屏项目通常是要投到会议室大屏或者LED拼接屏上展示的,这些浏览器UI会直接破坏沉浸感。尤其是演示场景下,领导看到地址栏露出来,体验直接打折扣。
第二个缺陷是F11和CSS全屏不等价。用户按F11进入的是浏览器级别的全屏,这时候你的页面确实占满了整个屏幕,但问题是:你无法通过JavaScript监听F11的按下事件,也无法在F11全屏状态下控制页面行为。更麻烦的是,F11全屏和代码触发的全屏是两套独立的状态,用户按了F11之后,你再调用代码全屏,可能会出现状态混乱。
第三个缺陷是ESC键行为不可控。CSS伪全屏下,用户按ESC没有任何反应,因为压根没进入真正的全屏状态。但如果你用了Fullscreen API,ESC会退出全屏,这时候你需要监听全屏变化事件来同步UI状态。如果只靠CSS,你连这个事件都拿不到。
// 错误示范:纯CSS伪全屏 // 这种方式无法隐藏浏览器UI,也无法监听ESC退出 .dashboard-container { width: 100vw; height: 100vh; overflow: hidden; }1.2 Fullscreen API的核心优势
Fullscreen API是浏览器原生提供的全屏接口,它能让指定元素真正占据整个屏幕,隐藏所有浏览器UI。核心方法就几个:
element.requestFullscreen():让某个元素进入全屏document.exitFullscreen():退出全屏document.fullscreenElement:获取当前全屏的元素document.fullscreenEnabled:判断当前环境是否支持全屏
这几个API看起来简单,但实际用起来有不少门道。比如requestFullscreen()返回的是Promise,在有些浏览器里如果不处理rejection,会静默失败。再比如fullscreenElement在跨浏览器时前缀不一致,需要做兼容处理。
注意:Fullscreen API必须由用户交互事件(如click、keydown)触发,不能在页面加载时自动调用,否则浏览器会拦截并报错。这是浏览器的安全策略,没有绕过的方法。
1.3 全屏方案选型对比
| 方案 | 能否隐藏浏览器UI | 能否监听ESC | 兼容性 | 适用场景 |
|---|---|---|---|---|
| CSS伪全屏 | 否 | 否 | 全部 | 普通页面布局 |
| Fullscreen API | 是 | 是 | IE11+ | 大屏演示 |
| F11浏览器全屏 | 是 | 否 | 全部 | 用户手动操作 |
| Electron全屏 | 是 | 是 | 仅桌面端 | 桌面应用 |
从表格可以清楚看出,如果是Web端的大屏项目,Fullscreen API是唯一能同时满足隐藏UI和监听状态变化的方案。如果是Electron打包的桌面应用,可以直接用Electron的setFullScreen方法,更稳定。
2. Fullscreen API的兼容性处理与封装
原生API在不同浏览器下的表现差异不小,直接裸用很容易出问题。我建议做一层封装,把兼容性细节都吃掉。
2.1 浏览器前缀的坑
虽然现在主流浏览器基本都支持标准API了,但如果你需要兼容一些老版本浏览器,前缀还是得处理。常见的后缀有:
- 标准:
requestFullscreen、exitFullscreen、fullscreenElement - WebKit:
webkitRequestFullscreen、webkitExitFullscreen、webkitFullscreenElement - Firefox:
mozRequestFullScreen、mozCancelFullScreen、mozFullScreenElement - IE11:
msRequestFullscreen、msExitFullscreen、msFullscreenElement
注意Firefox的大小写有点特殊,是mozRequestFullScreen,Screen的S是大写。这个细节很容易写错,写错了在Firefox下就静默失败。
// 全屏API兼容性封装 const FullscreenHelper = { // 获取当前全屏元素 getFullscreenElement() { return document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement || null; }, // 请求全屏 requestFullscreen(element) { const el = element || document.documentElement; const requestMethod = el.requestFullscreen || el.webkitRequestFullscreen || el.mozRequestFullScreen || el.msRequestFullscreen; if (requestMethod) { // 注意:requestFullscreen返回Promise,需要catch const result = requestMethod.call(el); if (result && result.catch) { result.catch((err) => { console.warn('全屏请求失败:', err.message); }); } return true; } console.warn('当前浏览器不支持全屏API'); return false; }, // 退出全屏 exitFullscreen() { const exitMethod = document.exitFullscreen || document.webkitExitFullscreen || document.mozCancelFullScreen || document.msExitFullscreen; if (exitMethod) { const result = exitMethod.call(document); if (result && result.catch) { result.catch((err) => { console.warn('退出全屏失败:', err.message); }); } } }, // 监听全屏变化 onFullscreenChange(callback) { const events = [ 'fullscreenchange', 'webkitfullscreenchange', 'mozfullscreenchange', 'MSFullscreenChange' ]; events.forEach(eventName => { document.addEventListener(eventName, callback); }); }, // 判断是否支持全屏 isSupported() { return !!(document.fullscreenEnabled || document.webkitFullscreenEnabled || document.mozFullScreenEnabled || document.msFullscreenEnabled); } };2.2 requestFullscreen的Promise陷阱
标准API的requestFullscreen()返回的是一个Promise,但很多老版本浏览器返回的是undefined。如果你直接.catch(),在老浏览器上会报"Cannot read property 'catch' of undefined"。所以上面代码里我先判断了result && result.catch,这样才稳妥。
另外,Promise的rejection一定要处理。常见的rejection原因有:
- 不是用户交互触发(比如在setTimeout里调用)
- 元素被隐藏或不可见
- 浏览器策略限制(比如iframe没有allowfullscreen属性)
- 用户拒绝了全屏请求
如果不catch,这些错误会静默吞掉,你调试的时候会一脸懵。
2.3 全屏状态同步的完整逻辑
全屏状态变化时,你需要同步更新UI,比如切换按钮图标、调整布局、触发图表resize。这里的关键是监听fullscreenchange事件,而不是在调用requestFullscreen之后直接改状态。因为用户可能按ESC退出,也可能按F11切换,这些操作都会触发fullscreenchange。
// Vue3组合式API中的全屏状态管理 import { ref, onMounted, onUnmounted } from 'vue'; export function useFullscreen(targetRef) { const isFullscreen = ref(false); const handleFullscreenChange = () => { isFullscreen.value = !!FullscreenHelper.getFullscreenElement(); // 全屏状态变化后,延迟触发图表resize // 延迟是因为浏览器全屏动画需要时间,立即resize尺寸可能不准 setTimeout(() => { window.dispatchEvent(new Event('resize')); }, 300); }; const toggleFullscreen = () => { if (isFullscreen.value) { FullscreenHelper.exitFullscreen(); } else { const el = targetRef.value || document.documentElement; FullscreenHelper.requestFullscreen(el); } }; onMounted(() => { FullscreenHelper.onFullscreenChange(handleFullscreenChange); }); onUnmounted(() => { // 组件销毁时移除监听,防止内存泄漏 const events = [ 'fullscreenchange', 'webkitfullscreenchange', 'mozfullscreenchange', 'MSFullscreenChange' ]; events.forEach(eventName => { document.removeEventListener(eventName, handleFullscreenChange); }); }); return { isFullscreen, toggleFullscreen }; }这段代码里有个细节值得说:全屏变化后我延迟了300ms才触发resize。原因是浏览器进入全屏有一个动画过程,在动画过程中元素的尺寸是渐变的,如果你立即resize,图表拿到的尺寸可能还是旧尺寸。延迟300ms后,全屏动画基本完成,这时候resize才准确。这个300ms不是随便定的,是我在多个浏览器上实测下来的经验值,Chrome大概200ms,Firefox大概250ms,留300ms比较保险。
3. 全屏后图表自适应的核心机制
全屏功能本身不难,难的是全屏之后图表要跟着自适应。ECharts、Chart.js这些图表库默认不会自动监听容器尺寸变化,需要你手动调用resize方法。
3.1 ECharts的resize机制拆解
ECharts的resize方法有两种调用方式:
// 方式一:不传参数,图表自动获取容器尺寸 chart.resize(); // 方式二:传入具体宽高 chart.resize({ width: 800, height: 600 });大多数场景用方式一就行。但方式一有个前提:图表的容器必须有明确的宽高,而且宽高是通过CSS计算出来的。如果容器宽高是0,resize会失效。
全屏场景下,容器宽高会从原来的尺寸变成屏幕尺寸,这个变化ECharts是感知不到的,必须你手动调用resize。问题是什么时候调用?如果只监听fullscreenchange事件,在事件回调里立即调用resize,可能会拿到错误的尺寸。所以我在上面代码里用了setTimeout延迟300ms。
但延迟300ms也有问题:如果用户快速切换全屏,可能会触发多次resize,造成性能浪费。更好的做法是用防抖:
// 防抖处理resize function debounce(fn, delay) { let timer = null; return function(...args) { if (timer) clearTimeout(timer); timer = setTimeout(() => { fn.apply(this, args); }, delay); }; } const handleResize = debounce(() => { // 遍历所有图表实例,逐个resize chartInstances.forEach(chart => { if (chart && !chart.isDisposed()) { chart.resize(); } }); }, 300); window.addEventListener('resize', handleResize); FullscreenHelper.onFullscreenChange(handleResize);这里有个关键点:chart.isDisposed()判断。如果图表已经被销毁了,再调用resize会报错。在Vue或React组件里,组件销毁时图表也会销毁,但resize事件可能还在触发,所以必须加这个判断。
3.2 多图表实例的统一管理
大屏项目通常有多个图表,如果每个图表都单独监听resize,代码会很乱。我建议用一个统一的图表管理器:
// 图表管理器 class ChartManager { constructor() { this.charts = new Map(); this.resizeHandler = debounce(this.resizeAll.bind(this), 300); this.initListeners(); } // 注册图表 register(id, chartInstance) { this.charts.set(id, chartInstance); } // 注销图表 unregister(id) { const chart = this.charts.get(id); if (chart && !chart.isDisposed()) { chart.dispose(); } this.charts.delete(id); } // 全部resize resizeAll() { this.charts.forEach((chart, id) => { if (chart && !chart.isDisposed()) { try { chart.resize(); } catch (err) { console.warn(`图表${id} resize失败:`, err.message); } } }); } // 初始化监听 initListeners() { window.addEventListener('resize', this.resizeHandler); FullscreenHelper.onFullscreenChange(this.resizeHandler); } // 销毁 destroy() { window.removeEventListener('resize', this.resizeHandler); this.charts.forEach(chart => { if (chart && !chart.isDisposed()) { chart.dispose(); } }); this.charts.clear(); } } // 全局单例 export const chartManager = new ChartManager();用管理器的方式,每个图表组件只需要在mounted时register,在unmounted时unregister,resize逻辑完全解耦。这样即使有几十个图表,代码也不会乱。
3.3 全屏切换时的图表动画衔接
全屏切换时,图表如果直接resize,会有一种"跳变"的感觉,视觉上不够流畅。我试过几种优化方案:
方案一是在resize时加过渡动画。ECharts的resize不支持动画参数,但你可以用CSS给容器加transition:
.chart-container { transition: width 0.3s ease, height 0.3s ease; }但这种方式有个问题:ECharts的canvas尺寸是JS控制的,CSS transition只对容器生效,canvas本身不会平滑过渡,所以效果有限。
方案二是用ECharts的setOption重新渲染。全屏后重新setOption,ECharts会有一个默认的过渡动画。但这种方式性能开销大,图表多了会卡。
方案三是我最终采用的:全屏切换时先隐藏图表,等resize完成后再显示。这样用户看到的是"隐藏-显示"的过程,而不是"跳变"。
// 全屏切换时的图表处理 async function handleFullscreenToggle() { // 1. 隐藏所有图表容器 document.querySelectorAll('.chart-container').forEach(el => { el.style.opacity = '0'; }); // 2. 切换全屏 if (isFullscreen.value) { FullscreenHelper.exitFullscreen(); } else { FullscreenHelper.requestFullscreen(targetRef.value); } // 3. 等待全屏动画完成 await new Promise(resolve => setTimeout(resolve, 350)); // 4. resize所有图表 chartManager.resizeAll(); // 5. 显示图表 document.querySelectorAll('.chart-container').forEach(el => { el.style.opacity = '1'; }); }这个方案实测下来视觉最流畅,用户几乎感觉不到图表在切换过程中的变化。
4. 大屏全屏功能的完整交互设计
全屏不是孤立的按钮,它和整个大屏的交互体系是联动的。这部分我讲讲实际项目中的交互设计经验。
4.1 全屏按钮的状态同步
全屏按钮需要根据当前状态切换图标和文案。但状态同步有个坑:用户可能通过多种方式进入/退出全屏,比如点击按钮、按ESC、按F11。你必须保证所有方式都能正确同步状态。
// 全屏按钮组件 const FullscreenButton = { template: ` <button class="fullscreen-btn" @click="toggle" :title="isFullscreen ? '退出全屏' : '进入全屏'"> <i :class="isFullscreen ? 'icon-exit-fullscreen' : 'icon-fullscreen'"></i> </button> `, setup() { const targetRef = ref(null); const { isFullscreen, toggleFullscreen } = useFullscreen(targetRef); return { isFullscreen, toggle: toggleFullscreen }; } };这里的关键是useFullscreen里监听了fullscreenchange事件,所以不管用户怎么切换全屏,isFullscreen都会正确更新。如果你只在按钮点击时改状态,用户按ESC退出后按钮状态就不对了。
4.2 ESC键退出的拦截与处理
默认情况下,用户按ESC会退出全屏。但有些大屏项目希望拦截ESC,比如在ESC退出前弹个确认框,或者ESC只退出某个弹窗而不退出全屏。
拦截ESC有个前提:你必须先进入全屏,才能拦截。因为ESC的keydown事件在非全屏状态下是浏览器级别的,页面拿不到。
// 拦截ESC退出全屏 document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && isFullscreen.value) { // 如果当前有弹窗打开,只关闭弹窗 if (hasOpenModal()) { closeTopModal(); e.preventDefault(); return; } // 否则正常退出全屏,不做拦截 } });注意:e.preventDefault()在全屏状态下对ESC的拦截效果因浏览器而异。Chrome下可以拦截,Firefox下拦截不了。所以如果你的项目需要严格拦截ESC,建议用全屏API的exitFullscreen配合自定义快捷键,而不是依赖ESC。
4.3 全屏状态下的鼠标隐藏
大屏演示时,鼠标指针如果一直显示,会影响视觉效果。可以在全屏状态下自动隐藏鼠标,用户移动鼠标时再显示。
// 全屏状态下自动隐藏鼠标 let mouseTimer = null; function setupMouseHide() { document.addEventListener('mousemove', () => { if (!isFullscreen.value) return; document.body.style.cursor = 'default'; if (mouseTimer) clearTimeout(mouseTimer); mouseTimer = setTimeout(() => { document.body.style.cursor = 'none'; }, 3000); }); }这个功能看起来小,但实际演示时体验提升很明显。3秒是我测试下来比较合适的值,太短了用户会觉得鼠标老是消失,太长了又起不到隐藏效果。
4.4 全屏与页面缩放的冲突处理
大屏项目通常会用rem或scale做整体缩放适配。全屏后,如果缩放逻辑没处理好,会出现图表模糊或者布局错位。
我的做法是:全屏时重新计算缩放比例。假设设计稿是1920x1080,全屏后屏幕是2560x1440,缩放比例应该是min(2560/1920, 1440/1080) = 1.333。
// 全屏时重新计算缩放 function recalcScale() { const designWidth = 1920; const designHeight = 1080; const screenWidth = window.innerWidth; const screenHeight = window.innerHeight; const scaleX = screenWidth / designWidth; const scaleY = screenHeight / designHeight; const scale = Math.min(scaleX, scaleY); const container = document.querySelector('.dashboard-container'); container.style.transform = `scale(${scale})`; container.style.transformOrigin = 'left top'; // 居中处理 const offsetX = (screenWidth - designWidth * scale) / 2; const offsetY = (screenHeight - designHeight * scale) / 2; container.style.left = `${offsetX}px`; container.style.top = `${offsetY}px`; }这里用Math.min而不是Math.max,是为了保证内容完整显示,不会被裁切。如果屏幕比例和设计稿比例不一致,会出现黑边,这是正常的,总比内容被裁掉好。
5. 踩坑实录:那些让我加班到凌晨的全屏问题
这部分我讲讲实际项目中遇到的具体问题,都是真实踩过的坑,希望能帮你少走弯路。
5.1 iframe嵌套下的全屏失效
大屏项目有时候会嵌入iframe,比如把某个子页面嵌到主页面里。这时候在iframe内部调用requestFullscreen会失败,报错"Fullscreen request denied"。
原因是iframe默认没有全屏权限,需要在iframe标签上加allowfullscreen属性:
<iframe src="child.html" allowfullscreen></iframe>如果是跨域的iframe,还需要在响应头里加Permissions-Policy: fullscreen=(self "https://父页面域名")。这个配置很容易漏,漏了之后全屏就是静默失败,控制台可能都不报错。
5.2 全屏后ECharts图表模糊
这个问题困扰了我很久。全屏后图表变模糊,原因是ECharts的canvas分辨率没有跟着设备像素比(devicePixelRatio)更新。
ECharts默认会用window.devicePixelRatio作为canvas的像素比。但在全屏切换时,如果devicePixelRatio变了(比如从普通屏幕切到Retina屏),ECharts不会自动更新。
解决方案是在resize时手动传入devicePixelRatio:
chart.resize({ width: 'auto', height: 'auto', // 强制使用当前设备像素比 devicePixelRatio: window.devicePixelRatio });但注意,ECharts的resize方法其实不支持devicePixelRatio参数。正确的做法是在初始化时设置,或者在resize后重新setOption:
// 方案:销毁重建 function rebuildChart(chartInstance, container, option) { chartInstance.dispose(); const newChart = echarts.init(container, null, { devicePixelRatio: window.devicePixelRatio }); newChart.setOption(option); return newChart; }销毁重建的性能开销大,但能彻底解决模糊问题。如果图表不多,这个方案可以接受。如果图表很多,建议只在检测到devicePixelRatio变化时才重建。
5.3 全屏切换时的白屏闪烁
全屏切换时,页面会有一个短暂的白屏。原因是浏览器在全屏动画过程中会重新绘制页面,如果页面背景是白色的,就会闪白。
解决方案很简单:把body和html的背景色设成深色,和大屏主题一致。
html, body { background-color: #0a0e27; margin: 0; padding: 0; overflow: hidden; }这个细节很小,但不处理的话,演示时白屏闪烁很影响观感。
5.4 移动端全屏的特殊处理
移动端浏览器的全屏API和桌面端不一样。iOS Safari不支持Fullscreen API,Android Chrome支持但行为有差异。
iOS上的替代方案是用webkitEnterFullscreen,但只对video元素有效。对于普通div,iOS没有真正的全屏方案,只能用CSS伪全屏模拟。
// 移动端全屏兼容 function mobileFullscreen(element) { // iOS不支持标准全屏API if (/iPhone|iPad|iPod/.test(navigator.userAgent)) { // 用CSS模拟 element.style.position = 'fixed'; element.style.top = '0'; element.style.left = '0'; element.style.width = '100vw'; element.style.height = '100vh'; element.style.zIndex = '9999'; // 隐藏地址栏 window.scrollTo(0, 1); return; } // Android用标准API FullscreenHelper.requestFullscreen(element); }移动端大屏项目相对少,但如果你的项目需要兼容移动端,这块必须处理。
5.5 全屏状态下的键盘快捷键冲突
大屏项目通常有一些快捷键,比如数字键切换页面、方向键翻页。全屏状态下,这些快捷键可能和浏览器快捷键冲突。
比如你按F5刷新,全屏状态下F5依然有效,会刷新页面并退出全屏。如果你不希望用户刷新,可以拦截:
document.addEventListener('keydown', (e) => { // 拦截F5刷新 if (e.key === 'F5' || (e.ctrlKey && e.key === 'r')) { e.preventDefault(); return false; } // 拦截F11 if (e.key === 'F11') { e.preventDefault(); // 用自定义全屏替代 toggleFullscreen(); return false; } });但注意,拦截浏览器快捷键是有风险的,用户可能会觉得"我的浏览器怎么不受控制了"。建议只在演示模式下拦截,正常浏览时不要拦截。
6. 全屏功能的性能优化与异常兜底
全屏功能虽然不复杂,但在大屏项目里,性能问题和异常情况必须考虑周全。
6.1 resize的性能优化
大屏项目图表多,每次resize都遍历所有图表,如果图表有几十个,会有明显的卡顿。优化思路有三个:
第一个是防抖,这个前面已经讲了,300ms的防抖能过滤掉大部分无效resize。
第二个是懒resize,只resize当前可见的图表。如果大屏有多个页面,只有当前页的图表需要resize,其他页的可以等切换过去再resize。
// 懒resize:只处理可见图表 resizeVisible() { this.charts.forEach((chart, id) => { const container = chart.getDom(); // 判断容器是否可见 if (container.offsetParent !== null && !chart.isDisposed()) { chart.resize(); } }); }第三个是用requestAnimationFrame替代setTimeout,让resize在下一帧渲染前执行,避免布局抖动。
// 用requestAnimationFrame优化 let rafId = null; function scheduleResize() { if (rafId) cancelAnimationFrame(rafId); rafId = requestAnimationFrame(() => { chartManager.resizeAll(); }); }6.2 全屏API失败的兜底方案
Fullscreen API不是100%可靠的,可能因为各种原因失败。你需要有兜底方案:
// 全屏失败兜底 function safeFullscreen(element) { const success = FullscreenHelper.requestFullscreen(element); if (!success) { // API不支持,用CSS伪全屏兜底 fallbackFullscreen(element); return; } // 监听全屏是否真的成功了 setTimeout(() => { if (!FullscreenHelper.getFullscreenElement()) { // 3秒后还没进入全屏,说明失败了 console.warn('全屏未生效,启用兜底方案'); fallbackFullscreen(element); } }, 3000); } function fallbackFullscreen(element) { element.classList.add('pseudo-fullscreen'); // 同时隐藏浏览器UI(能隐藏多少算多少) window.scrollTo(0, 1); }兜底方案虽然不如原生全屏完美,但至少能保证功能可用,不会让用户点了按钮没反应。
6.3 内存泄漏的排查与预防
全屏功能涉及事件监听,如果组件销毁时没清理干净,会造成内存泄漏。常见的泄漏点有:
- fullscreenchange事件监听没移除
- resize事件监听没移除
- 图表实例没dispose
- setTimeout没clear
我在项目里加了一个检查机制,在组件销毁时打印未清理的监听器数量:
// 开发环境下的泄漏检查 if (process.env.NODE_ENV === 'development') { const originalAdd = document.addEventListener; const originalRemove = document.removeEventListener; let listenerCount = 0; document.addEventListener = function(...args) { listenerCount++; return originalAdd.apply(this, args); }; document.removeEventListener = function(...args) { listenerCount--; return originalRemove.apply(this, args); }; window.__checkLeak = () => { console.log(`当前监听器数量: ${listenerCount}`); if (listenerCount > 10) { console.warn('可能存在内存泄漏,请检查事件监听是否清理'); } }; }这个检查机制在开发阶段很有用,能帮你提前发现泄漏问题。
6.4 全屏状态持久化
有些项目希望用户刷新页面后还能保持全屏状态。但浏览器的安全策略不允许页面加载时自动进入全屏,必须由用户交互触发。
折中方案是用localStorage记录全屏状态,页面加载后显示一个"点击恢复全屏"的提示:
// 记录全屏状态 FullscreenHelper.onFullscreenChange(() => { const isFull = !!FullscreenHelper.getFullscreenElement(); localStorage.setItem('dashboard_fullscreen', isFull ? '1' : '0'); }); // 页面加载时检查 window.addEventListener('load', () => { if (localStorage.getItem('dashboard_fullscreen') === '1') { // 显示恢复全屏的提示 showRestoreFullscreenTip(); } });这个方案虽然不是真正的自动恢复,但用户体验上比完全丢失状态好。
7. 写在最后的一些实战心得
全屏功能做了这么多次,我最大的体会是:不要小看任何一个"简单"的功能。Fullscreen API看起来就几个方法,但真正要做好,需要考虑兼容性、状态同步、图表自适应、性能优化、异常兜底等一堆细节。
如果你正在做大屏项目,我的建议是:全屏功能不要等到最后再做,最好在项目初期就把框架搭好。因为全屏涉及到布局、图表、交互等多个模块,后期再加会很痛苦。
另外,测试的时候一定要在真实的大屏设备上测,不要只在开发机的浏览器里测。大屏的分辨率、设备像素比、浏览器版本都可能和开发机不一样,很多问题只有在大屏上才会暴露。
最后分享一个我常用的调试技巧:在全屏状态下打开控制台,用document.fullscreenElement查看当前全屏元素,用window.innerWidth和window.innerHeight查看实际尺寸,用window.devicePixelRatio查看像素比。这三个值能帮你快速定位大部分全屏相关的问题。