mcpField
S
scale-and-responsive

scale-and-responsive

github.com/phaserjs开发40.6k939 次安装10 周前更新
代码仓库

关于 scale-and-responsive

面向 Phaser 4 游戏开发者的缩放与响应式布局技能指南,介绍 ScaleManager 的配置和使用。涵盖画布缩放、居中、全屏、方向变化及尺寸调整事件。

主要功能

  1. 01多种画布缩放模式
  2. 02画布自动居中与缩放
  3. 03响应式尺寸调整与事件
  4. 04全屏切换
  5. 05方向变化处理

适用场景

  1. 01让游戏画布适配容器
  2. 02创建居中的等比布局
  3. 03窗口尺寸变化时调整场景
  4. 04响应移动设备方向变化

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/phaserjs/phaser --skill scale-and-responsive

安装后在 Claude Code 中输入/scale-and-responsive确认已加载。

详细介绍

缩放与响应式设计

如何在 Phaser 4 中使用 ScaleManager 进行缩放、居中、全屏、方向处理以及响应式调整大小。

关键源码路径: src/scale/ScaleManager.js、src/scale/const/、src/scale/events/、src/core/typedefs/ScaleConfig.js 相关技能: ../game-setup-and-config/SKILL.md

快速开始

缩放以适应并居中——响应式游戏最常见的配置:

const config = {
    type: Phaser.AUTO,
    scale: {
        parent: 'game-container',
        mode: Phaser.Scale.FIT,
        autoCenter: Phaser.Scale.CENTER_BOTH,
        width: 800,
        height: 600
    },
    scene: MyScene
};

const game = new Phaser.Game(config);

scale 配置对象会被解析为一个 Phaser.Core.Config 实例,ScaleManager 在启动期间读取该实例。ScaleManager 会设置 canvas 元素的尺寸,并应用 CSS 缩放使其适配父容器。

核心概念

ScaleManager (src/scale/ScaleManager.js)

ScaleManager 在 Game 启动序列期间创建,可通过 game.scale 或在 Scene 内通过 this.scale 访问。它继承自 EventEmitter。

三个内部 Size 组件驱动所有计算:

组件属性用途
gameSizegame.scale.gameSize来自配置的未经修改的游戏尺寸。用于世界边界、摄像机。通过 game.scale.width / game.scale.height 读取。
baseSizegame.scale.baseSize自动取整后的 gameSize。设置实际的 canvas.width 和 canvas.height 属性。
displaySizegame.scale.displaySize应用缩放模式、父容器边界和缩放后,经 CSS 缩放的 canvas 尺寸。设置 canvas.style.width / canvas.style.height。

缩放的工作原理是保持 canvas 元素尺寸固定(baseSize),并通过 CSS 属性(displaySize)将其拉伸。这相当于 CSS transform-scale,但没有浏览器前缀问题。

displayScale 属性(Phaser.Math.Vector2)保存比率 baseSize / canvasBounds,在内部用于输入坐标转换。

缩放模式 (src/scale/const/SCALE_MODE_CONST.js)

所有模式都在 Phaser.Scale.ScaleModes 上,并通过配置中的 scale.mode 设置:

常量值行为
NONE0不自动缩放。Canvas 使用配置的宽度/高度。由你自行管理尺寸。如果你在外部调整 canvas 大小,请调用 game.scale.resize(w, h) 来更新内部状态。
WIDTH_CONTROLS_HEIGHT1高度根据宽度自动调整以保持宽高比。
HEIGHT_CONTROLS_WIDTH2宽度根据高度自动调整以保持宽高比。
FIT3缩放以适应父容器内部,同时保持宽高比。可能留有空白区域(letterbox/pillarbox)。最常用的模式。
ENVELOP4缩放以覆盖整个父容器,同时保持宽高比。可能超出父容器边界(内容会被裁剪)。
RESIZE5canvas 元素本身被调整大小以填满父容器。无 CSS 缩放——1:1 像素映射。gameSize、baseSize 和 displaySize 都会随之改变以匹配父容器。注意大尺寸显示器上的 GPU 填充率问题。
EXPAND6RESIZE 和 FIT 的混合体。可见区域调整大小以填满父容器,canvas 缩放以适应该区域内。v3.80 新增。

简写常量也可直接在 Phaser.Scale 上使用:Phaser.Scale.FIT、Phaser.Scale.RESIZE 等。

居中模式 (src/scale/const/CENTER_CONST.js)

通过配置中的 scale.autoCenter 设置。居中是通过在 canvas 上设置 CSS marginLeft 和 marginTop 实现的:

常量值行为
NO_CENTER0不自动居中(默认)。
CENTER_BOTH1在父容器内水平和垂直居中。
CENTER_HORIZONTALLY2仅水平居中。
CENTER_VERTICALLY3仅垂直居中。

