javascriptcsscanvaswindow-resize

Stretch canvas 100% width but contain in window


I have a canvas that I am stretching with CSS using width: 100%;. It works great to maintain aspect ratio because the height adjusts automatically. But I want it to stop growing when the window is resized so wide that the canvas' height would be below the fold. Is there an easy CSS property-value for this?

I thought the object-fit property could do that, but I think I'm not understanding something.

Please also note that I have 2 canvases layered on top of each other, so the solution shouldn't mess with the absolute positioning they have to their 'position: relative;' parent container.

EDIT: Here is a simplified version of my setup. Canvas sizes are initalized with JavaScript, and then the CSS width property stretches it to the full width of the page. Please be mindful of the control bar I put at the top because it takes up space as well.

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Canvas Demo</title>
    <style>
        canvas{
            width: 100%;
            border: 1px solid black;
            background-color: #eee;
        }
        #controls{ 
            border: 1px solid black;
            padding: 50px 20px;
        }
    </style>
    <script>
    
        function init(){
            let canvas1 = document.getElementById("canvas_environment");
            canvas1.width = 1200;
            canvas1.height = 1000;
            let canvas2 = document.getElementById("canvas_agents");
            canvas2.width = 1200;
            canvas2.height = 1000;
        }

    </script>
    
</head>

<body onload="init()">

    <!-- Control bar on top -->
    <div id="controls">
        <button>GO</button>
    </div>
    
    <!-- Two canvases overlaid to avoid redrawing the environment every frame -->
    <div style="position: relative;">

        <canvas id="canvas_environment"
            style="position: absolute; left: 0; top: 0; z-index: 0;">
        </canvas>
        
        <canvas id="canvas_agents"
          style="position: absolute; left: 0; top: 0; z-index: 1;">
        </canvas>

    </div>


</body>
</html>

Solution

  • To account for the button space above the canvases this snippet puts both the button and canvases into a container which is then divided up using grid - so the canvases can take the remaining vertical space.

    Each canvas is given max width and height of 100% (which will equate to the width and height of the cell) and then object-fit is used to ensure it adjusts appropriately.

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
      <title>Canvas Demo</title>
      <meta name="viewport" content="width=device-width, initial-scale=1" />
      <style>
        * {
          margin: 0;
          box-sizing: border-box;
        }
        
        canvas {
          max-width: 100%;
          max-height: 100%;
          border: 1px solid black;
          background-color: cyan;
        }
        
        #controls {
          border: 1px solid black;
          padding: 50px 20px;
        }
        
        .container {
          display: grid;
          grid-template-rows: auto 1fr;
          grid-template-columns: 1fr;
          width: 100vw;
          height: 100vh;
        }
      </style>
      <script>
        function init() {
          let canvas1 = document.getElementById("canvas_environment");
          canvas1.width = 1200;
          canvas1.height = 1000;
          let canvas2 = document.getElementById("canvas_agents");
          canvas2.width = 1200;
          canvas2.height = 1000;
        }
      </script>
    
    </head>
    
    <body onload="init()">
      <div class="container">
        <!-- Control bar on top -->
        <div id="controls">
          <button>GO</button>
        </div>
    
        <!-- Two canvases overlaid to avoid redrawing the environment every frame -->
        <div style="position: relative;">
    
          <canvas id="canvas_environment" style="position: absolute; left: 0; top: 0; z-index: 0;">
            </canvas>
    
          <canvas id="canvas_agents" style="display: none; position: absolute; left: 0; top: 0; z-index: 1;">
            </canvas>
    
        </div>
      </div>
    
    </body>
    
    </html>