Максимальний розмір елемента <canvas>


112

Я працюю з елементом полотна з висотою 600до1000 пікселів і шириною в кілька десятків або сотень тисяч пікселів. Однак після певної кількості пікселів (очевидно, невідомих) полотно більше не відображає фігури, які я малюю за допомогою JS.

Хтось знає, чи є обмеження?

Тестовано як у Chrome 12, так і в Firefox 4.


2
@ Šime Він сказав tens OR hundreds of thousands...
Tadeck

6
Я теж переживаю це. У мене є полотно 8000x8000, яке працює нормально, але коли я збільшую його вміст зникає, і він просто не малюватиме. Розмір, на який він не працює, на моєму iPad значно нижчий. Цікаво, чи це обмеження пам'яті якесь.
Джошуа

28
Дивно, коли ти знаходиш свій власний коментар на 2 роки раніше, і ти все ще маєш справу з тією ж проблемою.
Джошуа

4
@Joshua та ще через рік!
Євген Ю.

1
Питання полягає в тому, як я вловлюю ці помилки чи краще попередження, що це таке? Я не можу виявити апаратне забезпечення пристроїв, тому мені доведеться реагувати на помилку.
br4nnigan

Відповіді:


115

Оновлено 13.10.2014

Усі перевірені веб-переглядачі мають обмеження по висоті / ширині елементів полотна, але багато браузерів також обмежують загальну площу елемента полотна. Для браузерів, які я можу перевірити, обмеження такі:

Chrome:

Максимальна висота / ширина: 32,767 пікселів
Максимальна площа: 268,435,456 пікселів (наприклад, 16,384 x 16,384)

Firefox:

Максимальна висота / ширина: 32 767 пікселів
Максимальна площа: 472,907,776 пікселів (наприклад, 22,528 x 20,992)

IE:

Максимальна висота / ширина: 8,192 пікселів
Максимальна площа: N / A

IE Mobile:

Максимальна висота / ширина: 4 096 пікселів
Максимальна площа: N / A

Інший:

Наразі я не в змозі перевірити інші браузери. Інші відповіді на цій сторінці див. Для додаткових обмежень.


Перевищення максимальної довжини / ширини / площі в більшості браузерів робить полотно непридатним. (Він буде ігнорувати будь-які команди малювання, навіть у користувальницькій області.) IE та IE Mobile будуть шанувати всі команди намалювання у просторі, що використовується.


3
Отже, пов'язане запитання ... як можна обійти межу, якщо їм потрібне полотно, яке перевищує дозволений максимум?
aroth

2
@aroth, я закінчив писати обгортку навколо полотна API, який використовував декілька складених <canvas>елементів і автоматично застосовував інструкції малювання до відповідного контексту.
Брендон Гано

1
Звучить дуже корисно. Не припускайте, що це на Github?
aroth

5
Safari (не для iOS) використовує 32K, як Chrome.Firefox. Крім того, якщо ви хочете спробувати відтворити частину цього коду обгортки, я запустив власну версію з відкритим кодом завдяки своєму обмеженому обмеженню 8K IE: github.com/adam-roth/wrapped-canvas
aroth

7
iOS 9.3.1 Safari на iPhone 6 Plus обмежений площею 16777216 пікселів (наприклад, 4096 x 4096). Я підозрюю, що це загальне число на нових пристроях iOS
matb33

16

У Firefox у мене виникли помилки пам'яті з висотою полотна більше 8000, хром, здається, справляється набагато вище, принаймні до 32000.

EDIT: Після запуску ще декількох тестів я виявив дуже дивні помилки з Firefox 16.0.2.

По-перше, я, здається, отримую іншу поведінку від полотна в пам'яті (створеної в JavaScript) на відміну від оголошеного html-полотна.

По-друге, якщо у вас немає належного html-тега та мета-діаграми, полотно може бути обмежено до 8196, інакше ви можете перейти до 32767.

По-третє, якщо ви отримаєте 2d контекст полотна, а потім зміните розмір полотна, ви також можете бути обмежені до 8196. Просто встановлення розміру полотна перед захопленням 2d-контексту дозволяє вам мати до 32767, не отримуючи помилок пам'яті.

