Canvas растягивается при использовании CSS, но обычно со свойствами "width" / "height"

У меня есть 2 холста, один использует атрибуты HTML width а также height чтобы определить его размер, другой использует 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.

Почему разница в отображении?

альтернативный текст

10 ответов

Решение

Кажется, что width а также height атрибуты определяют ширину или высоту системы координат холста, тогда как свойства CSS просто определяют размер поля, в котором он будет отображаться.

Это объясняется по адресу http://www.whatwg.org/html (требуется JS) или http://www.whatwg.org/c (вероятно, будет кушать ваш компьютер):

canvas Элемент имеет два атрибута для управления размером растрового изображения элемента: width а также height, Эти атрибуты, если они указаны, должны иметь значения, которые являются действительными неотрицательными целыми числами. Правила для анализа неотрицательных целых чисел должны использоваться для получения их числовых значений. Если атрибут отсутствует или анализ его значения возвращает ошибку, то вместо этого должно использоваться значение по умолчанию. width атрибут по умолчанию равен 300, а height атрибут по умолчанию равен 150.

Чтобы установить width а также height вам нужно, вы можете использовать

canvasObject.setAttribute('width', '475');

За <canvas> элементы, правила CSS для width а также height установите фактический размер элемента canvas, который будет отображаться на странице. С другой стороны, атрибуты HTML width а также height установите размер системы координат или "сетки", которую будет использовать API Canvas.

Например, рассмотрим это ( 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.

Примечание: если правила CSS для width и / или height не указываются, тогда браузер будет использовать атрибуты HTML для определения размера элемента таким образом, чтобы 1 единица этих значений равнялась 1 пикселю на странице. Если эти атрибуты не указаны, по умолчанию они width из 300 и height из 150,

Холст будет растянут, если вы установите ширину и высоту в своем CSS. Если вы хотите динамически манипулировать размером холста, вы должны использовать JavaScript следующим образом:

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

Браузер использует ширину и высоту CSS, но элемент холста масштабируется в зависимости от ширины и высоты холста. В javascript прочитайте ширину и высоту css и установите ширину и высоту холста.

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

Шеннимальная коррекция

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

холст отображает изображение по буферу, поэтому, когда вы указываете атрибуты html ширины и высоты, размер и длина буфера изменяются, но когда вы используете css, размер буфера не изменяется. делая изображение растянутым.

Использование размера html.

размер холста изменен -> размер буфера изменен -> визуализирован

Использование css sizing

размер холста изменен -> визуализирован

поскольку длина буфера остается неизменной, когда контекст визуализирует изображение, изображение отображается на холсте с измененным размером (но визуализируется в неизмененном буфере) .

CSS устанавливает ширину и высоту элемента холста, поэтому он влияет на пространство координат, оставляя все нарисованное перекошенным.

Вот мой способ установки ширины и высоты с помощью Vanilla JavaScript

canvas.width = numberForWidth

canvas.height = numberForHeight

Если вы хотите динамическое поведение, основанное, например, на медиазапросах CSS, не используйте атрибуты width и height. Используйте правила 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 есть гораздо лучший механизм для указания размера холста, и именно CSS должен определять стиль, а не JavaScript или HTML. Сказав это, установка ширины и высоты в HTML важна для решения проблемы с холстом.

CSS имеет!importantправило, которое позволяет переопределить другие правила стиля для свойства, в том числе в HTML. Обычно его использование не одобряется, но здесь это использование является законным взломом.

В модуле Rust для WebAssembly вы можете сделать следующее:

      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, а затем всякий раз, когда изменяется размер окна. Мы делаем это, указав вручнуюwidthиheightхолста как значенияclientWidthиclientHeight. Возможно, есть лучшие способы обновить буфер, но я считаю, что это решение лучше, чем те, которые предложены @SamB, @CoderNaveed, @Anthony Gedeon, @Bluerain, @Ben Jackson, @Manolo, @XaviGuardia, @Russel Harkins и @fermar. потому что

  1. Элемент оформлен с помощью CSS, а не HTML.
  2. В отличие отelem.style.width&elem.style.heightтрюк, используемый @Manolo или его эквивалент JQuery, используемый @XaviGuardia, он будет работать дляcanvasразмер которого определяется использованием какflexилиgridэлемент.
  3. В отличие от решения @Russel Harkings, это также обрабатывает изменение размера. Хотя мне нравится его ответ, потому что он действительно чистый и простой.
  4. WASM - это будущее! Ха-ха :D

PS есть тонна.unwrap()потому что Rust явно обрабатывает возможные сбои.

PPS

          {
        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);
})
Другие вопросы по тегам