1. 项目概述为什么全屏API值得你花时间研究如果你正在用Cocos Creator开发游戏或者互动应用那么“全屏”这个功能大概率是你绕不开的一个坎。它看起来简单——不就是让画面铺满整个屏幕吗但实际做起来你会发现这里面的坑一个接一个为什么在手机上点了按钮没反应为什么在PC浏览器里全屏后UI错位了为什么退出全屏后游戏状态乱了这些“为什么”背后是不同浏览器厂商五花八门的实现标准、移动端和桌面端迥异的交互逻辑以及引擎本身对原生API的封装策略。我接手过不少从其他引擎迁移过来或者从零开始的项目几乎每一个涉及到全屏需求时都会经历一番折腾。网上能找到的代码片段往往只解决了“能不能用”的问题但距离“好用、稳定、不出错”还差得远。这份指南就是把我这些年踩过的坑、总结的最佳实践以及那些官方文档里没明说但至关重要的细节系统地梳理给你。无论你是想实现一个沉浸式的关卡体验、一个全屏播放的视频广告还是一个简单的“点击全屏”按钮这里的内容都能让你少走弯路。2. 全屏API的核心原理与浏览器差异全屏功能本质上并不是Cocos引擎“创造”的而是对浏览器原生Fullscreen API或称Fullscreen API的一层封装。理解这层封装是解决一切奇怪问题的起点。2.1 原生Fullscreen API的“分裂”现状你可能听说过requestFullscreen这个方法但实际写代码时会发现它前面常常需要加上webkit、moz或ms这样的前缀。这是因为全屏API在成为正式标准前各大浏览器都有自己的实现。虽然现在主流浏览器都支持标准写法但为了极致的兼容性尤其是考虑一些老旧的内嵌浏览器环境我们通常还是需要做能力检测。// 一个经典的全屏请求函数考虑了兼容性 function requestFullscreen(element) { if (element.requestFullscreen) { element.requestFullscreen(); } else if (element.webkitRequestFullscreen) { // Chrome, Safari, Edge element.webkitRequestFullscreen(); } else if (element.mozRequestFullScreen) { // Firefox element.mozRequestFullScreen(); } else if (element.msRequestFullscreen) { // IE/Edge Legacy element.msRequestFullscreen(); } }Cocos Creator引擎内部已经帮你处理了这部分兼容性代码它通过screen模块提供了一个统一的接口。但知道底层原理非常重要因为当你需要调试一些深层次问题比如全屏事件没触发时你可能会需要直接和原生API打交道。2.2 Cocos Creator的封装逻辑在Cocos Creator 3.x版本中全屏API主要通过screen模块来提供。这个模块的设计目标是让开发者无需关心底层差异但同时也带来了一些限制和特定的行为模式。引擎通常会将全屏请求绑定到canvas元素上也就是我们游戏渲染的主画布。当你调用screen.requestFullScreen()时引擎内部会做以下几件事找到当前的canvasDOM元素。用上述兼容性方法调用该元素的requestFullscreen。监听全屏状态变化事件并同步到引擎的内部状态。这里有一个至关重要的限制也是新手最容易栽跟头的地方绝大多数现代浏览器包括Chrome、Safari、Firefox出于安全和用户体验的考虑都要求全屏请求必须由一个真实的、明确的用户手势User Gesture来触发。这意味着你不能在游戏的start()函数里、在一个定时器回调里、或者在一个网络请求成功的回调里直接调用全屏API。它必须发生在例如button节点的click事件、触摸屏的touchstart事件等直接由用户操作引发的事件处理函数中。注意这就是为什么你写了一个“游戏加载完成后自动全屏”的代码却无效的原因。解决方案通常是将全屏调用包装在一个按钮点击事件里或者先引导用户进行一个任意交互如点击“开始游戏”按钮再在回调中触发全屏。2.3 移动端与PC端的核心行为差异这个差异直接决定了你的全屏策略。PC浏览器通常实现的是“全屏模式”即网页内容占据整个屏幕隐藏浏览器地址栏、工具栏等。退出方式一般是按ESC键或调用exitFullscreen。移动端浏览器特别是iOS Safari和部分安卓浏览器情况复杂得多。很多移动浏览器并不支持或严格限制真正的Fullscreen API。它们更常见的是“横屏锁定”或“沉浸式网页应用”模式。当你旋转设备时浏览器会尝试让页面横屏显示并可能隐藏一些UI。此外移动端上常见的“添加到主屏幕”后以全屏模式打开的PWA渐进式Web应用又是另一种实现路径。因此在Cocos里做全屏很多时候在移动端实际做的是“横屏适配”和“阻止默认竖屏行为”的工作而非严格意义上的浏览器全屏。3. Cocos Creator中实现全屏的完整方案理解了原理我们来看具体怎么做。我将分基础实现、增强健壮性、UI适配三个层次来构建一个完整的方案。3.1 基础实现让全屏工作起来首先你需要在场景中有一个用于触发全屏的节点比如一个按钮。为这个按钮添加一个事件处理脚本。1. 创建全屏管理模块FullScreenManager.ts一个好的习惯是将全屏逻辑集中管理而不是散落在各个按钮脚本里。// FullScreenManager.ts import { _decorator, Component, screen, SystemEvent, macro } from cc; const { ccclass, property } _decorator; ccclass(FullScreenManager) export class FullScreenManager extends Component { // 单例模式方便全局访问 private static _instance: FullScreenManager null; public static get instance(): FullScreenManager { return FullScreenManager._instance; } // 当前是否处于全屏状态 private _isFullScreen: boolean false; onLoad() { if (FullScreenManager._instance FullScreenManager._instance ! this) { this.destroy(); return; } FullScreenManager._instance this; // 建议不要轻易DontDestroy除非全局需要 // director.addPersistRootNode(this.node); this.initFullScreenEvent(); } // 初始化全屏事件监听 private initFullScreenEvent() { // 监听引擎的全屏变化事件 screen.on(full-screen-change, this.onFullScreenChange, this); // 监听浏览器原生事件作为备用 document.addEventListener(fullscreenchange, this.onNativeFullScreenChange.bind(this)); document.addEventListener(webkitfullscreenchange, this.onNativeFullScreenChange.bind(this)); // for Safari } // 请求进入全屏 public requestFullScreen(): void { // 关键检查是否已有用户交互上下文。这里假设由按钮点击调用所以是安全的。 // 更安全的做法是传入触发事件的Event对象但Cocos封装后通常不需要。 if (screen.fullScreen()) { console.warn(Already in full screen mode.); return; } // 调用引擎API const success screen.requestFullScreen(); if (!success) { console.error(Failed to request full screen. It may not be called from a user gesture.); // 可以在这里触发一个失败回调或者显示提示给用户 // this.onFullScreenRequestFailed?.(); } } // 退出全屏 public exitFullScreen(): void { if (!screen.fullScreen()) { return; } screen.exitFullScreen(); } // 切换全屏状态 public toggleFullScreen(): void { if (screen.fullScreen()) { this.exitFullScreen(); } else { this.requestFullScreen(); } } // 引擎全屏变化回调 private onFullScreenChange(): void { this._isFullScreen screen.fullScreen(); console.log(Full screen changed via engine: ${this._isFullScreen}); this.dispatchFullScreenChangeEvent(this._isFullScreen); } // 原生全屏变化回调备用 private onNativeFullScreenChange(): void { const fullscreenElement document.fullscreenElement || document.webkitFullscreenElement; // Safari const isFullScreen !!fullscreenElement; // 如果引擎事件可能漏掉可以用原生事件同步状态但需谨慎处理重复触发 if (isFullScreen ! this._isFullScreen) { console.log(Full screen changed via native API: ${isFullScreen}); this._isFullScreen isFullScreen; this.dispatchFullScreenChangeEvent(isFullScreen); } } private dispatchFullScreenChangeEvent(isFullScreen: boolean): void { // 这里可以触发一个自定义事件通知游戏其他系统全屏状态变了 // 例如重新布局UI、调整分辨率、暂停/恢复游戏等 this.node.emit(fullscreen-changed, isFullScreen); } public get isFullScreen(): boolean { return this._isFullScreen; } onDestroy() { screen.off(full-screen-change, this.onFullScreenChange, this); document.removeEventListener(fullscreenchange, this.onNativeFullScreenChange); document.removeEventListener(webkitfullscreenchange, this.onNativeFullScreenChange); if (FullScreenManager._instance this) { FullScreenManager._instance null; } } }2. 在按钮上使用创建一个简单的按钮控制脚本。// FullScreenButton.ts import { _decorator, Component, Button } from cc; import { FullScreenManager } from ./FullScreenManager; const { ccclass, property } _decorator; ccclass(FullScreenButton) export class FullScreenButton extends Component { property(Button) public button: Button null; onLoad() { if (this.button) { this.button.node.on(Button.EventType.CLICK, this.onButtonClick, this); } } private onButtonClick() { const manager FullScreenManager.instance; if (manager) { manager.toggleFullScreen(); } else { console.error(FullScreenManager not found in scene.); } } onDestroy() { if (this.button) { this.button.node.off(Button.EventType.CLICK, this.onButtonClick, this); } } }将这个脚本挂载到你的按钮节点上并将按钮组件拖拽赋值。点击按钮理论上就能切换全屏了。3.2 增强方案处理边界情况与提升用户体验基础方案能跑通但离“健壮”还差得远。下面这些增强点是区分普通功能和优秀体验的关键。1. 用户手势验证与优雅降级如前所述非用户手势触发的全屏会失败。我们可以增加一个验证层。// 在FullScreenManager中增加 private _pendingFullScreenRequest: boolean false; public safeRequestFullScreen(userGestureEvent?: any): void { // 如果已经在全屏或已有 pending 请求则忽略 if (this._isFullScreen || this._pendingFullScreenRequest) return; // 尝试直接请求 if (screen.requestFullScreen()) { this._pendingFullScreenRequest true; // 标记请求已发出等待回调 } else { // 如果失败可能是非用户手势。提供一个降级方案。 console.warn(Immediate fullscreen request failed. This might be due to lack of user gesture.); // 方案A显示一个提示告诉用户“请点击屏幕任意位置进入全屏” // this.showFullScreenHint(); // 方案B将请求缓存在下一个真实的用户事件中触发复杂需事件代理 } } // 在全屏变化回调中重置 pending 状态 private onFullScreenChange(): void { const newState screen.fullScreen(); this._pendingFullScreenRequest false; // 无论成功失败请求流程结束 this._isFullScreen newState; console.log(Full screen changed: ${newState}); this.dispatchFullScreenChangeEvent(newState); }2. 处理ESC键退出用户按ESC退出全屏是浏览器默认行为但我们需要捕获这个事件以便同步更新游戏状态比如更新全屏按钮的图标。// 在FullScreenManager的initFullScreenEvent中增加键盘监听 private initFullScreenEvent() { // ... 原有事件监听 // 监听键盘事件主要用于检测ESC退出 systemEvent.on(SystemEvent.EventType.KEY_DOWN, this.onKeyDown, this); } private onKeyDown(event: EventKeyboard) { // macro.KEY.escape 是Cocos定义的键码常量 if (event.keyCode macro.KEY.escape) { // 注意按ESC时浏览器已退出全屏引擎事件会触发。 // 这里可以做一些额外的逻辑比如防止游戏暂停等。 // console.log(ESC key pressed, fullscreen might exit.); } }3. 移动端横屏适配对于移动端全屏往往意味着横屏。你需要确保你的游戏Canvas适配横屏。项目设置在Cocos Creator的项目设置-项目数据中将适配屏幕宽度/高度策略设置好通常SHOW_ALL或FIXED_WIDTH是安全选择。监听屏幕旋转import { view, ResolutionPolicy } from cc; // 在游戏启动或全屏时可以强制或建议横屏 public adjustForMobileLandscape(): void { // 设置设计分辨率策略更适合横屏 view.setDesignResolutionSize(1920, 1080, ResolutionPolicy.SHOW_ALL); // 监听屏幕旋转事件注意浏览器API非所有环境支持 window.addEventListener(orientationchange, this.onOrientationChange.bind(this)); } private onOrientationChange() { console.log(Orientation changed: ${screen.orientation}); // 根据新方向重新调整UI布局 this.dispatchFullScreenChangeEvent(this._isFullScreen); // 重新触发布局事件 }阻止竖屏默认行为在index.html的meta标签中可以添加以下内容来建议浏览器横屏显示但效果因浏览器而异。meta nameviewport contentwidthdevice-width, initial-scale1.0, maximum-scale1.0, user-scalableno, viewport-fitcover !-- 尝试锁定横屏支持性有限 -- meta namescreen-orientation contentlandscape3.3 UI适配全屏状态下的布局挑战进入全屏后你的游戏分辨率screen.windowSize很可能变了但Canvas的设计分辨率可能没变。这会导致UI拉伸、错位或模糊。解决方案使用Widget组件和动态调整对于静态UI如背景使用Widget对齐挂件组件将其对齐到父节点的各边并勾选绝对位置或相对位置使其能随父节点通常是Canvas大小变化而自动锚定。对于需要动态调整的UI监听我们FullScreenManager发出的fullscreen-changed事件。// 在某个UI管理脚本中 onLoad() { const manager FullScreenManager.instance; if (manager) { manager.node.on(fullscreen-changed, this.onFullScreenChanged, this); } // 初始调整一次 this.adjustUILayout(screen.fullScreen()); } private onFullScreenChanged(isFullScreen: boolean) { this.adjustUILayout(isFullScreen); } private adjustUILayout(isFullScreen: boolean) { const canvas find(Canvas); // 获取Canvas节点 const winSize screen.windowSize; // 获取窗口实际大小 const designSize view.getDesignResolutionSize(); // 获取设计分辨率 // 计算缩放比例和偏移 const scaleX winSize.width / designSize.width; const scaleY winSize.height / designSize.height; const scale Math.min(scaleX, scaleY); // 通常取最小缩放以保证内容全部显示 // 调整UI根节点的缩放 canvas.setScale(scale, scale); // 如果你的UI需要根据宽高比进行不同的布局如横屏竖屏 if (winSize.width winSize.height) { // 横屏布局 this.arrangeUIForLandscape(); } else { // 竖屏布局 this.arrangeUIForPortrait(); } // 特别处理全屏下可能需要隐藏或移动某些UI元素如网页端的浏览器地址栏提示 const someUINode this.node.getChildByName(AddressBarHint); if (someUINode) { someUINode.active !isFullScreen; // 非全屏时显示提示 } }字体和图像清晰度如果缩放导致模糊考虑为高清屏幕如Retina准备2x、3x的图集并在代码中根据devicePixelRatio动态设置canvas的width和height属性这属于更高级的优化Cocos引擎有相关设置。4. 常见问题排查与实战技巧即使按照上面的方案做了你依然可能会遇到一些诡异的问题。下面是我总结的“排坑指南”。4.1 问题速查表问题现象可能原因排查步骤与解决方案点击按钮全屏无任何反应1. 代码未被正确调用。2. 调用时机非用户手势。3. 浏览器安全策略阻止如iframe中。1. 在按钮回调函数第一行加console.log确认函数执行。2. 确保调用发生在click、touchstart等事件的同步回调中。3. 尝试在浏览器控制台直接执行document.querySelector(canvas).requestFullscreen()看是否有错误。全屏后游戏画面只占中间一部分有黑边UI适配未做好。Canvas缩放或Widget设置不正确。1. 检查Canvas节点的Widget组件确保四边对齐并勾选。2. 监听fullscreen-changed事件打印screen.windowSize和设计分辨率计算并手动调整根节点缩放。按ESC退出全屏后游戏UI布局混乱退出全屏时未触发UI重新布局。确保FullScreenManager在onFullScreenChange无论是进入还是退出时都正确派发了状态变化事件并且UI管理器监听了该事件。在iOS Safari上无效或行为异常iOS对全屏API支持有限或处于低电量模式等限制。1. 考虑使用webkitEnterFullscreen等特定前缀APICocos已封装。2. 引导用户手动点击屏幕上的“全屏”图标浏览器自带。3. 将重点放在横屏适配和PWA上而非强求API全屏。在微信小游戏或其它平台无效该平台可能不支持或修改了全屏API。1. 使用条件编译CC_WECHATGAME。2. 调用平台提供的特定API如微信小游戏的wx.setKeepScreenOn或wx.createFullScreenAd后者是激励视频广告全屏。3. 查阅该平台的专属文档。全屏切换时有明显闪烁或延迟浏览器重绘、CSS过渡或引擎视图尺寸同步导致。1. 为Canvas的容器div添加CSS样式{ transition: none; }。2. 尝试在请求全屏前先隐藏一些复杂UI完成后再显示。4.2 实战技巧与心得“双保险”事件监听如上面代码所示同时监听Cocos引擎的screen.on(full-screen-change)和原生的fullscreenchange事件。因为极少数情况下引擎的事件可能因为封装层问题未能触发原生事件可以作为兜底。状态同步是核心全屏不是一个动作而是一个状态。你的游戏逻辑如UI布局、摄像机视野、输入处理都应该依赖于这个状态而不是假设某个动作一定成功。FullScreenManager维护一个_isFullScreen状态并对外提供事件就是这个目的。移动端优先考虑“横屏”而非“全屏”在移动端项目规划初期就按横屏设计UI和布局。把“全屏API”当作一个在支持它的浏览器上能获得更好体验的“增强功能”而不是核心功能。这样在不支持的平台上应用依然能正常以横屏模式运行。测试测试再测试全屏功能必须在你的目标平台和浏览器上逐一测试。Chrome、Firefox、SafariMac/iOS、各手机厂商浏览器、微信内置浏览器、各种小游戏平台……它们的行为都可能不同。建立一个简单的测试用例快速验证核心功能。提供明确的用户反馈当全屏请求失败时不要静默失败。可以给用户一个友好的提示例如“您的浏览器不支持自动全屏请尝试点击浏览器右上角的图标。”或者“请先点击游戏画面再尝试全屏。”良好的用户体验来自于对失败情况的妥善处理。全屏功能就像游戏开发中的许多其他特性一样概念简单细节魔鬼。它要求开发者不仅了解引擎API还要对Web平台的特性和差异有深入的了解。希望这份指南能帮你构建一个稳定、可靠的全屏体验让你能把更多精力集中在游戏内容本身而不是和浏览器特性搏斗。
Cocos Creator全屏API实战:兼容性、UI适配与移动端横屏解决方案
发布时间:2026/7/13 5:08:33
1. 项目概述为什么全屏API值得你花时间研究如果你正在用Cocos Creator开发游戏或者互动应用那么“全屏”这个功能大概率是你绕不开的一个坎。它看起来简单——不就是让画面铺满整个屏幕吗但实际做起来你会发现这里面的坑一个接一个为什么在手机上点了按钮没反应为什么在PC浏览器里全屏后UI错位了为什么退出全屏后游戏状态乱了这些“为什么”背后是不同浏览器厂商五花八门的实现标准、移动端和桌面端迥异的交互逻辑以及引擎本身对原生API的封装策略。我接手过不少从其他引擎迁移过来或者从零开始的项目几乎每一个涉及到全屏需求时都会经历一番折腾。网上能找到的代码片段往往只解决了“能不能用”的问题但距离“好用、稳定、不出错”还差得远。这份指南就是把我这些年踩过的坑、总结的最佳实践以及那些官方文档里没明说但至关重要的细节系统地梳理给你。无论你是想实现一个沉浸式的关卡体验、一个全屏播放的视频广告还是一个简单的“点击全屏”按钮这里的内容都能让你少走弯路。2. 全屏API的核心原理与浏览器差异全屏功能本质上并不是Cocos引擎“创造”的而是对浏览器原生Fullscreen API或称Fullscreen API的一层封装。理解这层封装是解决一切奇怪问题的起点。2.1 原生Fullscreen API的“分裂”现状你可能听说过requestFullscreen这个方法但实际写代码时会发现它前面常常需要加上webkit、moz或ms这样的前缀。这是因为全屏API在成为正式标准前各大浏览器都有自己的实现。虽然现在主流浏览器都支持标准写法但为了极致的兼容性尤其是考虑一些老旧的内嵌浏览器环境我们通常还是需要做能力检测。// 一个经典的全屏请求函数考虑了兼容性 function requestFullscreen(element) { if (element.requestFullscreen) { element.requestFullscreen(); } else if (element.webkitRequestFullscreen) { // Chrome, Safari, Edge element.webkitRequestFullscreen(); } else if (element.mozRequestFullScreen) { // Firefox element.mozRequestFullScreen(); } else if (element.msRequestFullscreen) { // IE/Edge Legacy element.msRequestFullscreen(); } }Cocos Creator引擎内部已经帮你处理了这部分兼容性代码它通过screen模块提供了一个统一的接口。但知道底层原理非常重要因为当你需要调试一些深层次问题比如全屏事件没触发时你可能会需要直接和原生API打交道。2.2 Cocos Creator的封装逻辑在Cocos Creator 3.x版本中全屏API主要通过screen模块来提供。这个模块的设计目标是让开发者无需关心底层差异但同时也带来了一些限制和特定的行为模式。引擎通常会将全屏请求绑定到canvas元素上也就是我们游戏渲染的主画布。当你调用screen.requestFullScreen()时引擎内部会做以下几件事找到当前的canvasDOM元素。用上述兼容性方法调用该元素的requestFullscreen。监听全屏状态变化事件并同步到引擎的内部状态。这里有一个至关重要的限制也是新手最容易栽跟头的地方绝大多数现代浏览器包括Chrome、Safari、Firefox出于安全和用户体验的考虑都要求全屏请求必须由一个真实的、明确的用户手势User Gesture来触发。这意味着你不能在游戏的start()函数里、在一个定时器回调里、或者在一个网络请求成功的回调里直接调用全屏API。它必须发生在例如button节点的click事件、触摸屏的touchstart事件等直接由用户操作引发的事件处理函数中。注意这就是为什么你写了一个“游戏加载完成后自动全屏”的代码却无效的原因。解决方案通常是将全屏调用包装在一个按钮点击事件里或者先引导用户进行一个任意交互如点击“开始游戏”按钮再在回调中触发全屏。2.3 移动端与PC端的核心行为差异这个差异直接决定了你的全屏策略。PC浏览器通常实现的是“全屏模式”即网页内容占据整个屏幕隐藏浏览器地址栏、工具栏等。退出方式一般是按ESC键或调用exitFullscreen。移动端浏览器特别是iOS Safari和部分安卓浏览器情况复杂得多。很多移动浏览器并不支持或严格限制真正的Fullscreen API。它们更常见的是“横屏锁定”或“沉浸式网页应用”模式。当你旋转设备时浏览器会尝试让页面横屏显示并可能隐藏一些UI。此外移动端上常见的“添加到主屏幕”后以全屏模式打开的PWA渐进式Web应用又是另一种实现路径。因此在Cocos里做全屏很多时候在移动端实际做的是“横屏适配”和“阻止默认竖屏行为”的工作而非严格意义上的浏览器全屏。3. Cocos Creator中实现全屏的完整方案理解了原理我们来看具体怎么做。我将分基础实现、增强健壮性、UI适配三个层次来构建一个完整的方案。3.1 基础实现让全屏工作起来首先你需要在场景中有一个用于触发全屏的节点比如一个按钮。为这个按钮添加一个事件处理脚本。1. 创建全屏管理模块FullScreenManager.ts一个好的习惯是将全屏逻辑集中管理而不是散落在各个按钮脚本里。// FullScreenManager.ts import { _decorator, Component, screen, SystemEvent, macro } from cc; const { ccclass, property } _decorator; ccclass(FullScreenManager) export class FullScreenManager extends Component { // 单例模式方便全局访问 private static _instance: FullScreenManager null; public static get instance(): FullScreenManager { return FullScreenManager._instance; } // 当前是否处于全屏状态 private _isFullScreen: boolean false; onLoad() { if (FullScreenManager._instance FullScreenManager._instance ! this) { this.destroy(); return; } FullScreenManager._instance this; // 建议不要轻易DontDestroy除非全局需要 // director.addPersistRootNode(this.node); this.initFullScreenEvent(); } // 初始化全屏事件监听 private initFullScreenEvent() { // 监听引擎的全屏变化事件 screen.on(full-screen-change, this.onFullScreenChange, this); // 监听浏览器原生事件作为备用 document.addEventListener(fullscreenchange, this.onNativeFullScreenChange.bind(this)); document.addEventListener(webkitfullscreenchange, this.onNativeFullScreenChange.bind(this)); // for Safari } // 请求进入全屏 public requestFullScreen(): void { // 关键检查是否已有用户交互上下文。这里假设由按钮点击调用所以是安全的。 // 更安全的做法是传入触发事件的Event对象但Cocos封装后通常不需要。 if (screen.fullScreen()) { console.warn(Already in full screen mode.); return; } // 调用引擎API const success screen.requestFullScreen(); if (!success) { console.error(Failed to request full screen. It may not be called from a user gesture.); // 可以在这里触发一个失败回调或者显示提示给用户 // this.onFullScreenRequestFailed?.(); } } // 退出全屏 public exitFullScreen(): void { if (!screen.fullScreen()) { return; } screen.exitFullScreen(); } // 切换全屏状态 public toggleFullScreen(): void { if (screen.fullScreen()) { this.exitFullScreen(); } else { this.requestFullScreen(); } } // 引擎全屏变化回调 private onFullScreenChange(): void { this._isFullScreen screen.fullScreen(); console.log(Full screen changed via engine: ${this._isFullScreen}); this.dispatchFullScreenChangeEvent(this._isFullScreen); } // 原生全屏变化回调备用 private onNativeFullScreenChange(): void { const fullscreenElement document.fullscreenElement || document.webkitFullscreenElement; // Safari const isFullScreen !!fullscreenElement; // 如果引擎事件可能漏掉可以用原生事件同步状态但需谨慎处理重复触发 if (isFullScreen ! this._isFullScreen) { console.log(Full screen changed via native API: ${isFullScreen}); this._isFullScreen isFullScreen; this.dispatchFullScreenChangeEvent(isFullScreen); } } private dispatchFullScreenChangeEvent(isFullScreen: boolean): void { // 这里可以触发一个自定义事件通知游戏其他系统全屏状态变了 // 例如重新布局UI、调整分辨率、暂停/恢复游戏等 this.node.emit(fullscreen-changed, isFullScreen); } public get isFullScreen(): boolean { return this._isFullScreen; } onDestroy() { screen.off(full-screen-change, this.onFullScreenChange, this); document.removeEventListener(fullscreenchange, this.onNativeFullScreenChange); document.removeEventListener(webkitfullscreenchange, this.onNativeFullScreenChange); if (FullScreenManager._instance this) { FullScreenManager._instance null; } } }2. 在按钮上使用创建一个简单的按钮控制脚本。// FullScreenButton.ts import { _decorator, Component, Button } from cc; import { FullScreenManager } from ./FullScreenManager; const { ccclass, property } _decorator; ccclass(FullScreenButton) export class FullScreenButton extends Component { property(Button) public button: Button null; onLoad() { if (this.button) { this.button.node.on(Button.EventType.CLICK, this.onButtonClick, this); } } private onButtonClick() { const manager FullScreenManager.instance; if (manager) { manager.toggleFullScreen(); } else { console.error(FullScreenManager not found in scene.); } } onDestroy() { if (this.button) { this.button.node.off(Button.EventType.CLICK, this.onButtonClick, this); } } }将这个脚本挂载到你的按钮节点上并将按钮组件拖拽赋值。点击按钮理论上就能切换全屏了。3.2 增强方案处理边界情况与提升用户体验基础方案能跑通但离“健壮”还差得远。下面这些增强点是区分普通功能和优秀体验的关键。1. 用户手势验证与优雅降级如前所述非用户手势触发的全屏会失败。我们可以增加一个验证层。// 在FullScreenManager中增加 private _pendingFullScreenRequest: boolean false; public safeRequestFullScreen(userGestureEvent?: any): void { // 如果已经在全屏或已有 pending 请求则忽略 if (this._isFullScreen || this._pendingFullScreenRequest) return; // 尝试直接请求 if (screen.requestFullScreen()) { this._pendingFullScreenRequest true; // 标记请求已发出等待回调 } else { // 如果失败可能是非用户手势。提供一个降级方案。 console.warn(Immediate fullscreen request failed. This might be due to lack of user gesture.); // 方案A显示一个提示告诉用户“请点击屏幕任意位置进入全屏” // this.showFullScreenHint(); // 方案B将请求缓存在下一个真实的用户事件中触发复杂需事件代理 } } // 在全屏变化回调中重置 pending 状态 private onFullScreenChange(): void { const newState screen.fullScreen(); this._pendingFullScreenRequest false; // 无论成功失败请求流程结束 this._isFullScreen newState; console.log(Full screen changed: ${newState}); this.dispatchFullScreenChangeEvent(newState); }2. 处理ESC键退出用户按ESC退出全屏是浏览器默认行为但我们需要捕获这个事件以便同步更新游戏状态比如更新全屏按钮的图标。// 在FullScreenManager的initFullScreenEvent中增加键盘监听 private initFullScreenEvent() { // ... 原有事件监听 // 监听键盘事件主要用于检测ESC退出 systemEvent.on(SystemEvent.EventType.KEY_DOWN, this.onKeyDown, this); } private onKeyDown(event: EventKeyboard) { // macro.KEY.escape 是Cocos定义的键码常量 if (event.keyCode macro.KEY.escape) { // 注意按ESC时浏览器已退出全屏引擎事件会触发。 // 这里可以做一些额外的逻辑比如防止游戏暂停等。 // console.log(ESC key pressed, fullscreen might exit.); } }3. 移动端横屏适配对于移动端全屏往往意味着横屏。你需要确保你的游戏Canvas适配横屏。项目设置在Cocos Creator的项目设置-项目数据中将适配屏幕宽度/高度策略设置好通常SHOW_ALL或FIXED_WIDTH是安全选择。监听屏幕旋转import { view, ResolutionPolicy } from cc; // 在游戏启动或全屏时可以强制或建议横屏 public adjustForMobileLandscape(): void { // 设置设计分辨率策略更适合横屏 view.setDesignResolutionSize(1920, 1080, ResolutionPolicy.SHOW_ALL); // 监听屏幕旋转事件注意浏览器API非所有环境支持 window.addEventListener(orientationchange, this.onOrientationChange.bind(this)); } private onOrientationChange() { console.log(Orientation changed: ${screen.orientation}); // 根据新方向重新调整UI布局 this.dispatchFullScreenChangeEvent(this._isFullScreen); // 重新触发布局事件 }阻止竖屏默认行为在index.html的meta标签中可以添加以下内容来建议浏览器横屏显示但效果因浏览器而异。meta nameviewport contentwidthdevice-width, initial-scale1.0, maximum-scale1.0, user-scalableno, viewport-fitcover !-- 尝试锁定横屏支持性有限 -- meta namescreen-orientation contentlandscape3.3 UI适配全屏状态下的布局挑战进入全屏后你的游戏分辨率screen.windowSize很可能变了但Canvas的设计分辨率可能没变。这会导致UI拉伸、错位或模糊。解决方案使用Widget组件和动态调整对于静态UI如背景使用Widget对齐挂件组件将其对齐到父节点的各边并勾选绝对位置或相对位置使其能随父节点通常是Canvas大小变化而自动锚定。对于需要动态调整的UI监听我们FullScreenManager发出的fullscreen-changed事件。// 在某个UI管理脚本中 onLoad() { const manager FullScreenManager.instance; if (manager) { manager.node.on(fullscreen-changed, this.onFullScreenChanged, this); } // 初始调整一次 this.adjustUILayout(screen.fullScreen()); } private onFullScreenChanged(isFullScreen: boolean) { this.adjustUILayout(isFullScreen); } private adjustUILayout(isFullScreen: boolean) { const canvas find(Canvas); // 获取Canvas节点 const winSize screen.windowSize; // 获取窗口实际大小 const designSize view.getDesignResolutionSize(); // 获取设计分辨率 // 计算缩放比例和偏移 const scaleX winSize.width / designSize.width; const scaleY winSize.height / designSize.height; const scale Math.min(scaleX, scaleY); // 通常取最小缩放以保证内容全部显示 // 调整UI根节点的缩放 canvas.setScale(scale, scale); // 如果你的UI需要根据宽高比进行不同的布局如横屏竖屏 if (winSize.width winSize.height) { // 横屏布局 this.arrangeUIForLandscape(); } else { // 竖屏布局 this.arrangeUIForPortrait(); } // 特别处理全屏下可能需要隐藏或移动某些UI元素如网页端的浏览器地址栏提示 const someUINode this.node.getChildByName(AddressBarHint); if (someUINode) { someUINode.active !isFullScreen; // 非全屏时显示提示 } }字体和图像清晰度如果缩放导致模糊考虑为高清屏幕如Retina准备2x、3x的图集并在代码中根据devicePixelRatio动态设置canvas的width和height属性这属于更高级的优化Cocos引擎有相关设置。4. 常见问题排查与实战技巧即使按照上面的方案做了你依然可能会遇到一些诡异的问题。下面是我总结的“排坑指南”。4.1 问题速查表问题现象可能原因排查步骤与解决方案点击按钮全屏无任何反应1. 代码未被正确调用。2. 调用时机非用户手势。3. 浏览器安全策略阻止如iframe中。1. 在按钮回调函数第一行加console.log确认函数执行。2. 确保调用发生在click、touchstart等事件的同步回调中。3. 尝试在浏览器控制台直接执行document.querySelector(canvas).requestFullscreen()看是否有错误。全屏后游戏画面只占中间一部分有黑边UI适配未做好。Canvas缩放或Widget设置不正确。1. 检查Canvas节点的Widget组件确保四边对齐并勾选。2. 监听fullscreen-changed事件打印screen.windowSize和设计分辨率计算并手动调整根节点缩放。按ESC退出全屏后游戏UI布局混乱退出全屏时未触发UI重新布局。确保FullScreenManager在onFullScreenChange无论是进入还是退出时都正确派发了状态变化事件并且UI管理器监听了该事件。在iOS Safari上无效或行为异常iOS对全屏API支持有限或处于低电量模式等限制。1. 考虑使用webkitEnterFullscreen等特定前缀APICocos已封装。2. 引导用户手动点击屏幕上的“全屏”图标浏览器自带。3. 将重点放在横屏适配和PWA上而非强求API全屏。在微信小游戏或其它平台无效该平台可能不支持或修改了全屏API。1. 使用条件编译CC_WECHATGAME。2. 调用平台提供的特定API如微信小游戏的wx.setKeepScreenOn或wx.createFullScreenAd后者是激励视频广告全屏。3. 查阅该平台的专属文档。全屏切换时有明显闪烁或延迟浏览器重绘、CSS过渡或引擎视图尺寸同步导致。1. 为Canvas的容器div添加CSS样式{ transition: none; }。2. 尝试在请求全屏前先隐藏一些复杂UI完成后再显示。4.2 实战技巧与心得“双保险”事件监听如上面代码所示同时监听Cocos引擎的screen.on(full-screen-change)和原生的fullscreenchange事件。因为极少数情况下引擎的事件可能因为封装层问题未能触发原生事件可以作为兜底。状态同步是核心全屏不是一个动作而是一个状态。你的游戏逻辑如UI布局、摄像机视野、输入处理都应该依赖于这个状态而不是假设某个动作一定成功。FullScreenManager维护一个_isFullScreen状态并对外提供事件就是这个目的。移动端优先考虑“横屏”而非“全屏”在移动端项目规划初期就按横屏设计UI和布局。把“全屏API”当作一个在支持它的浏览器上能获得更好体验的“增强功能”而不是核心功能。这样在不支持的平台上应用依然能正常以横屏模式运行。测试测试再测试全屏功能必须在你的目标平台和浏览器上逐一测试。Chrome、Firefox、SafariMac/iOS、各手机厂商浏览器、微信内置浏览器、各种小游戏平台……它们的行为都可能不同。建立一个简单的测试用例快速验证核心功能。提供明确的用户反馈当全屏请求失败时不要静默失败。可以给用户一个友好的提示例如“您的浏览器不支持自动全屏请尝试点击浏览器右上角的图标。”或者“请先点击游戏画面再尝试全屏。”良好的用户体验来自于对失败情况的妥善处理。全屏功能就像游戏开发中的许多其他特性一样概念简单细节魔鬼。它要求开发者不仅了解引擎API还要对Web平台的特性和差异有深入的了解。希望这份指南能帮你构建一个稳定、可靠的全屏体验让你能把更多精力集中在游戏内容本身而不是和浏览器特性搏斗。