问题
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 aheight:0
, thenmin-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