Canvas在使用CSS时是拉伸的,但是使用“width”是正常的。/“height"属性

我有2个画布,一个使用HTML属性widthheight来调整它的大小,另一个使用CSS:

<canvas id="compteur1" width="300" height="300" onmousedown="compteurClick(this.id);"></canvas>
<canvas id="compteur2" style="width: 300px; height: 300px;" onmousedown="compteurClick(this.id);"></canvas>

Compteur1正常显示,但compteur2不正常。内容使用JavaScript在300x300的画布上绘制。

为什么会有显示差异?

alt text

90523 次浏览

widthheight属性似乎决定了画布坐标系统的宽度或高度,而CSS属性只决定了它将显示在其中的框的大小。

这在HTML规范中解释:

canvas元素有两个属性来控制元素位图的大小:widthheight。当指定这些属性时,必须具有有效的非负整数的值。必须使用解析非负整数的规则来获取它们的数值。如果某个属性缺失,或者解析其值返回错误,则必须使用默认值。width属性默认值为300,而height属性默认值为150。

要在画布上设置widthheight,你可以使用:

canvasObject.setAttribute('width', '150');
canvasObject.setAttribute('height', '300');

如果你在CSS中设置了宽度和高度,画布将被拉伸。如果你想动态地操纵画布的尺寸,你必须像这样使用JavaScript:

canvas = document.getElementById('canv');
canvas.setAttribute('width', '438');
canvas.setAttribute('height', '462');

对于<canvas>元素,widthheight的CSS规则设置了将绘制到页面的画布元素的实际大小。另一方面,widthheight的HTML属性设置了画布API将使用的坐标系或“网格”的大小。

例如,考虑这个(jsfiddle):

.
var ctx = document.getElementById('canvas1').getContext('2d');
ctx.fillStyle = "red";
ctx.fillRect(10, 10, 30, 30);


var ctx2 = document.getElementById('canvas2').getContext('2d');
ctx2.fillStyle = "red";
ctx2.fillRect(10, 10, 30, 30);
canvas {
border: 1px solid black;
}
<canvas id="canvas1" style="width: 50px; height: 100px;" height="50" width="100"></canvas>
<canvas id="canvas2" style="width: 100px; height: 100px;" height="50" width="100"></canvas>

相对于canvas元素的内部坐标,两者都有相同的东西被画在上面。但在第二个画布中,红色矩形的宽度将是原来的两倍,因为根据CSS规则,整个画布将被拉伸到更大的区域。

注意:如果没有指定width和/或height的CSS规则,那么浏览器将使用HTML属性来调整元素的大小,使这些值的1个单位等于页面上的1px。如果没有指定这些属性,那么它们将默认为300width150height

浏览器使用css的宽度和高度,但canvas元素根据画布的宽度和高度缩放。在javascript中,读取css的宽度和高度,并设置画布的宽度和高度。

var myCanvas = $('#TheMainCanvas');
myCanvas[0].width = myCanvas.width();
myCanvas[0].height = myCanvas.height();

Shannimal校正

var el = $('#mycanvas');
el.attr('width', parseInt(el.css('width')))
el.attr('height', parseInt(el.css('height')))

如果你想要一个动态的行为,例如CSS媒体查询,不要使用画布的宽度和高度属性。使用CSS规则,然后,在获得画布渲染上下文之前,为width和height属性分配CSS width和height样式:

var elem = document.getElementById("mycanvas");


elem.width = elem.style.width;
elem.height = elem.style.height;


var ctx1 = elem.getContext("2d");
...

CSS设置画布元素的宽度和高度,因此它会影响坐标空间,使所有内容都倾斜绘制

下面是我如何用Vanilla JavaScript设置宽度和高度的方法

canvas.width = numberForWidth


canvas.height = numberForHeight

Canvas通过缓冲区渲染图像,因此当你指定HTML属性的宽度和高度时,缓冲区的大小和长度会发生变化,但当你使用css时,缓冲区的大小是不变的。使图像拉伸。

使用html调整大小。

canvas的大小被改变->缓冲区大小改变->呈现

使用css调整大小

canvas的大小被改变->呈现

,因为缓冲区长度保持不变,当上下文渲染图像时, 图像显示在调整大小的画布(但在未更改的缓冲区中呈现).

我相信CSS有更好的机制来指定画布的大小,CSS必须决定样式,而不是JavaScript或HTML。话虽如此,在HTML中设置宽度和高度对于解决canvas问题很重要。

CSS有!important规则,允许覆盖属性的其他样式规则,包括HTML中的规则。通常,它的使用是不受欢迎的,但在这里使用是合法的黑客

在WebAssembly的Rust模块中,你可以做以下事情:

fn update_buffer(canvas: &HtmlCanvasElement) {
canvas.set_width(canvas.client_width() as u32);
canvas.set_height(canvas.client_height() as u32);
}


//..
#[wasm_bindgen(start)]
pub fn start() -> Result<(), JsValue> {
// ...
let canvas: Rc<_> = document
.query_selector("canvas")
.unwrap()
.unwrap()
.dyn_into::<HtmlCanvasElement>()
.unwrap()
.into();
update_buffer(&canvas);
// ...


// create resizing handler for window
{
let on_resize = Closure::<dyn FnMut(_)>::new(move |_event: Event| {
let canvas = canvas.clone();
// ...


update_buffer(&canvas);
// ...
window.add_event_listener_with_callback("resize", on_resize.as_ref().unchecked_ref())?;
on_resize.forget();
}
}

在那里,一旦WASM模块被加载,我们就更新画布缓冲区,然后每当窗口大小被调整时。我们通过手动指定canvas的widthheight作为clientWidthclientHeight的值来实现。也许有更好的方法来更新缓冲区,但我相信这个解决方案比@SamB, @CoderNaveed, @Anthony Gedeon, @Bluerain, @Ben Jackson, @Manolo, @XaviGuardia, @Russel Harkins和@fermar的建议更好,因为

  1. 元素的样式是CSS,而不是HTML。
  2. 不像elem.style.width &@Manolo使用的elem.style.height技巧或@XaviGuardia使用的JQuery等效物,它将适用于canvas,其大小由用法指定为flexgrid项。
  3. 不像@Russel Harkings的解决方案,这也处理调整大小。虽然我喜欢他的答案,因为它真的是干净和简单。
  4. WASM是未来!哈哈:D

附注:有大量的.unwrap(),因为Rust显式地处理可能的失败。

P.P.S.

    {
let on_resize = Closure::<dyn FnMut(_)>::new(move |_event: Event| {
let canvas = canvas.clone();
// ...


update_buffer(&canvas);
// ...
window.add_event_listener_with_callback("resize", on_resize.as_ref().unchecked_ref())?;
on_resize.forget();
}

使用更好的库可以更干净地完成。如。

add_resize_handler(&window, move |e: ResizeEvent| {
let canvas = canvas.clone();
// ...


update_buffer(&canvas);
})