英文:
Force a canvas to use available space in a flex column
问题
I have a full size page, containing a top toolbar, and below, a container with 2 columns:
- a right column
- a left column containing a canvas (centered horizontally and vertically in this column), which has its own coordinates system (typically 4000 x 3000) and should preserve this aspect ratio. This canvas has a "+" button layered on it. (In my real code, there are several canvas for multiple layers, and multiple buttons, but not important here)
这里几乎可以工作,但我无法使画布占据左列中的全部可用空间。
通过我尝试的所有方法(max-width: 100%
,max-height: 100%
),无论是在浏览器中缩放(70%、80%...150%)还是因所使用的方法导致按钮无法保持在画布的右上角而失败。
TL;DR: 如何使这个画布在左列中占据所有可用空间,保持其纵横比,保持分层按钮,而不溢出?
注意:我正在寻找一个CSS解决方案,JS不是用于定位,而只用于绘制。
ctx = document.querySelector("canvas").getContext("2d");
ctx.fillStyle = "#FF0000"; ctx.fillRect(0, 0, 1000, 1000);
ctx.fillStyle = "#00FF00"; ctx.fillRect(1000, 1000, 1000, 1000);
ctx.fillStyle = "#0000FF"; ctx.fillRect(2000, 2000, 1000, 1000);
ctx.fillStyle = "#000000"; ctx.fillRect(3000, 0, 1000, 1000);
* {
padding: 0;
margin: 0;
}
.page {
display: flex;
flex-direction: column;
height: 100vh;
}
.toolbar {
background-color: yellow;
flex: 0 0 5em;
}
.container {
background-color: orange;
flex: 1 0 0;
display: flex;
}
.left {
background-color: magenta;
flex: 1 0 0;
display: flex;
align-items: center;
justify-content: center;
}
.right {
background-color: salmon;
flex: 0 0 10em;
}
.canvas-wrapper {
background-color: grey;
display: flex;
position: relative;
}
canvas {
height: 100px;
background-color: white;
}
.button {
background-color: yellow;
position: absolute;
top: 0;
right: 0;
width: 1em;
height: 1em;
}
<div class="page">
<div class="toolbar">
TOOLBAR OF HEIGHT 5 EM
</div>
<div class="container">
<div class="left">
<div class="canvas-wrapper">
<canvas width="4000" height="3000"></canvas>
<div class="button">+</div>
</div>
</div>
<div class="right">
RIGHT OF WIDTH 10 EM
</div>
</div>
</div>
PS: 你必须在“全屏”中打开片段,以查看浏览器的缩放功能(CTRL +,CTRL -)。
<details>
<summary>英文:</summary>
I have a full size page, containing a top toolbar, and below, a container with 2 columns:
- a right column
- a left column containing a canvas (centered horizontally and vertically in this column), which has its own coordinates system (typically 4000 x 3000) and should preserve this aspect ratio. This canvas has a "+" button layered on it. (In my real code, there are several canvas for multiple layers, and multiple buttons, but not important here)
[![enter image description here][1]][1]
Here it *nearly* works, but I can't make the canvas occupy the full available space in the left column.
With all methods that I tried (`max-width: 100%`, `max-height: 100%`), either it fails when we zoom in / out in the browser (70% 80% ... 150 %), or it fails because the method used prevents the button to stay on top of the canvas, at its right corner.
**TL;DR: how to make this canvas take all available space in the left column, keeping its aspect ratio, keeping the layered buttons, without overflow?**
Note: I'm looking for a CSS solution, the JS is not for the positioning, but only the drawing.
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
ctx = document.querySelector("canvas").getContext("2d");
ctx.fillStyle = "#FF0000"; ctx.fillRect(0, 0, 1000, 1000);
ctx.fillStyle = "#00FF00"; ctx.fillRect(1000, 1000, 1000, 1000);
ctx.fillStyle = "#0000FF"; ctx.fillRect(2000, 2000, 1000, 1000);
ctx.fillStyle = "#000000"; ctx.fillRect(3000, 0, 1000, 1000);
<!-- language: lang-css -->
* {
padding: 0;
margin: 0;
}
.page {
display: flex;
flex-direction: column;
height: 100vh;
}
.toolbar {
background-color: yellow;
flex: 0 0 5em;
}
.container {
background-color: orange;
flex: 1 0 0;
display: flex;
}
.left {
background-color: magenta;
flex: 1 0 0;
display: flex;
align-items: center;
justify-content: center;
}
.right {
background-color: salmon;
flex: 0 0 10em;
}
.canvas-wrapper {
background-color: grey;
display: flex;
position: relative;
}
canvas {
height: 100px;
background-color: white;
}
.button {
background-color: yellow;
position: absolute;
top: 0;
right: 0;
width: 1em;
height: 1em;
}
<!-- language: lang-html -->
<div class="page">
<div class="toolbar">
TOOLBAR OF HEIGHT 5 EM
</div>
<div class="container">
<div class="left">
<div class="canvas-wrapper">
<canvas width="4000" height="3000"></canvas>
<div class="button">+</div>
</div>
</div>
<div class="right">
RIGHT OF WIDTH 10 EM
</div>
</div>
</div>
<!-- end snippet -->
PS: you have to open the snippet in "Full page" to see the browser zoom in / zoom out feature (CTRL + , CTRL -).
[1]: https://i.stack.imgur.com/gXckTm.png
</details>
# 答案1
**得分**: 2
你需要更改一些对齐方式,`width`,`height`,`flex-grow`,并添加一个HTML包装器(即`<div class="canvas-wrapper-inner">`)。
请参见下面的代码片段。
```html
<div class="page">
<div class="toolbar">
TOOLBAR OF HEIGHT 5 EM
</div>
<div class="container">
<div class="left">
<div class="canvas-wrapper">
<div class="canvas-wrapper-inner">
<canvas width="4000" height="3000"></canvas>
<div class="button">+</div>
</div>
</div>
</div>
<div class="right">
RIGHT OF WIDTH 10 EM
</div>
</div>
</div>
英文:
You need to change some alignments, width
s, height
s, flex-grow
s and add an HTML wrapper (i.e., <div class="canvas-wrapper-inner">
).
See the snippet below.
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
ctx = document.querySelector("canvas").getContext("2d");
ctx.fillStyle = "#FF0000";
ctx.fillRect(0, 0, 1000, 1000);
ctx.fillStyle = "#00FF00";
ctx.fillRect(1000, 1000, 1000, 1000);
ctx.fillStyle = "#0000FF";
ctx.fillRect(2000, 2000, 1000, 1000);
ctx.fillStyle = "#000000";
ctx.fillRect(3000, 0, 1000, 1000);
<!-- language: lang-css -->
* {
padding: 0;
margin: 0;
}
.page {
display: flex;
flex-direction: column;
height: 100vh;
}
.toolbar {
background-color: yellow;
flex: 0 0 5em;
}
.container {
background-color: orange;
flex: 1 0 0;
display: flex;
}
.left {
background-color: magenta;
flex: 1 0 0;
display: flex;
align-items: center;
justify-content: center;
}
.right {
background-color: salmon;
flex: 0 0 10em;
}
.canvas-wrapper {
background-color: grey;
display: flex;
position: relative;
flex-grow: 1; /* Added */
align-self: stretch; /* Added */
align-items: center; /* Added */
justify-content: center; /* Added */
}
canvas {
width: 100%; /* Added */
flex-grow: 1; /* Added */
object-fit: contain; /* Added */
max-height: calc(100vh - 5em); /* Added */
}
.button {
background-color: yellow;
position: absolute;
top: 0;
right: 0;
width: 1em;
height: 1em;
}
/* Added */
.canvas-wrapper-inner {
position: relative;
}
<!-- language: lang-html -->
<div class="page">
<div class="toolbar">
TOOLBAR OF HEIGHT 5 EM
</div>
<div class="container">
<div class="left">
<div class="canvas-wrapper">
<div class="canvas-wrapper-inner">
<canvas width="4000" height="3000"></canvas>
<div class="button">+</div>
</div>
</div>
</div>
<div class="right">
RIGHT OF WIDTH 10 EM
</div>
</div>
</div>
<!-- end snippet -->
答案2
得分: 1
There's a handy unit size called cqmin which is the lowest size of the inline or block sizes. Set container wrapper as a container of type size so the content responds to both block and inline dimensions then set the width of the canvas to 100cqmin. Use aspect-ratio to maintain the, er, aspect ratio. See example below. I've put a codepen of it here too.
ctx = document.querySelector("canvas").getContext("2d");
ctx.fillStyle = "#FF0000"; ctx.fillRect(0, 0, 1000, 1000);
ctx.fillStyle = "#00FF00"; ctx.fillRect(1000, 1000, 1000, 1000);
ctx.fillStyle = "#0000FF"; ctx.fillRect(2000, 2000, 1000, 1000);
ctx.fillStyle = "#000000"; ctx.fillRect(3000, 0, 1000, 1000);
* {
padding: 0;
margin: 0;
}
.page {
display: flex;
flex-direction: column;
height: 100vh;
}
.toolbar {
background-color: yellow;
flex: 0 0 5em;
}
.container {
background-color: orange;
flex: 1 0 0;
display: flex;
}
.left {
container-type: size;
background-color: magenta;
flex: 1 0 0;
display: flex;
align-items: center;
justify-content: center;
}
.right {
background-color: salmon;
flex: 0 0 10em;
}
.canvas-wrapper {
position: relative;
width: 100cqmin;
}
.button {
background-color: yellow;
position: absolute;
top: 0;
right: 0;
width: 1em;
height: 1em;
}
canvas {
width: 100%;
aspect-ratio: 4/3;
background-color: white;
}
英文:
There's a handy unit size called cqmin which is the lowest size of the inline or block sizes. Set container wrapper as a container of type size so the content responds to both block and inline dimesions then set the width of the canvas to 100cqmin. Use aspect-ratio to maintain the, er, aspect ratio. See example below. I've put a codepen of it here too.
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
ctx = document.querySelector("canvas").getContext("2d");
ctx.fillStyle = "#FF0000"; ctx.fillRect(0, 0, 1000, 1000);
ctx.fillStyle = "#00FF00"; ctx.fillRect(1000, 1000, 1000, 1000);
ctx.fillStyle = "#0000FF"; ctx.fillRect(2000, 2000, 1000, 1000);
ctx.fillStyle = "#000000"; ctx.fillRect(3000, 0, 1000, 1000);
<!-- language: lang-css -->
* {
padding: 0;
margin: 0;
}
.page {
display: flex;
flex-direction: column;
height: 100vh;
}
.toolbar {
background-color: yellow;
flex: 0 0 5em;
}
.container {
background-color: orange;
flex: 1 0 0;
display: flex;
}
.left {
container-type: size;
background-color: magenta;
flex: 1 0 0;
display: flex;
align-items: center;
justify-content: center;
}
.right {
background-color: salmon;
flex: 0 0 10em;
}
.canvas-wrapper {
position: relative;
width: 100cqmin;
}
.button {
background-color: yellow;
position: absolute;
top: 0;
right: 0;
width: 1em;
height: 1em;
}
canvas {
width: 100%;
aspect-ratio: 4/3;
background-color: white;
}
<!-- language: lang-html -->
<div class="page">
<div class="toolbar">
TOOLBAR OF HEIGHT 5 EM
</div>
<div class="container">
<div class="left">
<div class="canvas-wrapper">
<canvas width="4000" height="3000"></canvas>
<div class="button">+</div>
</div>
</div>
<div class="right">
RIGHT OF WIDTH 10 EM
</div>
</div>
</div>
<!-- end snippet -->
答案3
得分: 1
The canvas-wrapper
需要与画布具有相同的大小和位置,但画布的大小和位置基于其父级。一个简单的解决方案是将画布的尺寸复制为其父级的 aspect-ratio
:
<div class="canvas-wrapper" style="aspect-ratio: 4000 / 3000;">
<canvas width="4000" height="3000"></canvas>
<div class="button">+</div>
</div>
请注意,这是一段HTML代码,用于描述如何设置canvas-wrapper
元素的尺寸和位置以适应画布。
英文:
The canvas-wrapper
needs to have same size and position as the canvas, but the size and position of the canvas is based on its grand parent. A simple solution is to copy the canvas dimensions as aspect-ratio
to its parent:
<!-- begin snippet: js hide: false console: false babel: false -->
<!-- language: lang-js -->
ctx = document.querySelector("canvas").getContext("2d");
ctx.fillStyle = "#FF0000";
ctx.fillRect(0, 0, 1000, 1000);
ctx.fillStyle = "#00FF00";
ctx.fillRect(1000, 1000, 1000, 1000);
ctx.fillStyle = "#0000FF";
ctx.fillRect(2000, 2000, 1000, 1000);
ctx.fillStyle = "#000000";
ctx.fillRect(3000, 0, 1000, 1000);
<!-- language: lang-css -->
body {
margin: 0;
}
.page {
height: 100vh;
display: flex;
flex-direction: column;
}
.toolbar {
background-color: yellow;
flex: 0 0 5em;
}
.container {
background-color: orange;
flex: 1 1 auto;
/* wrapper for two column layout */
display: flex;
flex-direction: row;
}
.left {
background-color: magenta;
flex: 1 1 auto;
/* reference element for size and position */
position: relative;
}
.right {
background-color: salmon;
flex: 0 0 10em;
align-self: center;
}
.canvas-wrapper {
background-color: gray;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
max-width: 100%;
max-height: 100%;
}
canvas {
display: block;
width: 100%;
height: 100%;
}
.button {
background-color: cyan;
position: absolute;
top: 0;
right: 0;
padding: .5em;
}
<!-- language: lang-html -->
<div class="page">
<div class="toolbar">
TOOLBAR OF HEIGHT 5 EM
</div>
<div class="container">
<div class="left">
<div class="canvas-wrapper" style="aspect-ratio: 4000 / 3000;">
<canvas width="4000" height="3000"></canvas>
<div class="button">+</div>
</div>
</div>
<div class="right">
RIGHT OF WIDTH 10 EM
</div>
</div>
</div>
<!-- end snippet -->
答案4
得分: 1
这是基于RokBenko的回答的答案,似乎可以工作,无需使用第二个包装和第二个flex:
<div class="page">
<div class="toolbar">
TOOLBAR OF HEIGHT 5 EM
</div>
<div class="container">
<div class="left">
<div class="canvas-wrapper">
<canvas width="4000" height="3000"></canvas>
<div class="button">+</div>
</div>
</div>
<div class="right">
RIGHT OF WIDTH 10 EM
</div>
</div>
</div>
英文:
Here is an answer based on RokBenko's answer which seems to work, without the secondary wrapper and the secondary flex:
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
ctx = document.querySelector("canvas").getContext("2d");
ctx.fillStyle = "#FF0000";
ctx.fillRect(0, 0, 1000, 1000);
ctx.fillStyle = "#00FF00";
ctx.fillRect(1000, 1000, 1000, 1000);
ctx.fillStyle = "#0000FF";
ctx.fillRect(2000, 2000, 1000, 1000);
ctx.fillStyle = "#000000";
ctx.fillRect(3000, 0, 1000, 1000);
<!-- language: lang-css -->
* {
padding: 0;
margin: 0;
}
.page {
display: flex;
flex-direction: column;
height: 100vh;
}
.toolbar {
background-color: yellow;
flex: 0 0 5em;
}
.container {
background-color: orange;
flex: 1 0 0;
display: flex;
}
.left {
background-color: magenta;
flex: 1 0 0;
display: flex;
align-items: center;
justify-content: center;
}
.right {
background-color: salmon;
flex: 0 0 10em;
}
.canvas-wrapper {
background-color: #aaa;
position: relative;
}
canvas {
background-color: white;
width: 100%;
max-height: calc(100vh - 5em);
}
.button {
background-color: yellow;
position: absolute;
top: 0;
right: 0;
width: 1em;
height: 1em;
}
<!-- language: lang-html -->
<div class="page">
<div class="toolbar">
TOOLBAR OF HEIGHT 5 EM
</div>
<div class="container">
<div class="left">
<div class="canvas-wrapper">
<canvas width="4000" height="3000"></canvas>
<div class="button">+</div>
</div>
</div>
<div class="right">
RIGHT OF WIDTH 10 EM
</div>
</div>
</div>
<!-- end snippet -->
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论