Resize HTML5 canvas to fit window
I believe I have found an elegant solution to this:
JavaScript
/* important! for alignment, you should make things
* relative to the canvas' current width/height.
*/
function draw() {
var ctx = (a canvas context);
ctx.canvas.width = window.innerWidth;
ctx.canvas.height = window.innerHeight;
//...drawing code...
}
CSS
html, body {
width: 100%;
height: 100%;
margin: 0;
}
Hasn't had any large negative performance impact for me, so far.
How to resize the canvas using JavaScript?
The following jsfiddle demonstrates how to resize the canvas. https://jsfiddle.net/intrinsica/msj0cwx3/
(function() { var canvas = document.getElementById('canvas'), context = canvas.getContext('2d');
// Event handler to resize the canvas when the document view is changed window.addEventListener('resize', resizeCanvas, false);
function resizeCanvas() { canvas.width = window.innerWidth; canvas.height = window.innerHeight;
// Redraw everything after resizing the window drawStuff(); } resizeCanvas();
function drawStuff() { // Do drawing here context.strokeRect(10,10, 230,100); context.font = '16px serif'; context.fillText('The canvas is the blue', 30, 30); context.fillText('background color seen here.', 30, 50); context.fillText('It will resize if the window', 30, 70); context.fillText('size is adjusted.', 30, 90); }})();
* { margin:0; padding:0; } /* to remove the top and left whitespace */
html, body { width:100%; height:100%; } /* just to be sure these are full screen*/
canvas { background: #77f; /* to show the canvas bounds */ display:block; /* to remove the scrollbars */}
<canvas id="canvas"></canvas>
Add CSS Resize Property to Canvas Element
No, CSS resize
is not applicable to inline elements. <canvas>
, just like <img>
is an inline element. Thus, you cannot use this property on canvases. See MDN Note
Note that you could wrap you canvas inside a resizeable block container and make your canvas to display at 100%, but remember this is just for display. This will stretch the actual pixel content of your canvas image.
const ctx = canvas.getContext('2d');ctx.arc(25,25,25,0,Math.PI*2);ctx.fill();
.resize-me { display: inline-block; border: 1px solid; resize: both; width: 50px; height: 50px; overflow: hidden;}canvas { width: 100%; height: 100%; pointer-events: none; /* Chrome needs this oO */}
<div class="resize-me"> <canvas id="canvas" width="50" height="50"></canvas></div>
Resizing a canvas element via dragging the edges
- Put your fabric.js canvas in a wrapper div.
- Make the wrapper resizable. Here I'm using CSS resize. It only adds a bottom-left corner as a resize control but it's good enough for the sake of the demo. To have all the edges as controls you can try something like this.
- Detect the wrapper's size change. Ideally, you would use something like ResizeObserver. However, since browser support is still about 80% at the time of posting this, you might feel the need to use a polyfill or write something specific to your case. A simple
setInterval
with size check might prove to be sufficient. - When the wrapper's size changes, set new fabric.js canvas dimensions via
setWidth()
andsetHeight()
.
const canvas = new fabric.Canvas('c')
canvas.add(new fabric.Rect({
width: 100,
height: 100,
fill: 'red',
left: 100,
top: 50,
}))
const canvasWrapper = document.getElementById('wrapper')
// initial dimensions
canvasWrapper.style.width = '300px'
canvasWrapper.style.height = '150px'
let width
let height
setInterval(() => {
const newWidth = canvasWrapper.clientWidth
const newHeight = canvasWrapper.clientHeight
if (newWidth !== width || newHeight !== height) {
width = newWidth
height = newHeight
canvas.setWidth(newWidth)
canvas.setHeight(newHeight)
}
}, 100)
#wrapper {
border: solid 1px black;
resize: both;
overflow: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.2/fabric.min.js"></script>
<div id="wrapper">
<canvas id="c"></canvas>
</div>
Resize canvas element when parent changes its width/height
The problem is that there is not (yet) a good way to do this. Instinctively you would look for a resize
event on an element. But that can only be applied to the window
as explained here on MDN.
After doing some research and looking at other threads I found a couple of options for you to consider.
Continuous loop
Draw the image in a recursive function which will also check the imgDiv
's width and height every time it draws itself.
CSS:
canvas {
width: 100%;
height: 100%;
}
JS:
function draw(ctx, imgDiv, img) {
ctx.canvas.width = imgDiv.offsetWidth;
ctx.canvas.height = imgDiv.offsetheight;
ctx.drawImage(img, 0, 0);
requestAnimationFrame(() => {
draw(ctx, imgDiv, img);
});
}
img.onload = function() {
draw(ctx, imgDiv, img);
};
This option has the benefit of checking all the time it can, but I image it can be quite heavy in terms of performance. requestAnimationFrame
does improve performance by firing the function only when it is allowed in the speed of the framerate. So about 60 times per second on average.
ResizeObserver
(Warning: experimental technology)
This is one I have not seen anywhere yet. The ResizeObserver
is new and is in the family of the IntersectionObserver
, PerformanceObserver
and MutationObserver
. Like the name suggests it observers changes in elements that are resized and fires a callback when it detects a change.
It works like this:
const observer = new ResizeObserver(entries => {
entries.forEach(entry => {
// Update the canvas dimensions.
});
});
// Observer the image-div when it resizes.
observer.observe(imgDiv);
Since it is experimental I wouldn't recommend using it, although it would perfectly fit your scenario. More on browser support for this API her on Caniuse and an article on how to use it from Alligator.io.
Use the resize
event on window
This would only capture the scenario of changing the width of the window or document. And in case of your sidebar opening and closing it would need some extra tinkering.
window.addEventListener('resize', (event) => {
requestAnimationFrame(() => {
// Update the canvas dimensions.
});
});
Using requestAnimationFrame
here is recommended since the resize
event will be called on every pixel and can slow down performance.
Edit:
It seems that Artyomska solved the problem already. And although the issue is resolved I still want to share my research in case a future query may stumble on this thread.
Resizing a HTML canvas blanks its contents
You need to either resize the canvas with CSS or redraw the canvas after you resize it.
If you want to save the content of the canvas and redraw it, I can think of a few options:
- Use
context.getImageData
to grab the whole canvas, resize the canvas, then usecontext.putImageData
to redraw it at the new scale. - Create an
Image
object, setting the source to the result ofcanvas.toDataUrl()
, resize the canvas, then draw the image at the new scale. - call
context.setScale(xscale, yscale)
and call whatever function you used to draw the canvas originally. Assuming you set upxscale
andyscale
correctly, it will automatically scale everything to the new size. - Create a new canvas with the updated size and call
context.drawImage(oldCanvas, ...)
to copy the old canvas onto the new one. Then you would switch out the old canvas with the new one.
The first two options won't work if you have drawn an image from a different domain to the canvas at any time, and aren't supported by older implementations.
In my opinion, option 3 (redrawing the image at the new scale) is the best if it's possible. It's the only option that will keep your lines completely smooth and sharp, and it will always work (assuming you still have all the information to generate the image).
Resize div containing a canvas
You could add a CSS width of 100% to the canvas element.
While resizing a canvas via CSS is usually not a good practice as it stretches / shrinks the "image inside the canvas", if it's in sync with the canvas width & height properties there's nothing wrong with it.
You can solve that inline:
<div class="canvas-wrapper">
<canvas height="30px" style="width: 100%;"></canvas>
</div>
Or use whatever CSS selection method you prefer instead.
Dynamic resize Canvas; keep image on position
You want to set the width and height attributes of the canvas element, not the css width and height which only stretch the canvas, which defaults to 300x150 width/height. You can dynamically size the canvas element with JavaScript.
Here is a working example.
//get the elementsconst canvas = document.getElementById("canvas");const myDiv = document.getElementById("my-div");
//set the width and height attributes to the div width and heightfunction resize(){ canvas.width = myDiv.clientWidth; canvas.height = myDiv.clientHeight;}//on page resize, call resize()window.addEventListener("resize", resize, false);
//call resize() initially to set the canvas size correctlyresize();
//you can call resize() when your div changes size, dynamically resizing the canvas to the div
div { width: 50vw; height: 50vh; background-color: lightblue;}canvas { background-color: rgba(255,0,0,0.5);}
<div id="my-div"> <canvas id="canvas"></canvas></div>
Related Topics
Pass a JavaScript Variable Value into Input Type Hidden Value
Open a New JavaScript Window(.Open) Along with Its CSS Styling
How to Get a Floating Footer to Stick to the Bottom of the Viewport in Ie 6
How to Detect CSS Flex Wrap Event
Mime Type Error with Express.Static and CSS Files
Call Swift Function with JavaScript Using Uiwebview
Does Every JavaScript Function Have to Return a Value
Execute JavaScript Using Selenium Webdriver in C#
$ Variable (Dollar Sign) in Chrome
Should I Use Window.Navigate or Document.Location in JavaScript
How to Stop User from Printing Webpages? Using JavaScript or Jquery
How to Darken an Image on Mouseover
How to Use CSSstylesheet.Insertrule() Properly
React 'Cannot Read Property of Undefined' When Using Map
When a 'Blur' Event Occurs, How to Find Out Which Element Focus Went *To*
What Is the Fastest or Most Elegant Way to Compute a Set Difference Using JavaScript Arrays