【问题标题】:How to make a HTML5 canvas fit dynamic parent/flex box container如何使 HTML5 画布适合动态父/弹性框容器
【发布时间】:2015-07-25 14:37:21
【问题描述】:

有没有办法可以在动态调整大小的 flex-box 容器中创建画布?

最好是纯 CSS 解决方案,但我认为重绘需要 JavaScript?

我认为一种解决方案可能是监听 re-sizing 事件,然后缩放画布以满足 flex box 父级的大小,然后强制重绘,但我希望尽可能多地使用 CSS 或更多干净/更少的代码解决方案。

当前的方法是基于 CSS 的,画布根据父弹性框元素重新调整大小。在下面的屏幕截图中,图形被模糊、重新定位并从画布溢出。

CSS:

html,body{
            margin:0;
            width:100%;
            height:100%;
        }
        body{
            display:flex;
            flex-direction:column;
        }
header{
            width:100%;
            height:40px;
            background-color:red;
        }
        main{
            display:flex;
            flex: 1 1 auto;
            border: 1px solid blue;
            width:80vw;
        }
        canvas{
            flex: 1 1 auto;
            background-color:black;
        }

HTML:

<header>
</header>
<main>
    <canvas id="stage"></canvas>
</main>

Javascript:

$( document ).ready(function() {
    var ctx = $("#stage")[0].getContext("2d");
    ctx.strokeStyle="#FFFFFF";
    ctx.beginPath();
    ctx.arc(100,100,50,0,2*Math.PI);
    ctx.stroke();
});

显示问题的 JS 小提琴:https://jsfiddle.net/h2v1w0a1/

【问题讨论】:

  • canvas 正在占用其父级的宽度,即 80vw。 100vw 可以解决你的问题
  • no.. 这是有目的的,菜单稍后会在它的右侧。图形缩放是问题,可能需要进行编辑,以免其他人有相同的假设。
  • @Dima 你能澄清一下你所谓的“原始问题”吗?当我读到它时,当前接受的答案完美地回答了它(除了它可以从使用 ResizeObserver 的更新中获胜)。为了使画布“适合”在动态容器中,它必须调整大小,或者您是否正在考虑其他行为?如果有,是哪一个?
  • 也许你是对的,也许我看不懂 :) 我想,最终,我希望有两种选择:画布跟随容器大小,改变和不改变画布(内部)像素大小。
  • 我认为您正在寻找 object-fit 或纯粹的 CSS 解决方案。这是一个jsfiddle.net/vfsgpm3q

标签: javascript jquery html css flexbox


【解决方案1】:

将画布视为图像。如果将其缩放到与原始大小不同的大小,它会在某些时候显得模糊,并且可能在早期取决于浏览器选择的插值算法。对于画布,浏览器倾向于选择双线性而不是双三次,因此与实际图像相比,模糊的风险更高。

您会希望画布中的内容尽可能清晰,唯一的方法是使用 JavaScript 调整大小以适应父级,并避免使用 CSS(后者适用于打印等内容,或者当您需要时) “视网膜分辨率”)。

要获取以像素为单位的父容器大小,您可以使用getComputedStyle()(请参阅下面的更新):

var parent = canvas.parentNode,
    styles = getComputedStyle(parent),
    w = parseInt(styles.getPropertyValue("width"), 10),
    h = parseInt(styles.getPropertyValue("height"), 10);

canvas.width = w;
canvas.height = h;

Fiddle

(注意:Chrome 目前似乎在计算 flex 方面存在一些问题)

更新

这里有一个更简单的方法:

var rect = canvas.parentNode.getBoundingClientRect();
canvas.width = rect.width;
canvas.height = rect.height;

Fiddle

【讨论】:

  • 在画布上添加 style="position:absolute" 使它对我来说也适用于边缘。
  • 第二个小提琴链接坏了。
  • @Glenn - 这也帮助了我使用 chrome。我已经获得了画布父级的大小并将画布设置为该大小,但是当我将那个父级放入弹性盒时,画布就会破裂。设置位置:画布上的绝对固定它。
【解决方案2】:

