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

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

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不是用于定位,而只用于绘制。


  1. ctx = document.querySelector("canvas").getContext("2d");
  2. ctx.fillStyle = "#FF0000"; ctx.fillRect(0, 0, 1000, 1000);
  3. ctx.fillStyle = "#00FF00"; ctx.fillRect(1000, 1000, 1000, 1000);
  4. ctx.fillStyle = "#0000FF"; ctx.fillRect(2000, 2000, 1000, 1000);
  5. ctx.fillStyle = "#000000"; ctx.fillRect(3000, 0, 1000, 1000);

  1. * {
  2. padding: 0;
  3. margin: 0;
  4. }
  5. .page {
  6. display: flex;
  7. flex-direction: column;
  8. height: 100vh;
  9. }
  10. .toolbar {
  11. background-color: yellow;
  12. flex: 0 0 5em;
  13. }
  14. .container {
  15. background-color: orange;
  16. flex: 1 0 0;
  17. display: flex;
  18. }
  19. .left {
  20. background-color: magenta;
  21. flex: 1 0 0;
  22. display: flex;
  23. align-items: center;
  24. justify-content: center;
  25. }
  26. .right {
  27. background-color: salmon;
  28. flex: 0 0 10em;
  29. }
  30. .canvas-wrapper {
  31. background-color: grey;
  32. display: flex;
  33. position: relative;
  34. }
  35. canvas {
  36. height: 100px;
  37. background-color: white;
  38. }
  39. .button {
  40. background-color: yellow;
  41. position: absolute;
  42. top: 0;
  43. right: 0;
  44. width: 1em;
  45. height: 1em;
  46. }

  1. <div class="page">
  2. <div class="toolbar">
  3. TOOLBAR OF HEIGHT 5 EM
  4. </div>
  5. <div class="container">
  6. <div class="left">
  7. <div class="canvas-wrapper">
  8. <canvas width="4000" height="3000"></canvas>
  9. <div class="button">+</div>
  10. </div>
  11. </div>
  12. <div class="right">
  13. RIGHT OF WIDTH 10 EM
  14. </div>
  15. </div>
  16. </div>

  1. PS: 你必须在“全屏”中打开片段,以查看浏览器的缩放功能(CTRL +,CTRL -)。
  2. <details>
  3. <summary>英文:</summary>
  4. I have a full size page, containing a top toolbar, and below, a container with 2 columns:
  5. - a right column
  6. - 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)
  7. [![enter image description here][1]][1]
  8. Here it *nearly* works, but I can&#39;t make the canvas occupy the full available space in the left column.
  9. 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.
  10. **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?**
  11. Note: I&#39;m looking for a CSS solution, the JS is not for the positioning, but only the drawing.
  12. &lt;!-- begin snippet: js hide: false console: true babel: false --&gt;
  13. &lt;!-- language: lang-js --&gt;
  14. ctx = document.querySelector(&quot;canvas&quot;).getContext(&quot;2d&quot;);
  15. ctx.fillStyle = &quot;#FF0000&quot;; ctx.fillRect(0, 0, 1000, 1000);
  16. ctx.fillStyle = &quot;#00FF00&quot;; ctx.fillRect(1000, 1000, 1000, 1000);
  17. ctx.fillStyle = &quot;#0000FF&quot;; ctx.fillRect(2000, 2000, 1000, 1000);
  18. ctx.fillStyle = &quot;#000000&quot;; ctx.fillRect(3000, 0, 1000, 1000);
  19. &lt;!-- language: lang-css --&gt;
  20. * {
  21. padding: 0;
  22. margin: 0;
  23. }
  24. .page {
  25. display: flex;
  26. flex-direction: column;
  27. height: 100vh;
  28. }
  29. .toolbar {
  30. background-color: yellow;
  31. flex: 0 0 5em;
  32. }
  33. .container {
  34. background-color: orange;
  35. flex: 1 0 0;
  36. display: flex;
  37. }
  38. .left {
  39. background-color: magenta;
  40. flex: 1 0 0;
  41. display: flex;
  42. align-items: center;
  43. justify-content: center;
  44. }
  45. .right {
  46. background-color: salmon;
  47. flex: 0 0 10em;
  48. }
  49. .canvas-wrapper {
  50. background-color: grey;
  51. display: flex;
  52. position: relative;
  53. }
  54. canvas {
  55. height: 100px;
  56. background-color: white;
  57. }
  58. .button {
  59. background-color: yellow;
  60. position: absolute;
  61. top: 0;
  62. right: 0;
  63. width: 1em;
  64. height: 1em;
  65. }
  66. &lt;!-- language: lang-html --&gt;
  67. &lt;div class=&quot;page&quot;&gt;
  68. &lt;div class=&quot;toolbar&quot;&gt;
  69. TOOLBAR OF HEIGHT 5 EM
  70. &lt;/div&gt;
  71. &lt;div class=&quot;container&quot;&gt;
  72. &lt;div class=&quot;left&quot;&gt;
  73. &lt;div class=&quot;canvas-wrapper&quot;&gt;
  74. &lt;canvas width=&quot;4000&quot; height=&quot;3000&quot;&gt;&lt;/canvas&gt;
  75. &lt;div class=&quot;button&quot;&gt;+&lt;/div&gt;
  76. &lt;/div&gt;
  77. &lt;/div&gt;
  78. &lt;div class=&quot;right&quot;&gt;
  79. RIGHT OF WIDTH 10 EM
  80. &lt;/div&gt;
  81. &lt;/div&gt;
  82. &lt;/div&gt;
  83. &lt;!-- end snippet --&gt;
  84. PS: you have to open the snippet in &quot;Full page&quot; to see the browser zoom in / zoom out feature (CTRL + , CTRL -).
  85. [1]: https://i.stack.imgur.com/gXckTm.png
  86. </details>
  87. # 答案1
  88. **得分**: 2
  89. 你需要更改一些对齐方式,`width``height``flex-grow`,并添加一个HTML包装器(即`&lt;div class=&quot;canvas-wrapper-inner&quot;&gt;`)。
  90. 请参见下面的代码片段。
  91. ```html
  92. <div class="page">
  93. <div class="toolbar">
  94. TOOLBAR OF HEIGHT 5 EM
  95. </div>
  96. <div class="container">
  97. <div class="left">
  98. <div class="canvas-wrapper">
  99. <div class="canvas-wrapper-inner">
  100. <canvas width="4000" height="3000"></canvas>
  101. <div class="button">+</div>
  102. </div>
  103. </div>
  104. </div>
  105. <div class="right">
  106. RIGHT OF WIDTH 10 EM
  107. </div>
  108. </div>
  109. </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 -->

  1. ctx = document.querySelector(&quot;canvas&quot;).getContext(&quot;2d&quot;);
  2. ctx.fillStyle = &quot;#FF0000&quot;;
  3. ctx.fillRect(0, 0, 1000, 1000);
  4. ctx.fillStyle = &quot;#00FF00&quot;;
  5. ctx.fillRect(1000, 1000, 1000, 1000);
  6. ctx.fillStyle = &quot;#0000FF&quot;;
  7. ctx.fillRect(2000, 2000, 1000, 1000);
  8. ctx.fillStyle = &quot;#000000&quot;;
  9. ctx.fillRect(3000, 0, 1000, 1000);

<!-- language: lang-css -->

  1. * {
  2. padding: 0;
  3. margin: 0;
  4. }
  5. .page {
  6. display: flex;
  7. flex-direction: column;
  8. height: 100vh;
  9. }
  10. .toolbar {
  11. background-color: yellow;
  12. flex: 0 0 5em;
  13. }
  14. .container {
  15. background-color: orange;
  16. flex: 1 0 0;
  17. display: flex;
  18. }
  19. .left {
  20. background-color: magenta;
  21. flex: 1 0 0;
  22. display: flex;
  23. align-items: center;
  24. justify-content: center;
  25. }
  26. .right {
  27. background-color: salmon;
  28. flex: 0 0 10em;
  29. }
  30. .canvas-wrapper {
  31. background-color: grey;
  32. display: flex;
  33. position: relative;
  34. flex-grow: 1; /* Added */
  35. align-self: stretch; /* Added */
  36. align-items: center; /* Added */
  37. justify-content: center; /* Added */
  38. }
  39. canvas {
  40. width: 100%; /* Added */
  41. flex-grow: 1; /* Added */
  42. object-fit: contain; /* Added */
  43. max-height: calc(100vh - 5em); /* Added */
  44. }
  45. .button {
  46. background-color: yellow;
  47. position: absolute;
  48. top: 0;
  49. right: 0;
  50. width: 1em;
  51. height: 1em;
  52. }
  53. /* Added */
  54. .canvas-wrapper-inner {
  55. position: relative;
  56. }

<!-- language: lang-html -->

  1. &lt;div class=&quot;page&quot;&gt;
  2. &lt;div class=&quot;toolbar&quot;&gt;
  3. TOOLBAR OF HEIGHT 5 EM
  4. &lt;/div&gt;
  5. &lt;div class=&quot;container&quot;&gt;
  6. &lt;div class=&quot;left&quot;&gt;
  7. &lt;div class=&quot;canvas-wrapper&quot;&gt;
  8. &lt;div class=&quot;canvas-wrapper-inner&quot;&gt;
  9. &lt;canvas width=&quot;4000&quot; height=&quot;3000&quot;&gt;&lt;/canvas&gt;
  10. &lt;div class=&quot;button&quot;&gt;+&lt;/div&gt;
  11. &lt;/div&gt;
  12. &lt;/div&gt;
  13. &lt;/div&gt;
  14. &lt;div class=&quot;right&quot;&gt;
  15. RIGHT OF WIDTH 10 EM
  16. &lt;/div&gt;
  17. &lt;/div&gt;
  18. &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.

  1. ctx = document.querySelector("canvas").getContext("2d");
  2. ctx.fillStyle = "#FF0000"; ctx.fillRect(0, 0, 1000, 1000);
  3. ctx.fillStyle = "#00FF00"; ctx.fillRect(1000, 1000, 1000, 1000);
  4. ctx.fillStyle = "#0000FF"; ctx.fillRect(2000, 2000, 1000, 1000);
  5. ctx.fillStyle = "#000000"; ctx.fillRect(3000, 0, 1000, 1000);
  6. * {
  7. padding: 0;
  8. margin: 0;
  9. }
  10. .page {
  11. display: flex;
  12. flex-direction: column;
  13. height: 100vh;
  14. }
  15. .toolbar {
  16. background-color: yellow;
  17. flex: 0 0 5em;
  18. }
  19. .container {
  20. background-color: orange;
  21. flex: 1 0 0;
  22. display: flex;
  23. }
  24. .left {
  25. container-type: size;
  26. background-color: magenta;
  27. flex: 1 0 0;
  28. display: flex;
  29. align-items: center;
  30. justify-content: center;
  31. }
  32. .right {
  33. background-color: salmon;
  34. flex: 0 0 10em;
  35. }
  36. .canvas-wrapper {
  37. position: relative;
  38. width: 100cqmin;
  39. }
  40. .button {
  41. background-color: yellow;
  42. position: absolute;
  43. top: 0;
  44. right: 0;
  45. width: 1em;
  46. height: 1em;
  47. }
  48. canvas {
  49. width: 100%;
  50. aspect-ratio: 4/3;
  51. background-color: white;
  52. }
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 -->

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

<!-- language: lang-css -->

  1. * {
  2. padding: 0;
  3. margin: 0;
  4. }
  5. .page {
  6. display: flex;
  7. flex-direction: column;
  8. height: 100vh;
  9. }
  10. .toolbar {
  11. background-color: yellow;
  12. flex: 0 0 5em;
  13. }
  14. .container {
  15. background-color: orange;
  16. flex: 1 0 0;
  17. display: flex;
  18. }
  19. .left {
  20. container-type: size;
  21. background-color: magenta;
  22. flex: 1 0 0;
  23. display: flex;
  24. align-items: center;
  25. justify-content: center;
  26. }
  27. .right {
  28. background-color: salmon;
  29. flex: 0 0 10em;
  30. }
  31. .canvas-wrapper {
  32. position: relative;
  33. width: 100cqmin;
  34. }
  35. .button {
  36. background-color: yellow;
  37. position: absolute;
  38. top: 0;
  39. right: 0;
  40. width: 1em;
  41. height: 1em;
  42. }
  43. canvas {
  44. width: 100%;
  45. aspect-ratio: 4/3;
  46. background-color: white;
  47. }

<!-- language: lang-html -->

  1. &lt;div class=&quot;page&quot;&gt;
  2. &lt;div class=&quot;toolbar&quot;&gt;
  3. TOOLBAR OF HEIGHT 5 EM
  4. &lt;/div&gt;
  5. &lt;div class=&quot;container&quot;&gt;
  6. &lt;div class=&quot;left&quot;&gt;
  7. &lt;div class=&quot;canvas-wrapper&quot;&gt;
  8. &lt;canvas width=&quot;4000&quot; height=&quot;3000&quot;&gt;&lt;/canvas&gt;
  9. &lt;div class=&quot;button&quot;&gt;+&lt;/div&gt;
  10. &lt;/div&gt;
  11. &lt;/div&gt;
  12. &lt;div class=&quot;right&quot;&gt;
  13. RIGHT OF WIDTH 10 EM
  14. &lt;/div&gt;
  15. &lt;/div&gt;
  16. &lt;/div&gt;

<!-- end snippet -->

答案3

得分: 1

The canvas-wrapper 需要与画布具有相同的大小和位置,但画布的大小和位置基于其父级。一个简单的解决方案是将画布的尺寸复制为其父级的 aspect-ratio

  1. <div class="canvas-wrapper" style="aspect-ratio: 4000 / 3000;">
  2. <canvas width="4000" height="3000"></canvas>
  3. <div class="button">+</div>
  4. </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 -->

  1. ctx = document.querySelector(&quot;canvas&quot;).getContext(&quot;2d&quot;);
  2. ctx.fillStyle = &quot;#FF0000&quot;;
  3. ctx.fillRect(0, 0, 1000, 1000);
  4. ctx.fillStyle = &quot;#00FF00&quot;;
  5. ctx.fillRect(1000, 1000, 1000, 1000);
  6. ctx.fillStyle = &quot;#0000FF&quot;;
  7. ctx.fillRect(2000, 2000, 1000, 1000);
  8. ctx.fillStyle = &quot;#000000&quot;;
  9. ctx.fillRect(3000, 0, 1000, 1000);

<!-- language: lang-css -->

  1. body {
  2. margin: 0;
  3. }
  4. .page {
  5. height: 100vh;
  6. display: flex;
  7. flex-direction: column;
  8. }
  9. .toolbar {
  10. background-color: yellow;
  11. flex: 0 0 5em;
  12. }
  13. .container {
  14. background-color: orange;
  15. flex: 1 1 auto;
  16. /* wrapper for two column layout */
  17. display: flex;
  18. flex-direction: row;
  19. }
  20. .left {
  21. background-color: magenta;
  22. flex: 1 1 auto;
  23. /* reference element for size and position */
  24. position: relative;
  25. }
  26. .right {
  27. background-color: salmon;
  28. flex: 0 0 10em;
  29. align-self: center;
  30. }
  31. .canvas-wrapper {
  32. background-color: gray;
  33. position: absolute;
  34. left: 0;
  35. right: 0;
  36. top: 0;
  37. bottom: 0;
  38. margin: auto;
  39. max-width: 100%;
  40. max-height: 100%;
  41. }
  42. canvas {
  43. display: block;
  44. width: 100%;
  45. height: 100%;
  46. }
  47. .button {
  48. background-color: cyan;
  49. position: absolute;
  50. top: 0;
  51. right: 0;
  52. padding: .5em;
  53. }

<!-- language: lang-html -->

  1. &lt;div class=&quot;page&quot;&gt;
  2. &lt;div class=&quot;toolbar&quot;&gt;
  3. TOOLBAR OF HEIGHT 5 EM
  4. &lt;/div&gt;
  5. &lt;div class=&quot;container&quot;&gt;
  6. &lt;div class=&quot;left&quot;&gt;
  7. &lt;div class=&quot;canvas-wrapper&quot; style=&quot;aspect-ratio: 4000 / 3000;&quot;&gt;
  8. &lt;canvas width=&quot;4000&quot; height=&quot;3000&quot;&gt;&lt;/canvas&gt;
  9. &lt;div class=&quot;button&quot;&gt;+&lt;/div&gt;
  10. &lt;/div&gt;
  11. &lt;/div&gt;
  12. &lt;div class=&quot;right&quot;&gt;
  13. RIGHT OF WIDTH 10 EM
  14. &lt;/div&gt;
  15. &lt;/div&gt;
  16. &lt;/div&gt;

<!-- end snippet -->

答案4

得分: 1

这是基于RokBenko的回答的答案,似乎可以工作,无需使用第二个包装和第二个flex:

  1. <div class="page">
  2. <div class="toolbar">
  3. TOOLBAR OF HEIGHT 5 EM
  4. </div>
  5. <div class="container">
  6. <div class="left">
  7. <div class="canvas-wrapper">
  8. <canvas width="4000" height="3000"></canvas>
  9. <div class="button">+</div>
  10. </div>
  11. </div>
  12. <div class="right">
  13. RIGHT OF WIDTH 10 EM
  14. </div>
  15. </div>
  16. </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 -->

  1. ctx = document.querySelector(&quot;canvas&quot;).getContext(&quot;2d&quot;);
  2. ctx.fillStyle = &quot;#FF0000&quot;;
  3. ctx.fillRect(0, 0, 1000, 1000);
  4. ctx.fillStyle = &quot;#00FF00&quot;;
  5. ctx.fillRect(1000, 1000, 1000, 1000);
  6. ctx.fillStyle = &quot;#0000FF&quot;;
  7. ctx.fillRect(2000, 2000, 1000, 1000);
  8. ctx.fillStyle = &quot;#000000&quot;;
  9. ctx.fillRect(3000, 0, 1000, 1000);

<!-- language: lang-css -->

  1. * {
  2. padding: 0;
  3. margin: 0;
  4. }
  5. .page {
  6. display: flex;
  7. flex-direction: column;
  8. height: 100vh;
  9. }
  10. .toolbar {
  11. background-color: yellow;
  12. flex: 0 0 5em;
  13. }
  14. .container {
  15. background-color: orange;
  16. flex: 1 0 0;
  17. display: flex;
  18. }
  19. .left {
  20. background-color: magenta;
  21. flex: 1 0 0;
  22. display: flex;
  23. align-items: center;
  24. justify-content: center;
  25. }
  26. .right {
  27. background-color: salmon;
  28. flex: 0 0 10em;
  29. }
  30. .canvas-wrapper {
  31. background-color: #aaa;
  32. position: relative;
  33. }
  34. canvas {
  35. background-color: white;
  36. width: 100%;
  37. max-height: calc(100vh - 5em);
  38. }
  39. .button {
  40. background-color: yellow;
  41. position: absolute;
  42. top: 0;
  43. right: 0;
  44. width: 1em;
  45. height: 1em;
  46. }

<!-- language: lang-html -->

  1. &lt;div class=&quot;page&quot;&gt;
  2. &lt;div class=&quot;toolbar&quot;&gt;
  3. TOOLBAR OF HEIGHT 5 EM
  4. &lt;/div&gt;
  5. &lt;div class=&quot;container&quot;&gt;
  6. &lt;div class=&quot;left&quot;&gt;
  7. &lt;div class=&quot;canvas-wrapper&quot;&gt;
  8. &lt;canvas width=&quot;4000&quot; height=&quot;3000&quot;&gt;&lt;/canvas&gt;
  9. &lt;div class=&quot;button&quot;&gt;+&lt;/div&gt;
  10. &lt;/div&gt;
  11. &lt;/div&gt;
  12. &lt;div class=&quot;right&quot;&gt;
  13. RIGHT OF WIDTH 10 EM
  14. &lt;/div&gt;
  15. &lt;/div&gt;
  16. &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:

确定