CSS min-height on canvas (or even svg) has a limit?

此生再无相见时 提交于 2020-07-09 11:56:06

问题


The Use case

Let's said you've a couple of <div> in 2 nested flex (one for column, the other for rows), in order to get something like:

You can resize the parent <div>, in any direction, and everything works as expected.

note <div> are defined with

{
   /* flex: 1 1 0px */
   flex-grow: 1
   flex-shrink: 1
   flex-basis: 0px /* i.e. independant of content */
}

<html>
<style type="text/css">
  html,
  body {
    height: 100%;
    margin: 0px;
  }
  
  body {
    padding-left: 30px
  }
  
  span {
    display: block;
    margin-top: 30px
  }
  
  #dut {
    background-color: red;
  }
</style>

<body>
  <span id="info"></span>
  <div style="
        background-color: gray;
        height: 200px;
        width: 50%;
        resize: both;
        overflow: auto;" onresize="onResize()">

    <div style="
            height:100%;
            display:flex;
            flex-direction: column;
        ">

      <div style="
            flex: 1 1 0px;
            display:flex;
            flex-direction: row;
            ">
        <div style="flex: 1 1 0px; background-color: chocolate;"></div>
        <div style="flex: 1 1 0px; background-color: gray;"></div>
        <div style="flex: 1 1 0px; background-color: chocolate;"></div>
        <div style="flex: 1 1 0px; background-color: gray;"></div>
      </div>

      <div style="
            flex: 1 1 0px;
            display:flex;
            flex-direction: row;
            ">
        <div style="flex: 1 1 0px; background-color: gray;"></div>
        <div id="dut" style="flex: 1 1 0px;"></div>
        <div style="flex: 1 1 0px; background-color: gray;"></div>
        <div style="flex: 1 1 0px; background-color: chocolate;"></div>
      </div>

    </div>
  </div>

  <script>
    const dut = window.document.getElementById("dut");
    const info = window.document.getElementById("info");
    const cb = () => info.innerHTML = `size of RED element: ${dut.offsetWidth} x ${dut.offsetHeight}`
    new ResizeObserver(cb).observe(dut)
  </script>
</body>

</html>

Where I start to be puzzle...

If you takes one <div> element (the red one tagged "dut"), and you replace it with <canvas> you will get:

In other words the red element can't be shrinking under "300x150" here

We should be able to fix this with min-height and min-width (which predate flex computation):

{
    background-color: red;
    min-width: 0;
    min-height: 0;
}

Results:

width is now correct, but why not the height ?!

<html>
<style type="text/css">
  html,
  body {
    height: 100%;
    margin: 0px;
  }
  
  body {
    padding-left: 30px
  }
  
  span {
    display: block;
    margin-top: 30px
  }
  
  #dut {
    background-color: red;
    min-width: 0;
    min-height: 0;
  }
</style>

<body>
  <span id="info"></span>
  <div style="
        background-color: gray;
        height: 200px;
        width: 50%;
        resize: both;
        overflow: auto;" onresize="onResize()">

    <div style="
            height:100%;
            display:flex;
            flex-direction: column;
        ">

      <div style="
            flex: 1 1 0px;
            display:flex;
            flex-direction: row;
            ">
        <div style="flex: 1 1 0px; background-color: chocolate;"></div>
        <div style="flex: 1 1 0px; background-color: gray;"></div>
        <div style="flex: 1 1 0px; background-color: chocolate;"></div>
        <div style="flex: 1 1 0px; background-color: gray;"></div>
      </div>

      <div style="
            flex: 1 1 0px;
            display:flex;
            flex-direction: row;
            ">
        <div style="flex: 1 1 0px; background-color: gray;"></div>
        <canvas id="dut" style="flex: 1 1 0px;"></canvas>
        <div style="flex: 1 1 0px; background-color: gray;"></div>
        <div style="flex: 1 1 0px; background-color: chocolate;"></div>
      </div>

    </div>
  </div>

  <script>
    const dut = window.document.getElementById("dut");
    const info = window.document.getElementById("info");
    const cb = () => info.innerHTML = `size of RED element: ${dut.offsetWidth} x ${dut.offsetHeight}`
    new ResizeObserver(cb).observe(dut)
  </script>
</body>

</html>

So Why ?

I'm thinking of a bunch of workaround (more or less ugly), but my question more on what is the root cause of this behavior ? In there a another css tag like min-min-height ?!?

I've notice that min-min-height which is equal to 150 pixels in my case actually depends on your browser, and also on canvas's height attribute (and will be something in regard of viewBox attribute for <svg>)

Thanks !!


回答1:


If you check the MDN You can read:

height

The height of the coordinate space in CSS pixels. Defaults to 150.

and

width

The width of the coordinate space in CSS pixels. Defaults to 300.

