How to make a HTML5 canvas fit dynamic parent/flex box container

瘦欲@ 提交于 2021-01-20 19:00:28

问题


Is there a way I can create a canvas inside a dynamic re-sizing flex-box container?

Preferably a CSS only solution but I think JavaScript is required for redraw?

I think one solution could be to listen to the re-sizing event and then scale the canvas to meet the size of the flex box parent then force a redraw but I would preferably like to use as much CSS as possible or a more clean/less code solution.

The current approach is CSS based where the canvas is re-sized according to the parent flex box element. The graphics are blurred, re positioned and overflowed from the canvas in the below screenshot.

Stretched image/Graphics overflowing canvas

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 fiddle showing the issue: https://jsfiddle.net/h2v1w0a1/


回答1:


Think of canvas as an image. If you scale it to a different size than the original it will appear blurry at some point, and perhaps early on depending on interpolation algorithm chosen by the browser. For canvas the browser tend to chose bi-linear over bi-cubic so there is higher risk of blur than with an actual image.

You will want to have things in canvas rendered as sharp as possible and the only way to this is to adapt the size to the parent using JavaScript, and avoid CSS (the latter is good for things like printing, or when you need "retina resolutions").

To get the parent container size in pixels you can use getComputedStyle() (see update below):

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

(Note: Chrome seem to have some issues with computed flex at the moment)

Update

Here's a simpler way:

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

Fiddle




回答2:


The only CSS solution I can think about is the use of object-fit:none (related: 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>



回答3:


Using object-fit provides a pure CSS solution. I think you need to check for yourself which value is better to set to object-fit between cover and contain, there are two examples below.

$(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>

You will also find both examples in JSFiddle https://jsfiddle.net/vfsgpm3q/, https://jsfiddle.net/vfsgpm3q/1/




回答4:


One other solution is to create a new containing block, and make it follow the formatting context:

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

It is somewhat complimentary/orthogonal to the object-fit: ... suggested by Temani and Peter



来源:https://stackoverflow.com/questions/30229536/how-to-make-a-html5-canvas-fit-dynamic-parent-flex-box-container

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!