You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何计算动态浏览器窗口可见空间,适配Canvas固定宽高比?

问题描述

我正在开发一个配对小游戏,用Canvas构建游戏界面,需要让Canvas保持1.618的宽高比,同时随窗口大小自适应缩放,且不能超出可视窗口范围(避免出现滚动条)。目前设置canvas.style.width = "100%"能让宽度匹配窗口,但按比例计算高度时,窗口宽度较大时Canvas会超出底部,出现滚动条。

现有代码

index.html

<html lang="zxx">

<head>
    <meta charset="utf-8">
    <title> Pairs </title>
    <link rel="stylesheet" type="text/css" href="Style.css">
</head>

<body>
    <h1> Pairs </h1>
    <hr>
    <div class="GameDiv" id="gameDiv">
        <canvas>Sorry, your browser dosen't support HTML Canvas.</canvas>
    </div>
</body>

<script src="game.js"></script>
</html>

game.js

// Canvas set-up
const canvas = document.querySelector("canvas");
const canvasDims = { width: 300, height: 185 };
const gameDiv = document.getElementById("gameDiv");
const ctx = canvas.getContext("2d");

// dynamic screen fit
function resizeCanvas() {
    let dims = {
        width: window.innerWidth,
        height: (canvasDims.height / canvasDims.width) * window.innerWidth
    };

    canvas.style.aspectRatio = canvasDims.width / canvasDims.height;

    if (window.innerHeight > dims.height) {
        canvas.style.width = "100%";
        canvas.style.height = "initial";
        canvas.style.top = `calc(50% - ${dims.height + "px"}/2)`;
    } else {
        // 问题出在这里
        canvas.style.height = screen.height;
        canvas.style.width = screen.height + (screen.height/canvas.style.aspectRatio);
        canvas.style.top = "initial";
    }
    
}

window.addEventListener("resize", resizeCanvas);
resizeCanvas();
解决方案

核心思路是计算视窗的可用空间,选择能完整容纳Canvas的最大尺寸(保持宽高比),类似CSS的object-fit: contain效果。具体步骤:

  1. 扣除页面其他元素占用的高度:页面顶部的h1和hr会占用部分视窗高度,计算可用高度时需要减去这部分,避免Canvas被挤压出视窗。
  2. 计算两种缩放场景的尺寸:
    • 场景1:宽度铺满视窗,计算对应高度
    • 场景2:高度铺满可用空间,计算对应宽度
  3. 选择不超出视窗的尺寸:比较两种场景的尺寸,选择不会超出视窗的那个作为Canvas的最终样式尺寸。
  4. 可选:垂直居中Canvas:如果需要让Canvas在可用空间内垂直居中,调整margin即可。

修改后的game.js代码:

// Canvas set-up
const canvas = document.querySelector("canvas");
const canvasDims = { width: 300, height: 185 };
const gameDiv = document.getElementById("gameDiv");
const ctx = canvas.getContext("2d");

// 目标宽高比(宽/高)
const aspectRatio = canvasDims.width / canvasDims.height;

// dynamic screen fit
function resizeCanvas() {
    // 获取头部元素(h1 + hr)的总高度
    const headerTotalHeight = document.querySelector('h1').offsetHeight + document.querySelector('hr').offsetHeight;
    // 视窗中留给Canvas的可用高度
    const availableHeight = window.innerHeight - headerTotalHeight;

    let targetWidth, targetHeight;

    // 计算宽度铺满视窗时需要的高度
    const requiredHeightForFullWidth = window.innerWidth / aspectRatio;

    if (requiredHeightForFullWidth <= availableHeight) {
        // 宽度铺满视窗,高度按比例计算(不会超出可用高度)
        targetWidth = window.innerWidth;
        targetHeight = requiredHeightForFullWidth;
    } else {
        // 高度铺满可用空间,宽度按比例计算(不会超出视窗宽度)
        targetHeight = availableHeight;
        targetWidth = availableHeight * aspectRatio;
    }

    // 设置Canvas的样式尺寸(控制显示大小)
    canvas.style.width = `${targetWidth}px`;
    canvas.style.height = `${targetHeight}px`;

    // 可选:让Canvas在可用空间内垂直居中
    canvas.style.marginTop = `${(availableHeight - targetHeight) / 2}px`;

    // 重要:同步Canvas的绘图尺寸(避免画面拉伸模糊)
    // 若需要适配高清屏,可乘以window.devicePixelRatio
    canvas.width = targetWidth;
    canvas.height = targetHeight;
}

window.addEventListener("resize", resizeCanvas);
resizeCanvas();

额外说明

  • 如果你的页面有其他边距或padding,需要在计算可用高度时一并扣除,比如body的margin。
  • 若要适配高清屏幕,可将canvas.width和canvas.height乘以window.devicePixelRatio,然后用CSS样式尺寸设置为目标宽高,避免绘图模糊。

内容的提问来源于stack exchange,提问作者Henry Grantham-Smith

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 08:02:53