59

I use bootstrap. I want the user to be able to choose the canvas size while keeping the design screen responsive within the div.

<div class="row">
  <div class="col-xs-2 col-sm-2" id="border">content left</div>
  <div class="col-xs-6 col-sm-6" id="border">
    Width <input type="number" class="form-control"><br>
  Height <input type="number" class="form-control"><br>
  canvas
  <canvas id="canvas" width="500" height="300">  
  </canvas>
  
  </div>
  <div class="col-xs-2 col-sm-2" id="border">content right</div>
</div>

How can I limit the size of the canvas to the size of the div?

I do not know if it will be necessary to use JavaScript.


Edit

It should be taken into account that the width and height values are entered by the user and the canvas must be in div proportional in size

https://jsfiddle.net/1a11p3ng/2/

djvg
  • 11,722
  • 5
  • 72
  • 103
Gislef
  • 1,555
  • 3
  • 14
  • 37

12 Answers12

64

You can have a responsive canvas in 3 short and simple steps:

  1. Remove the width and height attributes from your <canvas>.

    <canvas id="responsive-canvas"></canvas>
    
  2. Using CSS, set the width of your canvas to 100%.

    #responsive-canvas {
      width: 100%;
    }
    
  3. Using JavaScript, set the height to some ratio of the width.

    var canvas = document.getElementById('responsive-canvas');
    var heightRatio = 1.5;
    canvas.height = canvas.width * heightRatio;
    
Faheel
  • 2,435
  • 24
  • 33
33

To change width is not that hard. Just remove the width attribute from the tag and add width: 100%; in the css for #canvas

#canvas{
  border: solid 1px blue;  
  width: 100%;
}

Changing height is a bit harder: you need javascript. I have used jQuery because i'm more comfortable with.

you need to remove the height attribute from the canvas tag and add this script:

  <script>
  function resize(){    
    $("#canvas").outerHeight($(window).height()-$("#canvas").offset().top- Math.abs($("#canvas").outerHeight(true) - $("#canvas").outerHeight()));
  }
  $(document).ready(function(){
    resize();
    $(window).on("resize", function(){                      
        resize();
    });
  });
  </script>

You can see this fiddle: https://jsfiddle.net/1a11p3ng/3/

EDIT:

To answer your second question. You need javascript

0) First of all i changed your #border id into a class since ids must be unique for an element inside an html page (you can't have 2 tags with the same id)

.border{
  border: solid 1px black;
}

#canvas{
  border: solid 1px blue;  
  width: 100%;
}

1) Changed your HTML to add ids where needed, two inputs and a button to set the values

<div class="row">
  <div class="col-xs-2 col-sm-2 border">content left</div>
  <div class="col-xs-6 col-sm-6 border" id="main-content">
    <div class="row">
      <div class="col-xs-6">
        Width <input id="w-input" type="number" class="form-control">
      </div>
      <div class="col-xs-6">
        Height <input id="h-input" type="number" class="form-control">
      </div>
      <div class="col-xs-12 text-right" style="padding: 3px;">
        <button id="set-size" class="btn btn-primary">Set</button>
      </div> 
    </div>
    canvas
    <canvas id="canvas"></canvas>

  </div>
  <div class="col-xs-2 col-sm-2 border">content right</div>
</div>

2) Set the canvas height and width so that it fits inside the container

$("#canvas").outerHeight($(window).height()-$("#canvas").offset().top-Math.abs( $("#canvas").outerHeight(true) - $("#canvas").outerHeight()));

3) Set the values of the width and height forms

$("#h-input").val($("#canvas").outerHeight());
$("#w-input").val($("#canvas").outerWidth());

4) Finally, whenever you click on the button you set the canvas width and height to the values set. If the width value is bigger than the container's width then it will resize the canvas to the container's width instead (otherwise it will break your layout)

    $("#set-size").click(function(){
        $("#canvas").outerHeight($("#h-input").val());
        $("#canvas").outerWidth(Math.min($("#w-input").val(), $("#main-content").width()));
    });

