问题
As you know, the <canvas>
element in HTML5 has an internal size, set via elm.width
and elm.height
where elm
is the DOM element.
The element within the DOM itself can have a different box size, set via CSS.
When the two differ, the browser automatically stretches the contents of the <canvas>
to fit the content box size. Can this behaviour be adjusted without an extra element? Can it be set to something like background-size: contain
so that it keeps the aspect ratio of the inner canvas size?
As far as I know, there is no standard way to do this, so webkit-specific hacks are also accepted. But if there is no way to do it at all, what's the most elegant to do it with a wrapper element (we all know that we can wrap the element and adjust it via JS on resize)?
The one solution I can think of is actually using background-size: contain
and background: element()
but this is Firefox-specific, and I am looking for a WebKit solution.
Side question, can a difference in element box and canvas internal sizes impact performance? If so, how much?
回答1:
Yes, there is a new CSS property called object-fit. This allow you to set the content to for example cover
which will then force aspect ratio to be kept at the same time as filling the DOM box.
Simply add this rule to the canvas element:
canvas {
object-fit: cover;
}

Support
(Updated) It is (partially) supported in Safari, but not in IE. Opera Mini needs a -o-
prefix.
Does not seem to work with WebGL canvas (or video) in Chrome likely related to these issues: issue #1, issue #2, issue #3.
As a fallback the size can be calculated manually. This can be used with drawImage. If drawImage is not an option it will require a wrapper element with overflow set to hidden to work.
Example
Both canvas' below are using the default bitmap size of 300x150. They are then stretched by defining CSS size 300x300 pixels.
Normally this would make the circle an oval. However, the canvas on the right will use the new object-fit
rule set to cover
, the bitmap will be scaled considering aspect ratio so we still get a circle, but of course we'll also loose some of the edges (as well as sharpness).
var ctx1 = document.getElementById("c1").getContext("2d");
var ctx2 = document.getElementById("c2").getContext("2d");
drawCircle(ctx1, 150, 75, 70); // draw circle to stretched canvas 1
drawCircle(ctx2, 150, 75, 70); // draw circle to object-fit/cover canvas 2
function drawCircle(ctx, x, y, r) {
ctx.moveTo(x + r, y);
ctx.arc(x, y, 70, 0 , 6.28);
ctx.closePath();
ctx.stroke();
}
#c1, #c2 {
width:300px;
height:300px;
}
#c2 {
-o-object-fit: cover; /* for Opera Mini */
object-fit: cover; /* W3C version (incl. FF/webkit) */
}
<canvas id="c1"></canvas><canvas id="c2"></canvas>
来源:https://stackoverflow.com/questions/30483874/aspect-ratio-behaviour-of-canvas-internal-dimensions-and-element-size