父元素必须具有可计算的边界。如果父元素没有定义宽度/高度,居中将无法正常工作。

缩放 (src/scale/const/ZOOM_CONST.js)

通过配置中的 scale.zoom 设置。它会放大显示尺寸:

常量值行为
NO_ZOOM1不缩放(默认)。
ZOOM_2X22 倍缩放 -- 适合低基础分辨率的像素画。
ZOOM_4X44 倍缩放。
MAX_ZOOM-1自动计算能放入父容器的最大整数缩放倍数。

你也可以传入任意数值。缩放会影响 CSS 显示尺寸,但不会影响画布分辨率。

方向 (src/scale/const/ORIENTATION_CONST.js)

通过 game.scale.orientation 读取。值为字符串:

  • Phaser.Scale.Orientation.LANDSCAPE = 'landscape-primary'
  • Phaser.Scale.Orientation.LANDSCAPE_SECONDARY = 'landscape-secondary'
  • Phaser.Scale.Orientation.PORTRAIT = 'portrait-primary'
  • Phaser.Scale.Orientation.PORTRAIT_SECONDARY = 'portrait-secondary'

便捷布尔值:game.scale.isPortrait、game.scale.isLandscape(设备方向),game.scale.isGamePortrait、game.scale.isGameLandscape(游戏尺寸)。

常见模式

居中显示的 FIT 模式(最常用)
scale: {
    parent: 'game-container',
    mode: Phaser.Scale.FIT,
    autoCenter: Phaser.Scale.CENTER_BOTH,
    width: 1280,
    height: 720
}

游戏会保持其 16:9 宽高比,并在父容器内居中。空白区域会显示为黑边/竖黑边条。通过设置父容器的背景颜色来控制黑边的外观。

响应式尺寸调整(动态画布尺寸)
scale: {
    parent: 'game-container',
    mode: Phaser.Scale.RESIZE,
    width: '100%',
    height: '100%'
}
// In your scene -- respond to size changes:
create() {
    this.scale.on('resize', this.handleResize, this);
}

handleResize(gameSize, baseSize, displaySize) {
    this.cameras.resize(gameSize.width, gameSize.height);
    // Reposition UI elements based on new dimensions
}

宽度/高度接受百分比字符串(例如 '100%'),它们会相对于父容器尺寸进行解析。如果父容器没有尺寸,则会回退到 window.innerWidth / window.innerHeight。

全屏切换
// Must be called from a pointerup gesture (not pointerdown)
this.input.on('pointerup', () => {
    if (this.scale.isFullscreen) {
        this.scale.stopFullscreen();
    } else {
        this.scale.startFullscreen();
    }
});

// Or use the convenience method:
this.input.on('pointerup', () => {
    this.scale.toggleFullscreen();
});
  • startFullscreen(fullscreenOptions) -- 请求浏览器全屏。默认选项:{ navigationUI: 'hide' }。
  • stopFullscreen() -- 退出全屏模式。
  • toggleFullscreen(fullscreenOptions) -- 在两者之间切换。
  • isFullscreen -- 表示当前状态只读布尔值。

如果未配置 fullscreenTarget,Phaser 会创建一个临时 <div>,将画布移入其中,并将该 div 全屏显示。退出全屏时该 div 会被移除。

对于 iframe,必须包含 allowfullscreen 属性。

移动端方向处理
create() {
    this.scale.on('orientationchange', (orientation) => {
        if (orientation === Phaser.Scale.LANDSCAPE) {
            // Show game UI
        } else {
            // Show "rotate device" message
        }
    });

    // Lock orientation (mobile browsers only, limited support):
    this.scale.lockOrientation('landscape');
}
固定尺寸(不缩放)
scale: {
    mode: Phaser.Scale.NONE,
    width: 800,
    height: 600
}

在 NONE 模式下,如果你在外部更改画布尺寸,必须调用 game.scale.resize(newWidth, newHeight) 来更新所有内部组件,包括输入坐标。

使用最大缩放的像素画
scale: {
    mode: Phaser.Scale.FIT,
    autoCenter: Phaser.Scale.CENTER_BOTH,
    width: 320,
    height: 240,
    zoom: Phaser.Scale.MAX_ZOOM
},
pixelArt: true

MAX_ZOOM 会计算能放入父容器的最大整数倍数。结合 pixelArt: true(会禁用抗锯齿),可以获得清晰的像素渲染效果。

用于网格对齐的吸附值
scale: {
    mode: Phaser.Scale.FIT,
    width: 800,
    height: 600,
    snap: { width: 16, height: 16 }
}

当浏览器尺寸变化时,尺寸会向下吸附到最近的网格倍数(使用向下取整)。最适合与 FIT 模式配合使用。也可以在运行时设置:game.scale.setSnap(16, 16),或用 game.scale.setSnap() 重置。

