如何计算动态浏览器窗口可见空间,适配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效果。具体步骤:
- 扣除页面其他元素占用的高度:页面顶部的
h1和hr会占用部分视窗高度,计算可用高度时需要减去这部分,避免Canvas被挤压出视窗。 - 计算两种缩放场景的尺寸:
- 场景1:宽度铺满视窗,计算对应高度
- 场景2:高度铺满可用空间,计算对应宽度
- 选择不超出视窗的尺寸:比较两种场景的尺寸,选择不会超出视窗的那个作为Canvas的最终样式尺寸。
- 可选:垂直居中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
相关产品推荐
相关产品推荐

