codingmaster398
codingmaster398

Reputation: 257

Canvas - Keeping aspect ratio while also keeping pixel amount

I have a canvas like so: The canvas with a banana duck and some cheese But when resizing the window and redrawing, you can get anything in between. The duck with only a few cheeses

I keep the aspect ratio with this code:

function setCanvasSize(){
    ctx.canvas.width = (window.innerWidth)*0.8;
    ctx.canvas.height= (window.innerWidth)*0.5;
}
setCanvasSize()
$(window).resize(function(){
    setCanvasSize()
})

and that keeps it nice and clean, the same-looking size, but how many pixels are in there changes, so when my program draws, it's always in a different spot, although the aspect is the same, so it looks really weird.

Any way that I can somehow tweak how I draw things, or a way to keep how many pixels there are?

Edit: code snippet to reproduce

const ctx = document.getElementById('game').getContext("2d");

$('#game').css('width', '100%');
$(window).resize(function(){
    $('#game').height($('#game').width() / 2.031);
 });
 
 const imageAt=((href,x,y,sizex,sizey)=>{
      var img = new Image();
      img.addEventListener('load', function() {
          ctx.drawImage(img, x, y, sizex, sizey);
      }, false);
      img.src = href
  })
imageAt('https://b.thumbs.redditmedia.com/bZedgr0gq7RQBBnVYVc-Nmzdr-5vEUg4Dj8nTrMb7yA.png',0,0,50,50)
#game{
    padding-left: 0;
    padding-right: 0;
    display: block;
    margin: 0;
    border:5px solid green;
    padding:0;
}

#vertical-center {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  background: black;
  padding:0;
}

body{
  background-color: black;
  padding:0;
}
<html>
    <head>
        <title>Tiled Canvas Engine Test 1</title>
        <meta name="viewport" content="width=device-width, initial-scale=0.75">
        <link rel="stylesheet" href="./index.css">
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
        <script src="engine.js"></script>
        <script defer src="script.js"></script>
    </head>
    <body>
        <div id="vertical-center">
            <canvas id="game"></canvas>
        </div>
    </body>
</html>

Upvotes: 0

Views: 1497

Answers (1)

Paul Wheeler
Paul Wheeler

Reputation: 20150

In order to render properly the canvas element needs to have its width and height attributes set explicitly. This will define the viewport into which graphics will be draw. The style attributes for width and height which is what jQuery sets via the width() and height() functions will only stretch the viewport to fit. (Note: for high DPI displays, i.e. Retina, you will want to double the dimensions when specifying the attributes).

The critical change is this:

  // Updates the actual width and height attributes
  canvas.width = $('#game').width() * window.devicePixelRatio;
  canvas.height = $('#game').height() * window.devicePixelRatio;

The snippet below also handles clearing the canvas, redrawing the image, and saving the Image once loaded:

const ctx = document.getElementById('game').getContext("2d");

$('#game').css('width', '100%');
$(document).ready(handleResize);
$(window).resize(handleResize);

function handleResize() {
  // sets the style.height and style.width for the canvas
  $('#game').height($('#game').width() / 2.031);
  let canvas = $('#game').get(0);

  // Updates the actual width and height attributes
  canvas.width = $('#game').width() * window.devicePixelRatio;
  canvas.height = $('#game').height() * window.devicePixelRatio;

  // redraw
  if (img) {
    redraw();
  }
}

var img;
const imageAt = ((href, x, y, sizex, sizey) => {
  img = new Image();
  img.addEventListener('load', function() {
    ctx.drawImage(img, x, y, sizex, sizey);
  }, false);
  img.src = href
})
imageAt('https://b.thumbs.redditmedia.com/bZedgr0gq7RQBBnVYVc-Nmzdr-5vEUg4Dj8nTrMb7yA.png', 0, 0, 50 * window.devicePixelRatio, 50 * window.devicePixelRatio)

function redraw() {
  ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
  ctx.drawImage(img, 0, 0, 50 * window.devicePixelRatio, 50 * window.devicePixelRatio);
}
#game {
  padding-left: 0;
  padding-right: 0;
  display: block;
  margin: 0;
  border: 5px solid green;
  padding: 0;
}

#vertical-center {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  background: black;
  padding: 0;
}

body {
  background-color: black;
  padding: 0;
}
<html>

<head>
  <title>Tiled Canvas Engine Test 1</title>
  <meta name="viewport" content="width=device-width, initial-scale=0.75">
  <link rel="stylesheet" href="./index.css">
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
  <script src="engine.js"></script>
  <script defer src="script.js"></script>
</head>

<body>
  <div id="vertical-center">
    <canvas id="game"></canvas>
  </div>
</body>

</html>

Upvotes: 1

Related Questions