强制画布在弹性列中使用可用空间。

huangapple go评论73阅读模式
英文:

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 &quot;+&quot; 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&#39;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&#39;m looking for a CSS solution, the JS is not for the positioning, but only the drawing.

&lt;!-- begin snippet: js hide: false console: true babel: false --&gt;

&lt;!-- language: lang-js --&gt;

    ctx = document.querySelector(&quot;canvas&quot;).getContext(&quot;2d&quot;);
    ctx.fillStyle = &quot;#FF0000&quot;; ctx.fillRect(0, 0, 1000, 1000);
    ctx.fillStyle = &quot;#00FF00&quot;; ctx.fillRect(1000, 1000, 1000, 1000);
    ctx.fillStyle = &quot;#0000FF&quot;; ctx.fillRect(2000, 2000, 1000, 1000);
    ctx.fillStyle = &quot;#000000&quot;; ctx.fillRect(3000, 0, 1000, 1000);

&lt;!-- language: lang-css --&gt;

    * {
      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;
    }

&lt;!-- language: lang-html --&gt;

    &lt;div class=&quot;page&quot;&gt;
      &lt;div class=&quot;toolbar&quot;&gt;
        TOOLBAR OF HEIGHT 5 EM
      &lt;/div&gt;
      &lt;div class=&quot;container&quot;&gt;
        &lt;div class=&quot;left&quot;&gt;
          &lt;div class=&quot;canvas-wrapper&quot;&gt;
            &lt;canvas width=&quot;4000&quot; height=&quot;3000&quot;&gt;&lt;/canvas&gt;
            &lt;div class=&quot;button&quot;&gt;+&lt;/div&gt;
          &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;right&quot;&gt;
          RIGHT OF WIDTH 10 EM
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;

&lt;!-- end snippet --&gt;

PS: you have to open the snippet in &quot;Full page&quot; 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包装器(即`&lt;div class=&quot;canvas-wrapper-inner&quot;&gt;`)。

请参见下面的代码片段。

```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, widths, heights, flex-grows and add an HTML wrapper (i.e., &lt;div class=&quot;canvas-wrapper-inner&quot;&gt;).

See the snippet below.

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

ctx = document.querySelector(&quot;canvas&quot;).getContext(&quot;2d&quot;);
ctx.fillStyle = &quot;#FF0000&quot;;
ctx.fillRect(0, 0, 1000, 1000);
ctx.fillStyle = &quot;#00FF00&quot;;
ctx.fillRect(1000, 1000, 1000, 1000);
ctx.fillStyle = &quot;#0000FF&quot;;
ctx.fillRect(2000, 2000, 1000, 1000);
ctx.fillStyle = &quot;#000000&quot;;
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 -->

&lt;div class=&quot;page&quot;&gt;
  &lt;div class=&quot;toolbar&quot;&gt;
    TOOLBAR OF HEIGHT 5 EM
  &lt;/div&gt;
  &lt;div class=&quot;container&quot;&gt;
    &lt;div class=&quot;left&quot;&gt;
      &lt;div class=&quot;canvas-wrapper&quot;&gt;
        &lt;div class=&quot;canvas-wrapper-inner&quot;&gt;
          &lt;canvas width=&quot;4000&quot; height=&quot;3000&quot;&gt;&lt;/canvas&gt;
          &lt;div class=&quot;button&quot;&gt;+&lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class=&quot;right&quot;&gt;
      RIGHT OF WIDTH 10 EM
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

<!-- 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;
}
TOOLBAR OF HEIGHT 5 EM

+

RIGHT OF WIDTH 10 EM

英文:

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(&quot;canvas&quot;).getContext(&quot;2d&quot;);
ctx.fillStyle = &quot;#FF0000&quot;; ctx.fillRect(0, 0, 1000, 1000);
ctx.fillStyle = &quot;#00FF00&quot;; ctx.fillRect(1000, 1000, 1000, 1000);
ctx.fillStyle = &quot;#0000FF&quot;; ctx.fillRect(2000, 2000, 1000, 1000);
ctx.fillStyle = &quot;#000000&quot;; 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 -->

&lt;div class=&quot;page&quot;&gt;
  &lt;div class=&quot;toolbar&quot;&gt;
    TOOLBAR OF HEIGHT 5 EM
  &lt;/div&gt;
  &lt;div class=&quot;container&quot;&gt;
    &lt;div class=&quot;left&quot;&gt;
      &lt;div class=&quot;canvas-wrapper&quot;&gt;
        &lt;canvas width=&quot;4000&quot; height=&quot;3000&quot;&gt;&lt;/canvas&gt;
        &lt;div class=&quot;button&quot;&gt;+&lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class=&quot;right&quot;&gt;
      RIGHT OF WIDTH 10 EM
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

<!-- 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(&quot;canvas&quot;).getContext(&quot;2d&quot;);
ctx.fillStyle = &quot;#FF0000&quot;;
ctx.fillRect(0, 0, 1000, 1000);
ctx.fillStyle = &quot;#00FF00&quot;;
ctx.fillRect(1000, 1000, 1000, 1000);
ctx.fillStyle = &quot;#0000FF&quot;;
ctx.fillRect(2000, 2000, 1000, 1000);
ctx.fillStyle = &quot;#000000&quot;;
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 -->

&lt;div class=&quot;page&quot;&gt;
  &lt;div class=&quot;toolbar&quot;&gt;
    TOOLBAR OF HEIGHT 5 EM
  &lt;/div&gt;
  &lt;div class=&quot;container&quot;&gt;
    &lt;div class=&quot;left&quot;&gt;
      &lt;div class=&quot;canvas-wrapper&quot; style=&quot;aspect-ratio: 4000 / 3000;&quot;&gt;
        &lt;canvas width=&quot;4000&quot; height=&quot;3000&quot;&gt;&lt;/canvas&gt;
        &lt;div class=&quot;button&quot;&gt;+&lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class=&quot;right&quot;&gt;
      RIGHT OF WIDTH 10 EM
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

<!-- 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(&quot;canvas&quot;).getContext(&quot;2d&quot;);
ctx.fillStyle = &quot;#FF0000&quot;;
ctx.fillRect(0, 0, 1000, 1000);
ctx.fillStyle = &quot;#00FF00&quot;;
ctx.fillRect(1000, 1000, 1000, 1000);
ctx.fillStyle = &quot;#0000FF&quot;;
ctx.fillRect(2000, 2000, 1000, 1000);
ctx.fillStyle = &quot;#000000&quot;;
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 -->

&lt;div class=&quot;page&quot;&gt;
  &lt;div class=&quot;toolbar&quot;&gt;
    TOOLBAR OF HEIGHT 5 EM
  &lt;/div&gt;
  &lt;div class=&quot;container&quot;&gt;
    &lt;div class=&quot;left&quot;&gt;
      &lt;div class=&quot;canvas-wrapper&quot;&gt;
        &lt;canvas width=&quot;4000&quot; height=&quot;3000&quot;&gt;&lt;/canvas&gt;
        &lt;div class=&quot;button&quot;&gt;+&lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class=&quot;right&quot;&gt;
      RIGHT OF WIDTH 10 EM
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

<!-- end snippet -->

huangapple
  • 本文由 发表于 2023年5月10日 17:38:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/76216924.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定