最小/最大尺寸约束
scale: {
    mode: Phaser.Scale.FIT,
    width: 800,
    height: 600,
    min: { width: 400, height: 300 },
    max: { width: 1600, height: 1200 }
}

在缩放计算期间,显示尺寸会被限制在这些边界内。

配置参考

ScaleConfig (src/core/typedefs/ScaleConfig.js)

所有属性都放在游戏配置中的 scale 对象内:

属性类型默认值描述
widthnumber|string1024游戏基础宽度。像 '100%' 这样的字符串会基于父级尺寸解析。
heightnumber|string768游戏基础高度。像 '100%' 这样的字符串会基于父级尺寸解析。
zoomnumber|ZoomType1画布缩放因子。使用 Phaser.Scale.MAX_ZOOM 表示自动。
parentHTMLElement|string|nullundefined父级 DOM 元素或其 ID。undefined = document.body。null = 无父级(由你管理画布放置)。
expandParentbooleantrue允许 ScaleManager 将父级/body 的 CSS 高度设置为 100%。
modeScaleModeTypeNONE缩放模式常量(见上表)。
min{width, height}-最小画布尺寸。
max{width, height}-最大画布尺寸。
snap{width, height}-用于调整尺寸取整的对齐值。
autoRoundbooleanfalse将显示/样式尺寸向下取整,以提升低性能设备的性能。
autoCenterCenterTypeNO_CENTER自动居中模式(见上表)。
resizeIntervalnumber500父级尺寸检查之间的毫秒数(回退轮询)。
fullscreenTargetHTMLElement|stringundefined要发送到全屏的元素。如果未设置,Phaser 会创建一个包装 div。

事件 (src/scale/events/)

所有事件都位于 Phaser.Scale.Events 上,并由 ScaleManager 实例(game.scale)发出:

事件字符串值回调签名何时
RESIZE'resize'(gameSize, baseSize, displaySize, previousWidth, previousHeight)任何调整尺寸、刷新或缩放变化时。三个 Size 参数都有 .width、.height、.aspectRatio。
ORIENTATION_CHANGE'orientationchange'(orientation)设备方向变化时。orientation 是来自 Phaser.Scale.Orientation 的字符串常量。
ENTER_FULLSCREEN'enterfullscreen'()浏览器成功进入全屏时。
LEAVE_FULLSCREEN'leavefullscreen'()浏览器离开全屏时(通过代码或用户按 ESC)。
FULLSCREEN_FAILED'fullscreenfailed'(error)全屏请求被浏览器拒绝时。
FULLSCREEN_UNSUPPORTED'fullscreenunsupported'()浏览器不支持 Fullscreen API 时。

API 快速参考

关键属性(除非另有说明,否则为只读)
属性类型描述
widthnumber游戏宽度(来自 gameSize.width)。
heightnumber游戏高度(来自 gameSize.height)。
isFullscreenboolean当前是否处于全屏模式。
isPortraitboolean设备是否处于竖屏方向。
isLandscapeboolean设备是否处于横屏方向。
isGamePortraitboolean游戏尺寸是否高于宽。
isGameLandscapeboolean游戏尺寸是否宽于高。
scaleModenumber当前缩放模式常量。
orientationstring当前方向字符串。
zoomnumber当前缩放因子。
autoRoundboolean尺寸是否自动向下取整。
autoCenternumber当前居中模式常量。
parentSizeSize计算得出的父容器尺寸。
gameSizeSize未修改的游戏尺寸。
baseSizeSizeCanvas 元素尺寸。
displaySizeSizeCSS 缩放后的显示尺寸。
displayScaleVector2baseSize 与 canvasBounds 的比值(用于输入映射)。
canvasHTMLCanvasElement游戏 canvas 元素。
canvasBoundsRectanglecanvas 的 DOM 边界矩形。
parentHTMLElement父 DOM 元素。
parentIsWindowboolean如果父容器是 document.body 则为 true。
resizeIntervalnumber轮询间隔,单位为 ms(可写)。
关键方法
方法返回描述
resize(width, height)this用于 NONE 模式:直接设置游戏、基础和显示尺寸。更新 canvas 元素和 CSS。
setGameSize(width, height)this用于缩放模式(FIT 等):更改基础游戏尺寸并重新应用缩放。
setParentSize(width, height)this手动设置父容器尺寸(当 parent: null 时有用)。
setZoom(value)this在运行时更改缩放因子。
setMaxZoom()this将缩放设置为适合父容器的最大整数。
setSnap(snapWidth, snapHeight)this设置或重置吸附网格值。
getMaxZoom()number计算(但不应用)最大缩放。
getViewPort(camera?, out?)Rectangle获取可见区域矩形,可选针对特定相机。
refresh(prevW?, prevH?)this强制重新计算缩放、边界、方向。发出 RESIZE。
startFullscreen(options?)void进入全屏。必须从 pointerup 中调用。
stopFullscreen()void退出全屏。
toggleFullscreen(options?)void切换全屏状态。
lockOrientation(orientation)boolean尝试锁定屏幕方向(仅限移动设备)。
transformX(pageX)number将 DOM pageX 转换为游戏坐标。
transformY(pageY)number将 DOM pageY 转换为游戏坐标。
getParentBounds()boolean重新计算父容器尺寸。如果发生变化则返回 true。
updateCenter()void重新计算并应用居中边距。
updateBounds()void从 canvas 的边界客户端矩形更新 canvasBounds。