我能想到的唯一CSS解决方案是使用object-fit:none(相关:How does object-fit work with canvas element?

$(document).ready(function() {
  var ctx = $("#stage")[0].getContext("2d");
  ctx.strokeStyle = "#FFFFFF";
  ctx.beginPath();
  ctx.arc(100, 100, 50, 0, 2 * Math.PI);
  ctx.stroke();
});
html,
body {
  margin: 0;
  width: 100%;
  height: 100%;
}

body {
  display: flex;
  flex-direction: column;
}

header {
  width: 100%;
  height: 40px;
  background-color: red;
}

main {
  display: flex;
  flex: 1 1 auto;
  border: 1px solid blue;
  width: 80vw;
}

canvas {
  flex: 1 1 auto;
  background-color: black;
  object-fit:none;
  object-position:top left;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<header>
</header>
<main>
  <canvas id="stage"></canvas>
</main>

【讨论】:

  • object-fit 如果设置为 none 肯定会起作用,但它不会调整画布大小。在这种情况下,Object-fit 设置为 contains 似乎比设置为 none 更合适。
  • @PeterDarmis 检查接受的答案的代码,您会看到目标是来调整画布内容的大小。包含和覆盖将调整内容的大小,这不是这里的目的
  • 很可能我一定没有正确阅读问题中的这一部分... 我认为一种解决方案可能是收听重新调整大小的事件,然后缩放画布以满足大小flex box 父级然后强制重绘,但我希望使用尽可能多的 CSS 或更干净/更少的代码解决方案。
  • @PeterDarmis 缩放画布以满足弹性框父级的大小 --> 将画布的宽度/高度调整为父级之一,这将扭曲内容,这就是为什么:*然后强制重绘*->考虑到画布的新宽度/高度,我们再次重绘以获得准确的结果。查看问题的 jsfiddle(画布的内容被扭曲/更改)与内容保持不变的已接受答案的 jsfiddle 之间的区别。这就是我在这里所做的,这是使用 object-fit:none;
  • 我不知道我什么时候回答我没有阅读其他答案或 cmets,我只阅读了问题并看到圆圈在提供的 jsfiddle 中被扭曲的事实。对我来说,这并不意味着问题不希望画布相应地调整大小。如果我在最初阅读该问题时错了,则不会将 case object-fit 设置为 none 起作用。但我真的对我上面评论中提到的那句话感到困扰。我真的认为它是要调整大小的,我还没有看到问题持有者来定义它。最好的问候,对不起我的评论。
【解决方案3】:

使用object-fit 提供了一个纯粹的CSS 解决方案。我认为您需要自己检查在covercontain 之间将哪个值设置为object-fit 更好,下面有两个示例。

$(document).ready(function() {
  var ctx = $("#stage")[0].getContext("2d");
  ctx.strokeStyle = "#FFFFFF";
  ctx.beginPath();
  ctx.arc(100, 100, 50, 0, 2 * Math.PI);
  ctx.stroke();
});
html,
body {
  margin: 0;
  width: 100%;
  height: 100%;
}

body {
  display: flex;
  flex-direction: column;
}

header {
  width: 100%;
  height: 40px;
  background-color: red;
}

main {
  display: flex;
  flex: 1 1 auto;
  border: 1px solid blue;
  width: 80vw;
}

canvas {
  object-fit: cover;
  background-color: black;
  width: 100%;
  height: auto;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<header>
</header>
<main>
  <canvas id="stage"></canvas>
</main>

$(document).ready(function() {
  var ctx = $("#stage")[0].getContext("2d");
  ctx.strokeStyle = "#FFFFFF";
  ctx.beginPath();
  ctx.arc(100, 100, 50, 0, 2 * Math.PI);
  ctx.stroke();
});
html,
body {
  margin: 0;
  width: 100%;
  height: 100%;
}

body {
  display: flex;
  flex-direction: column;
}

header {
  width: 100%;
  height: 40px;
  background-color: red;
}

main {
  display: flex;
  flex: 1 1 auto;
  border: 1px solid blue;
  width: 80vw;
}

canvas {
  object-fit: contain;
  background-color: black;
  width: 100%;
  height: auto;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<header>
</header>
<main>
  <canvas id="stage"></canvas>
</main>

您还可以在 JSFiddle https://jsfiddle.net/vfsgpm3q/https://jsfiddle.net/vfsgpm3q/1/ 中找到这两个示例

【讨论】:

    【解决方案4】:

    另一种解决方案是创建一个新的包含块,并使其遵循格式化上下文:

    const Canvase = styled.canvas`
      flex: auto;
      position: relative;
      left: 0;
      top: 0;
      width: 100%;
      height: 100%;
    `;
    

    它与 Temani 和 Peter 建议的 object-fit: ... 有点互补/正交

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-15
      • 2018-10-22
      • 2014-02-15
      • 2016-11-21
      相关资源
      最近更新 更多