1
votes

I work on a canvas-based application and I needed to resize the canvas dynamically. The thing is, setting width and height through CSS stretches the canvas instead of actually widening the drawing surface. You need to set that through the html width and height attributes, in pixels. That's the problem: I wanted it to fill the table cell.

I worked out a solution thanks to a few lines of javascript I found on another SE answer.

canvas.width  = canvas.offsetWidth;
canvas.height = canvas.offsetHeight;

The offsetWidth property takes the CSS height into account, so it can be used to transform the 100% height into a pixel value. So we let the css resize the canvas element and we us JS to resize the drawing surface... But it seems to only work when trying to make the canvas grow.

http://jsfiddle.net/4p18g0e9/1/

Look at this fiddle in chrome. As you resize the result window, the canvas gets redrawn and resized so the red square always has the same aspect ratio. However, when you try to shrink the window, the canvas keeps the same size, overflowing below the body! I added the css code to draw a border under to body to show this.

In firefox, the CSS height of 100% on the canvas seems to make it wider than the page instead of making it relative to the parent. Remove the JS from the fiddle and try it, the canvas is huge.

I've worked out a fix for chrome's problem here: http://jsfiddle.net/yb6e9mwa/2/

updateCanvasSize = function() {
    canvas.width = 0;
    canvas.height = 0;
    canvas.style.width = "1px";
    canvas.style.height = "1px";

    setTimeout(_updateCanvasSize, 0);
};

_updateCanvasSize = function() {
  canvas.style.width = "100%";
  canvas.style.height = "100%";
  canvas.width  = canvas.offsetWidth;
  canvas.height = canvas.offsetHeight;
  drawCanvas();
};

It's quite simple, I just set both the height and the CSS height to 0px, wait for it to render and then set it back to what it should. It works fine, even though it means you can't see the canvas while resizing the window.

That fix on Firefox, however, sets the canvas to a constant 1px high. Is there something wrong with the browsers, or am I doing something wrong?

1
try setting height attribute to td, eg jsfiddle.net/yb6e9mwa/3, does it help? - mido
Rule of thumb: don't use tables for layout purposes. Using divs here will solve this problem. - user1693593
@mido22 That fiddle sets a size for all rows which is smaller than 100% of the page, but sets 100% as the table height. The browser doesn't know where to put the extra space so it just gives it all to the first row. - Domino
@KenFyrstenberg I set the table height to 100% and two rows to fixed height, so the middle one should resize automatically - that's how table works. Similar behavior with divs requires absolute positioning. But that's what I went for in the end because it indeed works, thanks. jsfiddle.net/aL3kayLs/2 Strangely enough, chrome doesn't do transparency on the resized canvas. I won't use that anyway, but it's still weird. - Domino
i believe this is more elegant way to do it, jsfiddle.net/dsnqr5m2, without using absolute ... - mido

1 Answers

2
votes

It seems to be a problem with the way tables automatically stretch to adapt to content even if it means overflowing the body. I made a quick test using the css resize property (Chrome/Firefox only). http://jsfiddle.net/dods7dpk/

So I did what KenFyrstenberg said and used divs instead. Since divs don't distribute their height to their children automatically, I had to set the middle row to top: 20px; bottom: 20px; to make it stretch automatically.

http://jsfiddle.net/aL3kayLs/2/

<body>
    <div class="fillParent" style="position:relative;">
        <div style="height:20px; width:100%;">
            <div class="fillParent">Test</div>
        </div>
        <div style="position:absolute; top:20px; bottom: 20px; width:100%;">
            <div class="fillParent">
                <canvas class="fillParent" width="100" height="100">
                    Your browser is not compatible with HTML5 canvas.
                </canvas>
            </div>
        </div>
        <div style="position: absolute; bottom: 0px; height:20px; width:100%;">
            <div>Test</div>
        </div>
    </div>
</body>

 

body{
    border-bottom: 1px solid black;
}

html, body, .fillParent {
    height: 100%;
    width: 100%;
    overflow: hidden;
}

table {
  table-layout: fixed;
}

canvas {
    background-color: rgba(0,0,255,0.5);
}

 

If you don't need to be compatible with old browsers (IE8 and before) mido22's solution is cleaner, using the CSS calc function.

Cleaned up code version: http://jsfiddle.net/dsnqr5m2/2/

<body>
    <div class="fillParent">
        <div class='header'>
            <div class="fillParent">Test</div>
        </div>
        <div class='middle'>
            <div class="fillParent">
                <canvas class="fillParent" width="100" height="100">
                    Your browser is not compatible with HTML5 canvas.
                </canvas>
            </div>
        </div>
        <div class='footer'>
            <div>Test</div>
        </div>
    </div>
</body>

 

html, body, .fillParent {
    height: 100%;
    width: 100%;
    overflow: hidden;
}

.header, .footer{
    height:20px;
}

.middle{
    height:calc(100% - 40px);
}

canvas {
    background-color: rgba(0,0,255,0.5);
}