3

TL;DR: here is a full page example: https://jsfiddle.net/rfp8xv6g/show (editable here: https://jsfiddle.net/rfp8xv6g), how to make that the canvas (with purple border) is fully contained vertically in the container, without hardcoding a height?


Here is a simplified example. I have the following layout (click "Full page" in the snippet to be able to see it).

The left column contains a wrapper containing several <canvas> on top of each other (here only one canvas for simplicity).

Problem: if the canvas width/height is high (in canvas coordinate system, here 4000x3000), then the full canvas is not visible (too high in height, it grows out of the viewport).

How to make that the canvas is scaled/shrinked to be contained in the left column? (without a page vertical scrollbar)

Here max-width: 100%; works in order to have the canvas contained horizontally.

But max-height: 100%; doesn't do any effect: the canvas is not contained vertically, why?

* {
  padding: 0;
  margin: 0;
  box-sizing: border-box;
}

html,
body, 
.page {
  height: 100%;
  width: 100%;
}

.page {
  background-color: red;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}

.left-column {
  background-color: green;
  display: flex;
}

.right-column {
  background-color: orange;
  flex: 1;
}

canvas {
  border: 3px solid magenta;
  max-width: 100%;
  max-height: 100%;
}
<div class="page">
  <div class="left-column">
    <div class="canvas-wrapper">
      <canvas width="4000" height="3000"></canvas>
    </div>
  </div>
  <div class="right-column">
    RIGHT
  </div>
</div>
Basj
  • 41,386
  • 99
  • 383
  • 673

2 Answers2

2

If you want the canvas to adapt to the height of the flex element, then you do not need to specify the align-items: center; value in the flex container. Or you need to add align-self: stretch; to the flex element. You can also use flexbox for centering canvases container.

In fact, all the code to solve your problem boils down to:

.zone-left-column {
  align-self: stretch;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}

.canvas-wrapper {
  min-height: 0;
}

Below is a complete example:

* {
  padding: 0;
  margin: 0;
  box-sizing: border-box;
}

html,
body,
.page {
  height: 100%;
}

.page {
  max-width: 70rem;
  margin: 0 auto;
  padding: 1rem;
  display: flex;
  flex-direction: column;
  background-color: yellow;
}

.toolbar {
  background-color: rgba(200, 200, 200, 0.5);
  margin-bottom: 1rem;
  display: flex;
  align-items: center;
  justify-content: space-around;
  flex: 0 0 5rem;
}

.container {
  background-color: red;
  flex: 1;
  overflow: hidden;
  gap: 2rem;
}

.flex-horizontal {
  display: flex;
  justify-content: center;
  align-items: center;
}

.zone-left-column {
  background-color: green;
  /* add  */
  align-self: stretch;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}

.canvas-wrapper {
  background-color: rgba(255, 255, 255, .4);
  /* must have  */
  min-height: 0;
}

canvas {
  border: 3px solid magenta;
  max-width: 100%;
  max-height: 100%;
  display: block;
}

.zone-right-column {
  background-color: orange;
  flex: 1;
  border: 1px solid black;
}
<div class="page">
  <div class="toolbar">
    TOOLBAR
  </div>
  <div class="container flex-horizontal">
    <div class="zone-left-column">
      <div class="canvas-wrapper">
        <canvas width="2000" height="1500"></canvas>
      </div>
    </div>
    <div class="zone-right-column">
      RIGHT
    </div>
  </div>