See a full example here https://jsfiddle.net/1a11p3ng/7/

UPDATE 2:

To have full control over the width you can use this:

<div class="container-fluid">
<div class="row">
  <div class="col-xs-2 border">content left</div>
  <div class="col-xs-8 border" id="main-content">
    <div class="row">
      <div class="col-xs-6">
        Width <input id="w-input" type="number" class="form-control">
      </div>
      <div class="col-xs-6">
        Height <input id="h-input" type="number" class="form-control">
      </div>
      <div class="col-xs-12 text-right" style="padding: 3px;">
        <button id="set-size" class="btn btn-primary">Set</button>
      </div> 
    </div>
      canvas
    <canvas id="canvas">

    </canvas>

  </div>
  <div class="col-xs-2 border">content right</div>
</div>
</div>
  <script>
   $(document).ready(function(){
    $("#canvas").outerHeight($(window).height()-$("#canvas").offset().top-Math.abs( $("#canvas").outerHeight(true) - $("#canvas").outerHeight()));
    $("#h-input").val($("#canvas").outerHeight());
    $("#w-input").val($("#canvas").outerWidth());
    $("#set-size").click(function(){
        $("#canvas").outerHeight($("#h-input").val());
      $("#main-content").width($("#w-input").val());
      $("#canvas").outerWidth($("#main-content").width());
    });
   });
  </script>

https://jsfiddle.net/1a11p3ng/8/

the content left and content right columns will move above and belove the central div if the width is too high, but this can't be helped if you are using bootstrap. This is not, however, what responsive means. a truly responsive site will adapt its size to the user screen to keep the layout as you have intended without any external input, letting the user set any size which may break your layout does not mean making a responsive site.

valepu
  • 3,136
  • 7
  • 36
  • 67
  • I think you're looking for `max-width` instead of `width` –  Jan 13 '16 at 17:54
  • Thanks, width and height are entered by the user, I updated my question – Gislef Jan 13 '16 at 18:52
  • @TinyGiant no since he's using bootstrap column layout, the canvas size will be proportional to the screen. Zeli this is an entirely different question though. I'll try to give an answer – valepu Jan 13 '16 at 20:48
  • I appreciate your response and explanation. But it's not working – Gislef Jan 14 '16 at 22:04
  • The input height in the console is running but the width does not work when the value changed – Gislef Jan 14 '16 at 22:08
  • as i wrote in my answer, the width won't go further than it's container because it'll break bootstrap grid layout, try to make it less than actual width – valepu Jan 15 '16 at 09:03
  • i can edit the answer to make it work with any width if that's what you need – valepu Jan 15 '16 at 09:07
  • 1
    Yes it must also be any width so that it is really responsive – Gislef Jan 19 '16 at 00:02
  • i have added what you asked in my answer (see "update 2") but let me tell you that's not what responsive means. – valepu Jan 19 '16 at 09:03
  • If you change thw width manualy, than the canvas width isn't any more responsive. – G.Georgiou Jun 11 '19 at 17:18
  • Yes it's explained in my answer if you read it all (my first answer was actually responsive, update 1 and 2 what was asked from the comments), but the manual change is what the asker actually wanted – valepu Jun 11 '19 at 20:51
32

The object-fit CSS property sets how the content of a replaced element, such as an img or video, should be resized to fit its container.

Magically, object fit also works on a canvas element. No JavaScript needed, and the canvas doesn't stretch, automatically fills to proportion.

canvas {
    width: 100%;
    object-fit: contain;
}
djvg
  • 11,722
  • 5
  • 72
  • 103