Я не міг послідовно отримувати помилки в пам'яті, іноді це відбувається лише при завантаженні першої сторінки, а потім працюють подальші зміни висоти. Це файл html, який я тестував за допомогою http://pastebin.com/zK8nZxdE .


4
"По-друге, якщо у вас немає належного html-тегу та мета-діаграми, полотно може бути обмежено до 8196, інакше ви можете перейти до 32767" - що ви маєте на увазі під правильним html-тегом та мета-діаграмою тут?
Джек Едлі

Звичайно, ви маєте на увазі 8192 (2 ^ 13)? 8196 - дивне число.
jamesdlin

14

Максимальний розмір полотна iOS (ширина х висота):

 iPod Touch 16GB = 1448x1448
 iPad Mini       = 2290x2289
 iPhone 3        = 1448x1448
 iPhone 5        = 2290x2289

перевірений березня 2014 року.


Ці значення довільні. Перегляньте нижче мою публікацію, де згадується посібник із вмісту сафарі
dcbarans

11

Щоб трохи розширити відповідь на @FredericCharette: Відповідно до посібника із вмісту сафарі в розділі " Знайти обмеження ресурсів iOS":

Максимальний розмір елемента "полотно" - 3 мегапікселі для пристроїв, що мають менше 256 Мб оперативної пам’яті, і 5 мегапікселів для пристроїв з більшою або рівною 256 Мб оперативної пам’яті

Отже, будь-яка різниця розмірів 5242880 (5 x 1024 x 1024) пікселів буде працювати на великих пристроях пам'яті, інакше це 3145728 пікселів.

Приклад для 5-мегапіксельного полотна (ширина х висота):

Any total <= 5242880
--------------------
5 x 1048576 ~= 5MP   (1048576 = 1024 x 1024)
50 x 104857 ~= 5MP
500 x 10485 ~= 5MP

і так далі..

Найбільші SQUARE полотна ("MiB" = 1024x1024 байт):

device < 256 MiB   device >= 256 MiB   iPhone 6 [not confirmed]
-----------------  -----------------   ---------------------
<= 3145728 pixels  <= 5242880 pixels   <= 16 x 1024 x 1024 p
1773 x 1773        2289 x 2289         4096 x 4096

1
Щоб бути більш конкретним, обмеження iPhone5 становить 3 * 1024 * 1024 = 3145728 пікселів. (Після того, як цікаво , чому мій полотно був добре на Android , але незаповнена прошивку, я знайшов цю відповідь, а також stackoverflow.com/questions/12556198 / ... і отримав моє PhoneGap додатки працює.)
Magnus Smith

FYI, Apple видалила інформацію про конкретний розмір із "Обмеження ресурсів iOS Safari / Know". Тепер це лише загальна порада щодо мінімізації пропускної здатності за допомогою невеликих зображень.
ToolmakerSteve

@ matb33 повідомив у коментарі до питання про те, що iPhone 6 має обмеження 16 * 1024 * 1024.
ToolmakerSteve

11

Оновлення на 2018 рік:

З плином часу обмеження полотна змінювалися. На жаль, те, що не змінилося, це те, що браузер все ще не надає інформацію про обмеження розміру полотна через Canvas API.

Для тих, хто хоче програмно визначити максимальний розмір полотна веб-переглядача або перевірити підтримку спеціальних розмірів полотна, ознайомтеся з розміром полотна .

З документів:

Елемент HTML-полотна широко підтримується сучасними і застарілими браузерами, однак кожна комбінація веб-переглядачів і платформ накладає унікальні обмеження розміру, що зробить полотно непридатним при його перевищенні. На жаль, веб-переглядачі не дають змоги визначити, які їх обмеження, а також не надають зворотного зв'язку після створення непридатного полотна. Це робить роботу з великими елементами полотна викликом, особливо це стосується програм, які підтримують різні браузери та платформи.

Ця мікробібліотека забезпечує максимальну площу, висоту та ширину елемента HTML-полотна, що підтримується браузером, а також можливість перевірити власні розміри полотна. Збираючи цю інформацію перед створенням нового елемента полотна, програми можуть надійно встановлювати розміри полотна в межах обмежень розміру кожного браузера / платформи.

Демонстраційне посилання та результати тестів доступні в розділі README , а також відомий розділ проблем, який стосується питань продуктивності та віртуальної машини.