You already found a way to counter the width since in the flexbox world element can shrink to fit their parent size on the main axis (using flex-shrink and by adding min-width) but there is no shrink effect on the cross axis and your code is equivalent to the following:

  html,
  body {
    height: 100%;
    margin: 0px;
  }
  
  body {
    padding-left: 30px
  }
  
  span {
    display: block;
    margin-top: 30px
  }
  
  #dut {
    background-color: red;
    min-width: 0;
    min-height: 0;
    height:150px;
    width:300px;
  }
  <span id="info"></span>
  <div style="
        background-color: gray;
        height: 200px;
        width: 50%;
        resize: both;
        overflow: auto;" onresize="onResize()">

    <div style="
            height:100%;
            display:flex;
            flex-direction: column;
        ">

      <div style="
            flex: 1 1 0px;
            display:flex;
            flex-direction: row;
            ">
        <div style="flex: 1 1 0px; background-color: chocolate;"></div>
        <div style="flex: 1 1 0px; background-color: gray;"></div>
        <div style="flex: 1 1 0px; background-color: chocolate;"></div>
        <div style="flex: 1 1 0px; background-color: gray;"></div>
      </div>

      <div style="
            flex: 1 1 0px;
            display:flex;
            flex-direction: row;
            ">
        <div style="flex: 1 1 0px; background-color: gray;"></div>
        <div id="dut" style="flex: 1 1 0px;"></div>
        <div style="flex: 1 1 0px; background-color: gray;"></div>
        <div style="flex: 1 1 0px; background-color: chocolate;"></div>
      </div>

    </div>
  </div>

  <script>
    const dut = window.document.getElementById("dut");
    const info = window.document.getElementById("info");
    const cb = () => info.innerHTML = `size of RED element: ${dut.offsetWidth} x ${dut.offsetHeight}`
    new ResizeObserver(cb).observe(dut)
  </script>
</body>

</html>

I replaced the canvas with a simple div having a dimension of 300x150. The width can shrink and the height can only trigger an overflow and will never shrink.

If you make the height 0 you will have what you want because we removed the height constraint and it now behave like a div (an empty div by default has no height)

html,
  body {
    height: 100%;
    margin: 0px;
  }
  
  body {
    padding-left: 30px
  }
  
  span {
    display: block;
    margin-top: 30px
  }
  
  #dut {
    background-color: red;
    min-width: 0;
    min-height: 0;
  }
<span id="info"></span>
  <div style="
        background-color: gray;
        height: 200px;
        width: 50%;
        resize: both;
        overflow: auto;" onresize="onResize()">

    <div style="
            height:100%;
            display:flex;
            flex-direction: column;
        ">

      <div style="
            flex: 1 1 0px;
            display:flex;
            flex-direction: row;
            ">
        <div style="flex: 1 1 0px; background-color: chocolate;"></div>
        <div style="flex: 1 1 0px; background-color: gray;"></div>
        <div style="flex: 1 1 0px; background-color: chocolate;"></div>
        <div style="flex: 1 1 0px; background-color: gray;"></div>
      </div>

      <div style="
            flex: 1 1 0px;
            display:flex;
            flex-direction: row;
            ">
        <div style="flex: 1 1 0px; background-color: gray;"></div>
        <canvas id="dut" style="flex: 1 1 0px;" height=0></canvas>
        <div style="flex: 1 1 0px; background-color: gray;"></div>
        <div style="flex: 1 1 0px; background-color: chocolate;"></div>
      </div>

    </div>
  </div>

  <script>
    const dut = window.document.getElementById("dut");
    const info = window.document.getElementById("info");
    const cb = () => info.innerHTML = `size of RED element: ${dut.offsetWidth} x ${dut.offsetHeight}`
    new ResizeObserver(cb).observe(dut)
  </script>
</body>

</html>

or height:0;min-height:100% inside the CSS and keep the intrinsic dimension unchanged and be able to draw inside your canvas.

html,
  body {
    height: 100%;
    margin: 0px;
  }
  
  body {
    padding-left: 30px
  }
  
  span {
    display: block;
    margin-top: 30px
  }
  
  #dut {
    background-color: red;
    min-width: 0;
    height: 0;
    min-height:100%;
  }
<span id="info"></span>
  <div style="
        background-color: gray;
        height: 200px;
        width: 50%;
        resize: both;
        overflow: auto;" onresize="onResize()">

    <div style="
            height:100%;
            display:flex;
            flex-direction: column;
        ">

      <div style="
            flex: 1 1 0px;
            display:flex;
            flex-direction: row;
            ">
        <div style="flex: 1 1 0px; background-color: chocolate;"></div>
        <div style="flex: 1 1 0px; background-color: gray;"></div>
        <div style="flex: 1 1 0px; background-color: chocolate;"></div>
        <div style="flex: 1 1 0px; background-color: gray;"></div>
      </div>

      <div style="
            flex: 1 1 0px;
            display:flex;
            flex-direction: row;
            ">
        <div style="flex: 1 1 0px; background-color: gray;"></div>
        <canvas id="dut" style="flex: 1 1 0px;" ></canvas>
        <div style="flex: 1 1 0px; background-color: gray;"></div>
        <div style="flex: 1 1 0px; background-color: chocolate;"></div>
      </div>

    </div>
  </div>

  <script>
    const dut = window.document.getElementById("dut");
    const info = window.document.getElementById("info");
    const cb = () => info.innerHTML = `size of RED element: ${dut.offsetWidth} x ${dut.offsetHeight}`
    new ResizeObserver(cb).observe(dut)
  </script>
