0

If I have a canvas element that is a certain width and height, e.g.

<canvas id="display" width="900" height="650" tabindex=0></canvas>

and I want to scale up the width and height of the canvas to as large as possible while being contained within the viewport without changing the aspect ratio or increasing the overall area for objects to be displayed within the canvas.

This means that if a window is width 2200 and height 1300, both the display width and height of the canvas will be doubled (and no larger because otherwise the height could not be contained) and the canvas will have the new dimensions 1800 and 1300.

However, I am scaling up the CSS display height of the canvas element, not the canvas itself, which means that objects inside the canvas should also have their dimensions increased.

canvas {
  width: ...
  height: ...
}

It would be even better if the canvas can rescale itself when the window is resized, perhaps with an event listener.

Is there any way to do this in JS?

2 Answers2

1

Scale to fit

You have to define the aspect. This can be done by just defining a default size.

const defWidth = 1920;
const defHeight = 1080;

Then you can listen to the window resize event and resize the canvas to fit.

function resizeEventHandler(){
    // get the max size that fits both width and height by finding the min scale
    var canvasScale = Math.min(innerWidth / defWidth, innerHeight / defHeight);
    // or for max size that fills
    // canvasScale = Math.max(innerWidth / defWidth, innerHeight / defHeight);

    // now set canvas size and resolution to the new scale
    canvas.style.width = (canvas.width = Math.floor(defWidth * canvasScale)) + "px";
    canvas.style.height = (canvas.height = Math.floor(defHeight * canvasScale)) + "px";
}

Because the canvas resolution must be an integer the aspect may be out by a pixel. But there is nothing that can be done about that.

Community
  • 1
  • 1
Blindman67
  • 51,134
  • 11
  • 73
  • 136
  • While this did resize the canvas, it did not do what I wanted because it did not change the size of objects within the canvas, allowing users to see more if they resize their window to be larger. I need something that scales up the objects so the size ratio is still the same. –  Dec 04 '16 at 07:03
  • @DaemonOfTheWest Just use the `canvasScale` to scale up any rendering you do. eg `ctx.setTransform(canvasScale,0,0,canvasScale,0,0);` – Blindman67 Dec 04 '16 at 08:57
0

Ended up finding my own solution, if there is a more elegant one available then please inform me.

// get maximum possible size for canvas while maintining aspect ratio
function resizeEventHandler(){
  var w = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
  var h = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
  if(w/canvas.width > h/canvas.height){
    canvas.style.width = "calc("+canvas.width+" / "+canvas.height+" * 100vh)";
    canvas.style.height = "calc(100vh)";
  } else {
    canvas.style.width = "calc(100vw)";
    canvas.style.height = "calc("+canvas.height+" / "+canvas.width+" * 100vw)";
  }
}
resizeEventHandler();
window.addEventListener("resize", resizeEventHandler);
  • This will stretch your canvas drawings by CSS, which can result in ugly antialiasing artifacts. – Kaiido Dec 04 '16 at 09:02
  • @Kaiido antialiasing effects can be controlled with CSS. [Like this](http://stackoverflow.com/questions/8597081/how-to-stretch-images-with-no-antialiasing) –  Dec 05 '16 at 01:22
  • Hum not really. First, the answer you linked to is from 2011 ! And it doesn't use CSS at all. Nowadays (actually since quite long), you can use `ctx.imageSmoothingEnabled = false` instead of doing this horrible `(get/put)ImageData`. Second, there is indeed an [`image-rendering`](https://developer.mozilla.org/en/docs/Web/CSS/image-rendering) CSS property, that will let you say that you want a closest-neighbor algo instead of antialias, but still, results won't be what you want to be. You have to set the canvas' width and height properties, or CSS will try to fill the gaps in the pixel matrix. – Kaiido Dec 05 '16 at 01:31