Повне розкриття, я автор бібліотеки. Я створив його ще в 2014 році і нещодавно переглянув код нового проекту, пов'язаного з полотном. Я здивований, виявивши той самий брак доступних інструментів для виявлення обмежень розміру полотна в 2018 році, тому я оновив код, випустив його та сподіваюся, що він допомагає іншим стикатися з подібними проблемами.


8

Відповідно до специфікацій w3 , інтерфейс ширини / висоти - це без підпису - так від 0 до 4 294 967 295 (якщо я пам'ятаю, що це число правильно - може бути відключено кілька).

EDIT: Як не дивно, він говорить про неподписане довге, але тестування показує просто нормальне довге значення як максимальне: 2147483647. Jsfiddle - 47 працює, але до 48, і він повертається до дефолту.


Хм. Цікаво, але я навіть не досягаю 1,5 мільярда.
серйозний

Відредаговано, вибачте (ось що я отримую не тестуючи спочатку) - додав jsfiddle для показу.
WSkid

7

Навіть незважаючи на те, що полотно дозволить вам нанести висоту = 2147483647, коли ви почнете малювати, нічого не станеться

Малювання відбувається лише тоді, коли я поверну висоту до 32767


7

iOS має різні обмеження.

Використовуючи тренажер iOS 7, я зміг продемонструвати обмеження в 5 Мб таким чином:

var canvas = document.createElement('canvas');
canvas.width = 1024 * 5;
canvas.height = 1024;
alert(canvas.toDataURL('image/jpeg').length);
// prints "110087" - the expected length of the dataURL

але якщо я зміщу розмір полотна на один рядок пікселів:

var canvas = document.createElement('canvas');
canvas.width = 1024 * 5;
canvas.height = 1025;
alert(canvas.toDataURL('image/jpeg'));
// prints "data:," - a broken dataURL

2
Не слід базувати такі дані на тренажері iOS.
Золтан

5

На ПК -
я не думаю, що існує обмеження, але так, ви можете позбутися виключення з пам'яті.

На мобільних пристроях.
Ось обмеження щодо полотна для мобільних пристроїв: -

Максимальний розмір елемента "полотно" - 3 мегапікселі для пристроїв, що мають менше 256 МБ оперативної пам’яті, і 5 мегапікселів для пристроїв з більшою або рівною оперативною пам’яттю 256 Мб.

Так, наприклад - якщо ви хочете підтримувати старіші апаратні засоби Apple, розмір ваших полотен не може перевищувати 2048 × 1464.

Сподіваюся, ці ресурси допоможуть вам витягнути вас.


3

Обмеження для Safari (усіх платформ) значно нижчі.

Відомі обмеження для iOS / Safari

Наприклад, у мене був буфер полотна 6400x6400px з даними, намальованими на ньому. Відстежуючи / експортуючи вміст і тестуючи інші браузери, я зміг побачити, що все в порядку. Але на Safari, це пропустило б малюнок цього специфічного буфера до мого основного контексту.


Так! Safari пропускає малювати ваше полотно, оскільки ви використовуєте великі полотна як "дозволені". Дивіться максимальні розміри, які я опублікував вище! Це макс. полотно на цьому сафарі / пристроях.
Дадо

Ваші розміри досить довільні, чи не так? У вас є документація? Яким був ваш процес тестування?
NodeNodeNode

3

Я спробував програмно визначити межу: встановити розмір полотна починаючи з 35000, зменшуючи 100, поки не знайдеться дійсний розмір. На кожному кроці записуйте піксель правого нижнього кута, а потім читайте його. Це працює - з обережністю.

Швидкість прийнятна , якщо або ширина або висота встановлена в деякій малої величини (наприклад , 10-200.) Наступним чином: get_max_canvas_size('height', 20).

Але якщо його називати без ширини чи висоти, наприклад get_max_canvas_size(), створене полотно настільки велике, що читання кольорів пікселів SINGLE відбувається дуже повільно, а в IE виникає серйозна повія.

Якщо подібний тест можна було б зробити якось без зчитування значення пікселя, швидкість була б прийнятною.

Звичайно, найпростішим способом виявити максимальний розмір був би рідний спосіб запиту максимальної ширини та висоти. Але Полотно - це "життєвий рівень", тож, можливо, воно настане в якийсь день.

http://jsfiddle.net/timo2012/tcg6363r/2/ (Будьте в курсі! Ваш браузер може висіти!)

if (!Date.now)
{
  Date.now = function now()
  {
    return new Date().getTime();
  };
}

var t0 = Date.now();
//var size = get_max_canvas_size('width', 200);
var size = get_max_canvas_size('height', 20);
//var size = get_max_canvas_size();
var t1 = Date.now();
var c = size.canvas;
delete size.canvas;
$('body').append('time: ' + (t1 - t0) + '<br>max size:' + JSON.stringify(size) + '<br>');
//$('body').append(c);

function get_max_canvas_size(h_or_w, _size)
{
  var c = document.createElement('canvas');
  if (h_or_w == 'height') h = _size;
  else if (h_or_w == 'width') w = _size;
  else if (h_or_w && h_or_w !== 'width' && h_or_w !== 'height' || !window.CanvasRenderingContext2D)
    return {
      width: null,
      height: null
    };
  var w, h;
  var size = 35000;
  var cnt = 0;
  if (h_or_w == 'height') w = size;
  else if (h_or_w == 'width') h = size;
  else
  {
    w = size;
    h = size;
  }

  if (!valid(w, h))
    for (; size > 10; size -= 100)
    {
      cnt++;
      if (h_or_w == 'height') w = size;
      else if (h_or_w == 'width') h = size;
      else
      {
        w = size;
        h = size;
      }
      if (valid(w, h)) break;
    }
  return {
    width: w,
    height: h,
    iterations: cnt,
    canvas: c
  };

  function valid(w, h)
  {
    var t0 = Date.now();
    var color, p, ctx;
    c.width = w;
    c.height = h;
    if (c && c.getContext)
      ctx = c.getContext("2d");
    if (ctx)
    {
      ctx.fillStyle = "#ff0000";
      try
      {
        ctx.fillRect(w - 1, h - 1, 1, 1);
        p = ctx.getImageData(w - 1, h - 1, 1, 1).data;
      }
      catch (err)
      {
        console.log('err');
      }

      if (p)
        color = p[0] + '' + p[1] + '' + p[2];
    }
    var t1 = Date.now();

    if (color == '25500')
    {
      console.log(w, h, true, t1 - t0);
      return true;
    }
    console.log(w, h, false, t1 - t0);
    return false;
  }
}

2
Це було б значно оптимізовано за допомогою en.wikipedia.org/wiki/Exponential_search
Brendan Annable

1
Це справді божевільно, що провал створення такого великого полотна мовчить, тому немає ніякого способу виявити ... непогано будь-якими способами з вашого боку, це просто божевільна річ для браузерів, що з’являються на нас
Colin D

@ColinD Я згоден. Це був би десь лише невеликий фрагмент інформації. Це було б. І слід.
Тімо Кахьонен

3

Коли ви використовуєте полотна WebGL, браузери (включаючи настільні) накладають додаткові обмеження на розмір основного буфера. Навіть якщо ваше полотно велике, напр., 16000х16000, більшість браузерів надасть меншу картину (скажімо, 4096x4096) та збільшить її. Це може спричинити некрасиві пікселяції тощо.

Я написав якийсь код, щоб визначити максимальний розмір, використовуючи експоненціальний пошук, якщо комусь це колись потрібно. determineMaxCanvasSize()- це функція, яка вас цікавить.

function makeGLCanvas()
{
    // Get A WebGL context
    var canvas = document.createElement('canvas');
    var contextNames = ["webgl", "experimental-webgl"];
    var gl = null;
    for (var i = 0; i < contextNames.length; ++i)
    {
        try
        {
            gl = canvas.getContext(contextNames[i], {
                // Used so that the buffer contains valid information, and bytes can
                // be retrieved from it. Otherwise, WebGL will switch to the back buffer
                preserveDrawingBuffer: true
            });
        }
        catch(e) {}
        if (gl != null)
        {
            break;
        }
    }
    if (gl == null)
    {
        alert("WebGL not supported.\nGlobus won't work\nTry using browsers such as Mozilla " +
            "Firefox, Google Chrome or Opera");
        // TODO: Expecting that the canvas will be collected. If that is not the case, it will
        // need to be destroyed somehow.
        return;
    }

    return [canvas, gl];
}

// From Wikipedia
function gcd(a,b) {
    a = Math.abs(a);
    b = Math.abs(b);
    if (b > a) {var temp = a; a = b; b = temp;}
    while (true) {
        if (b == 0) return a;
        a %= b;
        if (a == 0) return b;
        b %= a;
    }
}

function isGlContextFillingTheCanvas(gl) {
    return gl.canvas.width == gl.drawingBufferWidth && gl.canvas.height == gl.drawingBufferHeight;
}

// (See issue #2) All browsers reduce the size of the WebGL draw buffer for large canvases 
// (usually over 4096px in width or height). This function uses a varian of binary search to
// find the maximum size for a canvas given the provided x to y size ratio.
//
// To produce exact results, this function expects an integer ratio. The ratio will be equal to:
// xRatio/yRatio.
function determineMaxCanvasSize(xRatio, yRatio) {
    // This function works experimentally, by creating an actual canvas and finding the maximum
    // value, the browser allows.
    [canvas, gl] = makeGLCanvas();

    // Reduce the ratio to minimum
    gcdOfRatios = gcd(xRatio, yRatio);
    [xRatio, yRatio] = [xRatio/gcdOfRatios, yRatio/gcdOfRatios];

    // if the browser cannot handle the minimum ratio, there is not much we can do
    canvas.width = xRatio;
    canvas.height = yRatio;

    if (!isGlContextFillingTheCanvas(gl)) {
        throw "The browser is unable to use WebGL canvases with the specified ratio: " + 
            xRatio + ":" + yRatio;
    }

    // First find an upper bound
    var ratioMultiple = 1;  // to maintain the exact ratio, we will keep the multiplyer that
                            // resulted in the upper bound for the canvas size
    while (isGlContextFillingTheCanvas(gl)) {
        canvas.width *= 2;
        canvas.height *= 2;
        ratioMultiple *= 2;
    }

    // Search with minVal inclusive, maxVal exclusive
    function binarySearch(minVal, maxVal) {
        if (minVal == maxVal) {
            return minVal;
        }

        middle = Math.floor((maxVal - minVal)/2) + minVal;

        canvas.width = middle * xRatio;
        canvas.height = middle * yRatio;

        if (isGlContextFillingTheCanvas(gl)) {
            return binarySearch(middle + 1, maxVal);
        } else {
            return binarySearch(minVal, middle);
        }
    }

    ratioMultiple = binarySearch(1, ratioMultiple);
    return [xRatio * ratioMultiple, yRatio * ratioMultiple];
}

Також у jsfiddle https://jsfiddle.net/1sh47wfk/1/


Чи є офіційна документація щодо поведінки щодо масштабування?
Магнус Лінд Окслунд

1
@MagnusLindOxlund Інформація з відповіді була визначена експериментально. Найближче я знайшов після швидкого пошуку полягає в наступному: khronos.org/registry/webgl/specs/latest/1.0/#2.2 , кажучи: «Обмеження вище не змінює обсяг простору полотна елемент споживає на веб - сторінці "коли йдеться про обмеження розміру буфера малювання. Здається, що стандарт WebGL не вникає в шалені деталі щодо того, як елемент полотна повинен відображати буфер малювання.
Michał

1

Ви можете зіграти його і в автоматичному режимі JavaScript додати стільки менших полотен, скільки потрібно, і намалювати елементи на відповідному полотні. Зрештою, у вас може залишитися пам'ять, але ви отримаєте обмеження на єдиному полотні.


0

Я не знаю, як визначити максимально можливий розмір без ітерації, але ви можете виявити, чи заданий розмір полотна працює, заповнивши піксель і потім прочитавши колір назад. Якщо полотно не відображається, то колір, який ви отримаєте, не збігатиметься. W

частковий код:

function rgbToHex(r, g, b) {
    if (r > 255 || g > 255 || b > 255)
        throw "Invalid color component";
    return ((r << 16) | (g << 8) | b).toString(16);
}
var test_colour = '8ed6ff';
working_context.fillStyle = '#' + test_colour;
working_context.fillRect(0,0,1,1);
var colour_data = working_context.getImageData(0, 0, 1, 1).data;
var colour_hex = ("000000" + rgbToHex(colour_data[0], colour_data[1], colour_data[2])).slice(-6);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.