nicklundy
  • 446
  • 4
  • 3
  • 3
    This is by far the most simple solution but it may not have some legacy support. [JSFIDDLE HERE](https://jsfiddle.net/aspiringpro/91hvx83g/2/) – Marcus Reed Dec 31 '20 at 19:51
  • 1
    Omfg im like trying all the solutions from hours, and your simple ```object-fit: contain;``` worked THANK YOU – Simonsoft177 Jan 05 '21 at 17:09
  • This answers should get more upvotes! I was able to scale my canvas without needing to write any hacky JS. It's always nice when you can solve layout problems with native CSS. – Valentin Jun 14 '21 at 10:40
  • i gave it one up vote because it deserves more votes – AGrush Sep 01 '21 at 10:00
  • Simplest, works greats. A damn lifesaver. – Capagris Sep 28 '21 at 21:05
  • Are you sure it's not the `width: 100%` doing the trick here? It also maintains the canvas aspect ratio (`height` defaults to `auto`). For example, @MarcusReed's fiddle works just the same if you remove the `object-fit` line. – djvg May 05 '23 at 12:40
10
<div class="outer">
 <canvas id="canvas"></canvas>
</div>    
.outer {
 position: relative;
 width: 100%;
 padding-bottom: 100%;
}

#canvas {
 position: absolute;
 width: 100%;
 height: 100%;
}
Philipp Mitterer
  • 863
  • 9
  • 16
6

extending accepted answer with jquery

what if you want to add more canvas?, this jquery.each answer it

responsiveCanvas(); //first init
$(window).resize(function(){
  responsiveCanvas(); //every resizing
  stage.update(); //update the canvas, stage is object of easeljs

});
function responsiveCanvas(target){
  $(canvas).each(function(e){

    var parentWidth = $(this).parent().outerWidth();
    var parentHeight =  $(this).parent().outerHeight();
    $(this).attr('width', parentWidth);
    $(this).attr('height', parentHeight);
    console.log(parentWidth);
  })

}

it will do all the job for you

why we dont set the width or the height via css or style? because it will stretch your canvas instead of make it into expecting size

david valentino
  • 920
  • 11
  • 18
  • Sorry for the necro but doing this in regular JS with offsetWidth and offsetHeight infinitely scales the height. Any suggestions? – Kevin Toet Sep 24 '20 at 21:27
  • @KevinToet i think if mixing jquery and other progresive javascript framework wouldn't work because will overlap and the other framework will refresh the jquery result.. but might give a clue, if become infinite then it might be the parent height or width, since its the variable for the canvas to expand.. – david valentino Sep 25 '20 at 02:42
5

this seems to be working :

#canvas{
  border: solid 1px blue; 
  width:100%;
}
TheGrum
  • 51
  • 2
2

One of the best possible way to do this without even using JavaScript is to just put that canvas inside a div tag and then display block (Note: width:100%; height:100%; is completely optional).

Run the snippet below to see how it works.....

.container {
  display: block;
  margin: auto;
  height: auto;
}

#canvas {
  width: 100%;
  height: 100%;
  border: 1px solid black;
}
<div class="container">
  <!-- width and height are set just to show you it actually works-->
  <canvas id="canvas" width=864 height=480></canvas>
</div>
anzeqar
  • 21
  • 4
1

There's a better way to do this in modern browsers using the vh and vw units.

vh is the viewport height.

So you can try something like this:

<style>
canvas {
  border: solid 2px purple;
  background-color: green;
  width: 100%;
  height: 80vh;
}
</style>

This will distort the aspect ration.

You can keep the aspect ratio by using the same unit for each. Here's an example with a 2:1 aspect ratio:

<style>
canvas {
  width: 40vh;
  height: 80vh;
}
</style>
Michael Cole
  • 15,473
  • 7
  • 79
  • 96
1

try using max-width: 100%; on your canvas.

canvas {
  max-width: 100%;
}
1

Hi I know this post has been going on for a while, but I had a very similar problem with my canvas. like mentioned as the viewport gets resized and so does the elements within the window. This can be done using css, however it doesnt quite apply to the elements within the canvas. Now if your canvas context is 2D this is the solution that works for me... This can be used as a method.