注意事项

  1. 父元素必须有尺寸。 ScaleManager 依赖父元素计算后的 CSS 尺寸。未设置样式的 <div> 高度为零,会破坏居中和缩放。请为其指定明确的 CSS 尺寸,或让 expandParent: true(默认值)将 body/父元素设置为 100% 高度。

  2. 父元素不能有内边距。 ScaleManager 不会考虑父元素的内边距。请将内边距应用到包裹元素上,或在父元素的父元素上使用外边距。

  3. 不要直接为 canvas 设置样式。 ScaleManager 会控制 canvas.style.width、canvas.style.height、marginLeft 和 marginTop。外部 CSS 作用于 canvas 会产生冲突。

  4. 全屏需要 pointerup,而非 pointerdown。 在触摸设备上,除非文档已经接收过触摸输入,否则 pointerdown 全屏请求会被阻止。请始终使用 pointerup 以确保行为可靠。

  5. iframe 中的全屏需要 allowfullscreen。 如果 iframe 元素上没有此属性,全屏请求会静默失败。

  6. RESIZE 模式与 GPU 填充率。 RESIZE 会创建与父元素匹配的 1:1 像素 canvas。在高分辨率显示器上,这可能非常大。请在低端设备上监控性能。

  7. resize() 与 setGameSize()。 仅在 NONE 模式下使用 resize()(它会直接设置所有内容)。在使用 FIT/ENVELOP/等模式时使用 setGameSize()(它会保留缩放模式的计算)。

  8. iOS 高度问题。 当父元素是 iOS 上的 window 时,ScaleManager 会使用 GetInnerHeight 来规避 Safari 动态工具栏影响 getBoundingClientRect 的问题。

  9. 百分比字符串需要父元素有尺寸。 设置 width: '100%' 或 height: '100%' 会基于 parentSize 进行解析。如果父元素没有尺寸,则会回退到 window.innerWidth/innerHeight。

  10. resizeInterval 是后备轮询。 现代浏览器会派发 resize 事件,但 ScaleManager 也会每隔 resizeInterval 毫秒(默认 500)轮询一次,以捕获旧版浏览器上的边缘情况。

源文件映射

FilePurpose
src/scale/ScaleManager.js主类——所有缩放、居中、全屏、方向逻辑。通过 game.scale 访问。
src/scale/const/SCALE_MODE_CONST.js缩放模式常量:NONE、WIDTH_CONTROLS_HEIGHT、HEIGHT_CONTROLS_WIDTH、FIT、ENVELOP、RESIZE、EXPAND。
src/scale/const/CENTER_CONST.js居中模式常量:NO_CENTER、CENTER_BOTH、CENTER_HORIZONTALLY、CENTER_VERTICALLY。
src/scale/const/ZOOM_CONST.js缩放常量:NO_ZOOM、ZOOM_2X、ZOOM_4X、MAX_ZOOM。
src/scale/const/ORIENTATION_CONST.js方向字符串常量:LANDSCAPE、LANDSCAPE_SECONDARY、PORTRAIT、PORTRAIT_SECONDARY。
src/scale/events/RESIZE_EVENT.js缩放事件定义。回调接收 gameSize、baseSize、displaySize、previousWidth、previousHeight。
src/scale/events/ENTER_FULLSCREEN_EVENT.js成功进入全屏时触发。
src/scale/events/LEAVE_FULLSCREEN_EVENT.js离开全屏时触发。
src/scale/events/FULLSCREEN_FAILED_EVENT.js全屏请求被拒绝时触发。
src/scale/events/FULLSCREEN_UNSUPPORTED_EVENT.js浏览器不支持 Fullscreen API 时触发。
src/scale/events/ORIENTATION_CHANGE_EVENT.js设备方向变化时触发。回调接收方向字符串。
src/core/typedefs/ScaleConfig.jsscale 配置对象的 JSDoc typedef。
src/structs/Size.jsgameSize、baseSize、displaySize 使用的 Size 组件类。
src/dom/GetInnerHeight.js用于获取准确视口高度的 iOS 专用变通方案。
src/dom/GetScreenOrientation.js根据尺寸判断当前屏幕方向。

评论

支持 Markdown