</body>

</html>

You can even update the attribute like you want and it will behave the same:

html,
  body {
    height: 100%;
    margin: 0px;
  }
  
  body {
    padding-left: 30px
  }
  
  span {
    display: block;
    margin-top: 30px
  }
  
  #dut {
    background-color: red;
    min-width: 0;
    height: 0;
    min-height:100%;
  }
<span id="info"></span>
  <div style="
        background-color: gray;
        height: 200px;
        width: 50%;
        resize: both;
        overflow: auto;" onresize="onResize()">

    <div style="
            height:100%;
            display:flex;
            flex-direction: column;
        ">

      <div style="
            flex: 1 1 0px;
            display:flex;
            flex-direction: row;
            ">
        <div style="flex: 1 1 0px; background-color: chocolate;"></div>
        <div style="flex: 1 1 0px; background-color: gray;"></div>
        <div style="flex: 1 1 0px; background-color: chocolate;"></div>
        <div style="flex: 1 1 0px; background-color: gray;"></div>
      </div>

      <div style="
            flex: 1 1 0px;
            display:flex;
            flex-direction: row;
            ">
        <div style="flex: 1 1 0px; background-color: gray;"></div>
        <canvas id="dut" style="flex: 1 1 0px;" heght="600" width="600"></canvas>
        <div style="flex: 1 1 0px; background-color: gray;"></div>
        <div style="flex: 1 1 0px; background-color: chocolate;"></div>
      </div>

    </div>
  </div>

  <script>
    const dut = window.document.getElementById("dut");
    const info = window.document.getElementById("info");
    const cb = () => info.innerHTML = `size of RED element: ${dut.offsetWidth} x ${dut.offsetHeight}`
    new ResizeObserver(cb).observe(dut)
  </script>
</body>

</html>



回答2:


Here is a complete snippet (svg and canvas) based on the previous answer

Quote from @Termani Afif:

{ height:0; min-height:100% } are both use to remove the contraint. It will force the element to have a height:0, then min-height:100% will force it again to take all the available space. So now the container will decide about the height

<html>
<style type="text/css">
  html,
  body {
    height: 100%;
    margin: 0px;
  }
  
  body {
    padding-left: 30px
  }
  
  span {
    display: block;
    margin-top: 30px
  }
  
  #dut {
    background-color: red;
    min-width: 0;
    height: 0;
    min-height: 100%;
  }
  
  #dut2 {
    background-color: red;
    min-width: 0;
    height: 0;
    min-height: 100%;
  }
</style>

<body>
  <span id="info"></span>
  <div style="
              background-color: gray;
              height: 200px;
              width: 50%;
              resize: both;
              overflow: auto;" onresize="onResize()">

    <div style="
                  height:100%;
                  display:flex;
                  flex-direction: column;
              ">

      <div style="
                  flex: 1 1 0px;
                  display:flex;
                  flex-direction: row;
                  ">
        <div style="flex: 1 1 0px; background-color: chocolate;"></div>
        <div style="flex: 1 1 0px; background-color: gray;"></div>
        <div style="flex: 1 1 0px; background-color: chocolate;"></div>
        <div style="flex: 1 1 0px; background-color: gray;"></div>
      </div>

      <div style="
                  flex: 1 1 0px;
                  display:flex;
                  flex-direction: row;
                  ">
        <div style="flex: 1 1 0px; background-color: gray;"></div>
        <canvas id="dut" style="flex: 1 1 0px;" width=100 height=100></canvas>
        <div style="flex: 1 1 0px; background-color: gray;"></div>
        <svg id="dut2" style="flex: 1 1 0px;" viewBox="0 0 100 100" preserveAspectRatio="none">
                <circle cx="0" cy="0" r="100" stroke="black" stroke-width="1" fill="#a00" />
              </svg>
      </div>

    </div>
  </div>

  <script>
    const dut = window.document.getElementById("dut");
    const info = window.document.getElementById("info");
    const cb = () => info.innerHTML = `size of RED element: ${dut.offsetWidth} x ${dut.offsetHeight}`
    new ResizeObserver(cb).observe(dut)

    const ctx = dut.getContext('2d')
    ctx.beginPath();
    ctx.arc(0, 0, 100, 0, 2 * Math.PI);
    ctx.fillStyle = "#a00";
    ctx.fill()
    ctx.stroke();
  </script>
</body>

</html>
</body>

</html>


来源:https://stackoverflow.com/questions/61772770/css-min-height-on-canvas-or-even-svg-has-a-limit

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!