class ParentObject{
 constructor()
        /**
        * @this this._width - Private width variable that would store the size of the window width
        */
        this._width = Number();
       
        /**
         * @this this._height - Private Height variable that would store the height of the window
         */
        this._height= Number();
        
        /**
         * Calls the getCanvasDimensions method
         */
        this.getCanvasDimensions();
       
        //Getting the Width and Height as soon as the Window loads
        window.addEventListener('load',()=>{
            this.getCanvasDimensions()
        })
       
        //As the window is resized we are getting the new Canvas Dimensions
        window.addEventListener('resize',()=>{
            this.getCanvasDimensions();

getCanvasDimensions() {
        // Width is determined by the css value for the viewport width this is then respected by the device pixel ratio. This is then used to set the canvas.width value
        this._width = Math.round((Number(getComputedStyle(canvas).getPropertyValue('width').slice(0,-2))/devicePixelRatio) * devicePixelRatio);
        //Setting the canvas width 
        canvas.width = this._width
        
        // height is determined by the css value for the viewport height this is then respected by the device pixel ratio. This is then used to set the canvas.height value
        this._height = Math.round((Number(getComputedStyle(canvas).getPropertyValue('height').slice(0,-2))/devicePixelRatio) * devicePixelRatio);
        //Setting the canvas height
        canvas.height = this._height
        
    }
get width(){
        //This sets the width to the private _width value
        return this._width
        
    }
get height(){
        //This sets the height to the private _height value
        return this._height
    }

As a function on the global scope:

let width,height
function responsiveCanvas(){
   let w = Math.round((Number(getComputedStyle(canvas).getPropertyValue('width').slice(0,-2))/devicePixelRatio) * devicePixelRatio);
 width = canvas.height = w;

let h  = Math.round((Number(getComputedStyle(canvas).getPropertyValue('height').slice(0,-2))/devicePixelRatio) * devicePixelRatio);
height = canvas.height = h;
}
window.addEventListener('load',responsiveCanvas)
window.addEventListener('resize',responsiveCanvas)

Then just use the width and height variables to reference the sizes. Anyways I hope that this helps someone out.

Dr Gaud
  • 97
  • 3
1

Try changing the width of the canvas to be equal to it's parent element.

 <template>
    <span class="Wrapper">
       <canvas id="myChart"  ></canvas> 
    </span>
 </template>
 <script>
     const ctx = document.getElementById('myChart');
     ctx.width = ctx.parentElement.offsetWidth;
     ctx.height = ctx.parentElement.offsetHeight;
 </script>
Tiago Rangel
  • 1,159
  • 15
  • 29
0

This preserves the aspect ratio specified on the <canvas> element and shrinks the canvas if it does not fit.

<canvas width="500" height="300"></canvas>
canvas {
  width: 100%;
  max-width: max-content;
}

A slightly less minimal example:

const canvas = document.querySelector('canvas');
const input = document.querySelector('input');
const option = document.querySelector('option');


input.addEventListener('change', (event) => {
  canvas.width = event.target.value;
  drawCircle();
});

// init
input.value = window.innerWidth;
option.value = window.innerWidth;
drawCircle();

function drawCircle() {
  // circle allows quick verification of proper aspect ratio
  const ctx = canvas.getContext('2d');
  ctx.arc(canvas.width / 2, canvas.height / 2, 50, 0, 2 * Math.PI);
  ctx.strokeStyle = 'white';
  ctx.lineWidth = 5;
  ctx.stroke();
}
canvas {
  width: 100%;
  max-width: max-content;
  /* purely cosmetic */
  background-color: steelblue;
}
canvas width
<input type="range" min="100" max="2000" list="ticks"><br>
<datalist id="ticks"><option></option></datalist>
<canvas width="1000" height="200"></canvas>
djvg
  • 11,722
  • 5
  • 72
  • 103