</div>
imhvost
  • 4,750
  • 2
  • 8
  • 10
  • 1
    Thanks a lot! I see it works, but I don't know exactly why: why is `min-height: 0` important, and what does it do exactly? – Basj May 15 '23 at 12:28
  • Thanks for choosing my answer, glad to help. `min-height: 0;` is required so that the element can shrink, because by default when `overflow: visible;` it is set to `min-height: auto;`. You can also change the `overflow` value to anything other than `visible` in the element. [Automatic minimum size of Flex elements](https://www.w3.org/TR/css-flexbox-1/#min-size-auto) – imhvost May 15 '23 at 14:11
  • I am still trying to understand why min-height makes its child 100% height of its "grandparent" but it kind of does the job. – Salman A May 15 '23 at 18:59
  • @Salman A I provided a link to the specification in a previous comment. Perhaps the `min-height` is a bit confusing here, because our main axis comes from `flex-direction: column;`. Here is an example from `flex-direction: row;`: [codepen](https://codepen.io/imhvost/pen/abRabEB) Best Regards! ✌️ – imhvost May 15 '23 at 22:34
0

When an element has a percentage height, the parent must have a defined height.

If the parent has no defined height, then the percentage height simply resolves to auto.

Here's a complete explanation:

You have at least two available solutions:

  1. Define a height on the parent (.canvas-wrapper) or, even better in this case,

  2. Use viewport percentage units (e.g., vh) instead of percentage units (%).

Make one adjustment to your code:

canvas {
  border: 3px solid magenta;
  max-width: 100%;
  /* max-height: 100%; */
   max-height: 100vh; /* new */
}

* {
  padding: 0;
  margin: 0;
  box-sizing: border-box;
}

html,
body, 
.page {
  height: 100%;
  width: 100%;
}

.page {
  background-color: red;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}

.left-column {
  background-color: green;
  display: flex;
}

.right-column {
  background-color: orange;
  flex: 1;
}

canvas {
  border: 3px solid magenta;
  max-width: 100%;
  /* max-height: 100%; */
   max-height: 100vh; /* new */
}
<div class="page">
  <div class="left-column">
    <div class="canvas-wrapper">
      <canvas width="4000" height="3000"></canvas>
    </div>
  </div>
  <div class="right-column">
    RIGHT
  </div>
</div>
Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
  • Thank you very much @MichaelBenjamin for your answer! In fact in my case, it cannot use 100 % of vertical viewport, because there is a top toolbar (which I omitted for simplicity), see https://jsfiddle.net/rfp8xv6g/. How would you do in this case? Thanks again! – Basj May 04 '23 at 07:14
  • Here is the full screen version: https://jsfiddle.net/rfp8xv6g/show – Basj May 04 '23 at 10:58
  • 1
    Use the `calc()` function: `max-height: calc(100vh - 8rem)` (so, the full height, less the height of the toolbar (`5rem`), less the padding and margin (`3em`). https://jsfiddle.net/yfwvnbu6/ – Michael Benjamin May 04 '23 at 14:30
  • Thanks @MichaelBenjamin! Is there a way without hardcoding the toolbar height, and more in the spirit of `flex` / flex-grow etc. : "let it grow vertically until there is no remaining space"? Could flex do this automatically? – Basj May 04 '23 at 20:22
  • Set a height on the container. Then you can set the `canvas` height to `max-height: 100%`. Because the container is a flex container in column direction, the canvas (a flex item) will shrink to fit (because the default setting of `flex-shrink` is `1`). – Michael Benjamin May 05 '23 at 00:50
  • Thank you @MichaelBenjamin. Devil is in the detail, but I can't do it in the full example (full page: https://jsfiddle.net/rfp8xv6g/show) *without hardcoding the toolbar height with calc* when setting the container height. Would you have a solution on https://jsfiddle.net/rfp8xv6g? Thanks a lot! I started a bounty because I can't find a solution. – Basj May 08 '23 at 08:39
  • So you want a solution without the `flex: 0 0 5rem` on the `.toolbar`? https://jsfiddle.net/7q2uepfL/ – Michael Benjamin May 08 '23 at 14:18
  • I thought I solved the problem @MichaelBenjamin, but finally there's always a little something that doesn't work, I'm tearing my hair out since days :) My latest version: https://gget.it/tpif/page4.html. Last problem: when you zoom out in the browser (90% 80% 70% etc. with ` <->`) then the canvas wrapper is not vertically aligned in the middle, it stays on top. Every time I try a solution that solves this, it destroys something else. A last idea based on this .html page? :) Thanks one million times! – Basj May 09 '23 at 09:53
  • 1
    Add this: `.canvas-wrapper { display: flex; align-items: center }` – Michael Benjamin May 09 '23 at 17:11
  • It works indeed @MichaelBenjamin! https://gget.it/p2nw/page5.html The only issue I will probably have is that, as I need to stack multiple canvas on top of each other (multiple layers), I needed `.canvas-wrapper` to be *of the same size than the canvas* themselves. Here instead `.canvas-wrapper` (of grey background) takes the full height of the container and not *only* the size of the canvas: https://gget.it/qi6o/20230509_221717_1257a0c2.png Maybe you have an idea about this detail? I promiss it's the last one :) – Basj May 09 '23 at 20:18
  • Add `align-items: center` to `.zone-left-column`. (You can now remove the code from my last post.) – Michael Benjamin May 10 '23 at 16:17
  • 1
    Thank you for your help @MichaelBenjamin! This is the update with your last comment: https://gget.it/0bdc/page7.html. (Still some issues when we zoom in, but I think the problem is solved with another method, see my linked SO question). Thanks a lot again for your help. – Basj May 11 '23 at 06:20
  • 1
    @Michael Benjamin , Allow me to slightly disagree with you. It is not necessary to set the height directly on the parent. A father can be flexible. Also, according to [this link](https://stackoverflow.com/questions/31728022/why-is-percentage-height-not-working-on-my-div/31728799#31728799), everything is not so clear with `min-height` – imhvost May 11 '23 at 14:42
  • Thanks for the feedback and for posting an answer. Let's see what OP thinks. @imhvost – Michael Benjamin May 11 '23 at 15:41
  • @Michael Benjamin , Added my comment to your [answer](https://stackoverflow.com/questions/31728022/why-is-percentage-height-not-working-on-my-div/31728799#31728799). Best regards. – imhvost May 11 '23 at 21:20