【问题标题】:Canvas on resize - keep a maximal width or height without stretching/padding调整大小的画布 - 在不拉伸/填充的情况下保持最大宽度或高度
【发布时间】:2016-12-18 05:16:50
【问题描述】:

我对在 javascript 中使用 canvas 的 html5 游戏有疑问。

我希望向播放器显示的画布包含 1920 的 canvas.width 或 1080 的 canvas.height,以便看不到填充。这意味着如果玩家使用其他比例调整屏幕大小,我不想保持(最佳)游戏比例 16/9,但我想保持视口比例 16/9 以避免拉伸(使用 canvas.style. ***)

让我以一个可调整屏幕大小的玩家为例(window.innerWidth = 500 和 window.innerHeight = 1600),如 1600/500 > 1920 / 1080,我希望玩家可以看到游戏的 1920宽度和“小于 1080” 的游戏高度,防止视口拉伸。

Agar.io 是另一个很好的例子。

非常感谢!

【问题讨论】:

标签: javascript css html canvas render


【解决方案1】:

适合,但如果方面不匹配,将在侧面或顶部底部留下空白区域。

var scale = Math.min(innerWidth / canvas.width, innerHeight / canvas.height);

或填充,但如果方面不匹配将剪切画布

var scale = Math.max(innerWidth / canvas.width, innerHeight / canvas.height);

用于显示 1600 x 500 和画布 1920 x 1080

1600 / 1920 = 0.8333;
500 / 1080 = 0.463;

因此适合画布的 0.463 * (1920,1080) = (889,500) 两侧为空

并且填充画布将被0.8333 * (1920,1080) = (1600,900)顶部和底部剪裁。

更多关于比例和合身的信息可以在下面找到。

如果您要缩放以填充画布,则需要考虑剪切区域并找到画布左上角的偏移量(这将在页面之外)。

var leftOffset = 0;
var topOffset = 0;
var canW = scale * canvas.width;
var canH = scale * canvas.height;
if(canW > innerWidth ){
    leftOffset = ((canW - innerWidth) / canW) * 1920 / 2;
}else
if(canH > innerHeight ){
    topOffset = ((canH - innerHeight) / canH) * 1080 / 2;
}

您的画布将填充页面 innerWidth 和 innerHeight 但您需要偏移所有渲染。这可以通过将变换设置为正确的偏移量来完成

ctx.setTransform(1,0,0,1,-leftOffset, -topOffset);

画布显示尺寸将为

canvas.style.width = innerWidth + "px"; 
canvas.style.height = innerHeight + "px";

画布分辨率为

canvas.width = 1920 - (leftOffset * 2);
canvas.height = 1080 - (topOffset * 2);

以下是实际代码示例。非常懒惰的编码,只是为了显示它的工作不应该在调整大小时创建和破坏画布。还有as fiddle,因为它有可调整大小的面板可供测试。

 var canvas;
var createCanvas = function(){
    if(canvas !== undefined){
         document.body.removeChild(canvas);
    
    }
    var canWidth = 1920;
    var canHeight = 1080;

    var scale = Math.max(innerWidth /canWidth, innerHeight / canHeight);
    var leftOffset = 0;
    var topOffset = 0;
    var canW = scale * canWidth;
    var canH = scale * canHeight;
    if(canW > innerWidth ){
        leftOffset = ((canW - innerWidth) / canW) * canWidth / 2;
    }else
    if(canH > innerHeight ){
        topOffset = ((canH - innerHeight) / canH) * canHeight / 2;
    }

    canvas = document.createElement("canvas");
    canvas.style.position = "absolute";
    canvas.style.top = "0px";
    canvas.style.left = "0px";
    canvas.style.width = innerWidth + "px"; 
    canvas.style.height = innerHeight + "px";
    canvas.width = canWidth - (leftOffset * 2);
    canvas.height = canHeight - (topOffset * 2);
    document.body.appendChild(canvas);
    var ctx = canvas.getContext("2d");


    ctx.setTransform(1,0,0,1,-leftOffset, -topOffset);
    ctx.beginPath();
    ctx.arc(canWidth / 2, canHeight/2, 400,0,Math.PI * 2);
    ctx.stroke();
}

window.addEventListener('resize',createCanvas);
createCanvas();

缩放以适应

表示整个图像将可见,但如果图像与画布的纵横比不同,则侧面或顶部和底部可能会有一些空白空间。该示例显示缩放以适合的图像。两侧的蓝色是由于图像与画布的外观不同。

缩放以填充

表示对图像进行缩放,以使所有画布像素都被图像覆盖。如果图像方面与画布不同,则图像的某些部分将被剪裁。该示例显示缩放以填充的图像。请注意图像的顶部和底部是如何不再可见的。

示例比例以适应

var image = new Image();
image.src = "imgURL";
image.onload = function(){
    scaleToFit(this);
}

function scaleToFit(img){
    // get the scale
    var scale = Math.min(canvas.width / img.width, canvas.height / img.height);
    // get the top left position of the image
    var x = (canvas.width / 2) - (img.width / 2) * scale;
    var y = (canvas.height / 2) - (img.height / 2) * scale;
    ctx.drawImage(img, x, y, img.width * scale, img.height * scale);
}

要填充的示例比例

var image = new Image();
image.src = "imgURL";
image.onload = function(){
    scaleToFill(this);
}

function scaleToFill(img){
    // get the scale
    var scale = Math.max(canvas.width / img.width, canvas.height / img.height);
    // get the top left position of the image
    var x = (canvas.width / 2) - (img.width / 2) * scale;
    var y = (canvas.height / 2) - (img.height / 2) * scale;
    ctx.drawImage(img, x, y, img.width * scale, img.height * scale);
}

这两个函数之间的唯一区别是获取比例。拟合使用最小拟合比例,填充使用最大拟合比例。

【讨论】:

  • 谢谢!我成功“缩放填充”,但现在我无法将图像居中(x,y)......你能帮帮我吗?这是调整大小的实际代码:canvas.height = 1080; canvas.width = 1920; var scale = Math.max(window.innerWidth / canvas.width; window.innerHeight / canvas.height); canvas.style.width = 1920 * scale + "px"; canvas.style.height = 1080 * scale + "px";
  • @Miyud 我会在答案中添加更多内容,以便您获得正确的渲染区域
  • @Miyud 好的,这是我的想法,必须测试一下。当我发现问题时会更新
  • 您的答案适用于:canvas.style.width = 1920 * scale + "px"; canvas.style.height = 1080 * scale + "px"; 并保留canvas.width = 1920; canvas.height = 1080;。非常感谢您的快速回复:)
  • @androiddeveloper 你所说的“fit-center”就是调用 scaleToFit。这意味着图像尽可能大,并且可以看到所有像素。
猜你喜欢
  • 2014-09-04
  • 1970-01-01
  • 1970-01-01
  • 2013-09-07
  • 1970-01-01
  • 1970-01-01
  • 2013-04-28
  • 2015-05-22
  • 2017-12-08
相关资源
最近更新 更多