Tamaño máximo de un elemento <canvas>

112

Estoy trabajando con un elemento de lienzo con una altura de 600a 1000píxeles y un ancho de varias decenas o cientos de miles de píxeles. Sin embargo, después de una cierta cantidad de píxeles (obviamente desconocido), el lienzo ya no muestra las formas que dibujo con JS.

¿Alguien sabe si hay un límite?

Probado tanto en Chrome 12 como en Firefox 4.

serio
fuente
2
@ Šime Dijo tens OR hundreds of thousands...
Tadeck
6
Yo también experimento esto. Tengo un lienzo de 8000x8000 que funciona bien, pero cuando lo agrando, el contenido desaparece y no se dibuja. El tamaño en el que no funciona es mucho menor en mi iPad. Me pregunto si es una limitación de la memoria de algún tipo.
Joshua
28
Es extraño cuando encuentras tu propio comentario de 2 años antes y todavía estás lidiando con el mismo maldito problema.
Joshua
4
@Joshua ¡y aún un año después!
Eugene Yu
1
La pregunta es ¿cómo puedo detectar estos errores, o mejores advertencias, que es lo que son? No puedo detectar el hardware del dispositivo, así que tengo que reaccionar ante un error.
br4nnigan

Respuestas:

115

Actualizado 13/10/2014

Todos los navegadores probados tienen límites en la altura / ancho de los elementos del lienzo, pero muchos navegadores también limitan el área total del elemento del lienzo. Los límites son los siguientes para los navegadores que puedo probar:

Cromo:


Alto / ancho máximo: 32,767 píxeles Área máxima: 268,435,456 píxeles (por ejemplo, 16,384 x 16,384)

Firefox:


Alto / ancho máximo: 32,767 píxeles Área máxima: 472,907,776 píxeles (por ejemplo, 22,528 x 20,992)

ES DECIR:

Alto / ancho máximo: 8.192 píxeles
Área máxima: N / A

IE móvil:

Alto / ancho máximo: 4096 píxeles
Área máxima: N / A

Otro:

No puedo probar otros navegadores en este momento. Consulte las otras respuestas en esta página para conocer los límites adicionales.


Exceder el largo / ancho / área máxima en la mayoría de los navegadores hace que el lienzo sea inutilizable. (Ignorará cualquier comando de dibujo, incluso en el área utilizable). IE e IE Mobile respetarán todos los comandos de dibujo dentro del espacio utilizable.

Brandon Gano
fuente
3
Entonces, pregunta relacionada ... ¿cómo se trabaja alrededor del límite si necesitan un lienzo que sea más grande que el máximo permitido?
aroth
2
@aroth, terminé escribiendo un envoltorio alrededor de la API de lienzo que usaba múltiples <canvas>elementos apilados y aplicaba automáticamente instrucciones de dibujo al contexto apropiado.
Brandon Gano
1
Suena muy útil. ¿No crees que está en github?
aroth
5
Safari (versión que no es de iOS) usa 32K como Chrome.Firefox. Además, si desea intentar recrear parte de ese código contenedor, comencé una versión de código abierto propio gracias al miserable límite de 8K de IE: github.com/adam-roth/wrapped-canvas
aroth
7
iOS 9.3.1 Safari en un iPhone 6 Plus está limitado a un área de 16777216 píxeles (por ejemplo, 4096 x 4096). Sospecho que este es un número común en los dispositivos iOS más nuevos
matb33
16

Me encontré con errores de falta de memoria en Firefox con alturas de lienzo superiores a 8000, Chrome parece manejar mucho más alto, al menos a 32000.

EDITAR: Después de ejecutar algunas pruebas más, encontré algunos errores muy extraños con Firefox 16.0.2.

Primero, parece que obtengo un comportamiento diferente del lienzo en memoria (creado en javascript) en comparación con el lienzo declarado en html.

En segundo lugar, si no tiene la etiqueta html y el metajuego de caracteres adecuados, el lienzo puede estar restringido a 8196; de lo contrario, puede subir hasta 32767.

En tercer lugar, si obtiene el contexto 2d del lienzo y luego cambia el tamaño del lienzo, es posible que también esté restringido a 8196. Simplemente establecer el tamaño del lienzo antes de tomar el contexto 2d le permite tener hasta 32767 sin errores de memoria.

No he podido obtener los errores de memoria de manera constante, a veces es solo en la carga de la primera página, y luego funcionan los cambios de altura posteriores. Este es el archivo html que estaba probando con http://pastebin.com/zK8nZxdE .

Ben Burnett
fuente
4
"En segundo lugar, si no tiene la etiqueta html y el metajuego de caracteres adecuados, el lienzo puede estar restringido a 8196; de lo contrario, puede subir a 32767". ¿Qué quiere decir con la etiqueta html y el metajuego de caracteres adecuados aquí?
Jack Aidley
Seguramente te refieres a 8192 (2 ^ 13)? 8196 es un número extraño.
jamesdlin
14

Tamaño máximo del lienzo de iOS (ancho x alto):

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

probado en marzo de 2014.

Friso
fuente
Estos valores son arbitrarios. Por favor, vea mi publicación a continuación que hace referencia a la guía de contenido del safari
dcbarans
11

Para ampliar un poco la respuesta de @FredericCharette: según la guía de contenido de Safari en la sección "Conozca los límites de recursos de iOS":

El tamaño máximo de un elemento de lienzo es de 3 megapíxeles para dispositivos con menos de 256 MB de RAM y 5 megapíxeles para dispositivos con más o menos de 256 MB de RAM

Por lo tanto, cualquier variación de tamaño de 5242880 (5 x 1024 x 1024) píxeles funcionará en dispositivos de memoria grandes; de lo contrario, son 3145728 píxeles.

Ejemplo de lienzo de 5 megapíxeles (ancho x alto):

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

y así..

Los lienzos CUADRADOS más grandes son ("MiB" = 1024x1024 Bytes):

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
dcbarans
fuente
1
Para ser más específicos, el límite de iPhone5 es 3 * 1024 * 1024 = 3145728 píxeles. (Después de preguntarme por qué mi lienzo estaba bien en Android pero en blanco en iOS, encontré esta respuesta, y también stackoverflow.com/questions/12556198/… y conseguí que mi aplicación Phonegap funcionara).
Magnus Smith
Para su información, Apple ha eliminado la información de tamaño específico de "Safari / Conozca los límites de recursos de iOS". Ahora es simplemente un consejo genérico sobre cómo minimizar el ancho de banda mediante el uso de imágenes pequeñas.
ToolmakerSteve
@ matb33 informó en un comentario sobre la pregunta que el iPhone 6 parece tener un límite de 16 * 1024 * 1024.
ToolmakerSteve
11

Actualización para 2018:

A medida que avanza el tiempo, las limitaciones de los lienzos han cambiado. Lamentablemente, lo que no ha cambiado es el hecho de que los navegadores aún no brindan información sobre las limitaciones de tamaño del lienzo a través de la API de Canvas.

Para aquellos que buscan determinar mediante programación el tamaño máximo del lienzo del navegador o probar la compatibilidad con dimensiones de lienzo personalizadas, consulte el tamaño del lienzo .

De los documentos:

El elemento de lienzo HTML es ampliamente compatible con los navegadores modernos y heredados, pero cada combinación de navegador y plataforma impone limitaciones de tamaño únicas que inutilizarán un lienzo cuando se exceda. Desafortunadamente, los navegadores no proporcionan una forma de determinar cuáles son sus limitaciones, ni brindan ningún tipo de retroalimentación después de que se ha creado un lienzo inutilizable. Esto hace que trabajar con elementos de lienzo grandes sea un desafío, especialmente para aplicaciones que admiten una variedad de navegadores y plataformas.

Esta microbiblioteca proporciona el área, la altura y el ancho máximos de un elemento de lienzo HTML admitido por el navegador, así como la capacidad de probar dimensiones de lienzo personalizadas. Al recopilar esta información antes de que se cree un nuevo elemento de lienzo, las aplicaciones pueden establecer de manera confiable las dimensiones del lienzo dentro de las limitaciones de tamaño de cada navegador / plataforma.

Un enlace de demostración y los resultados de las pruebas están disponibles en el archivo README , así como una sección de problemas conocidos que trata sobre el rendimiento y las consideraciones de la máquina virtual.

Divulgación completa, soy el autor de la biblioteca. Lo creé en 2014 y recientemente revisé el código para un nuevo proyecto relacionado con el lienzo. Me sorprendió encontrar la misma falta de herramientas disponibles para detectar limitaciones de tamaño de lienzo en 2018, así que actualicé el código, lo publiqué y espero que ayude a otros a encontrar problemas similares.

jhildenbiddle
fuente
8

De acuerdo con las especificaciones de w3 , la interfaz de ancho / alto es un largo sin firmar, por lo que de 0 a 4,294,967,295 (si recuerdo bien ese número, podría estar fuera de algunos).

EDITAR: Extrañamente, dice unsigned long, pero la prueba muestra solo un valor largo normal como máximo: 2147483647. Jsfiddle - 47 funciona pero hasta 48 y vuelve a los valores predeterminados.

WSkid
fuente
Hmm. Interesante, pero ni siquiera llego a los 1.500 millones.
seriodev
Editado, lo siento (eso es lo que obtengo por no probar primero): se agregó un jsfiddle para mostrar.
WSkid
7

Aunque el lienzo te permitirá poner altura = 2147483647, cuando comiences a dibujar, no pasará nada

El dibujo ocurre solo cuando devuelvo la altura a 32767

avikaza123
fuente
7

iOS tiene diferentes límites.

Usando el simulador de iOS 7 pude demostrar que el límite es de 5 MB de esta manera:

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

pero si levanto el tamaño del lienzo en una sola fila de píxeles:

var canvas = document.createElement('canvas');
canvas.width = 1024 * 5;
canvas.height = 1025;
alert(canvas.toDataURL('image/jpeg'));
// prints "data:," - a broken dataURL
quemaduras mate
fuente
2
No debe basar estos datos en el simulador de iOS.
Zoltán
5

En PC
: no creo que haya una restricción, pero sí, puede salir de la excepción de memoria.

En dispositivos móviles:
aquí están las restricciones para el lienzo para dispositivos móviles: -

El tamaño máximo de un elemento de lienzo es de 3 megapíxeles para dispositivos con menos de 256 MB de RAM y 5 megapíxeles para dispositivos con más o igual de 256 MB de RAM.

Entonces, por ejemplo, si desea admitir el hardware más antiguo de Apple, el tamaño de su lienzo no puede exceder los 2048 × 1464.

Espero que estos recursos le ayuden a salir adelante.

Manish Gupta
fuente
3

Las limitaciones de Safari (todas las plataformas) son mucho menores.

Limitaciones conocidas de iOS / Safari

Por ejemplo, tenía un búfer de lienzo de 6400x6400px con datos dibujados en él. Al rastrear / exportar el contenido y al probar en otros navegadores, pude ver que todo estaba bien. Pero en Safari, omitiría el dibujo de este búfer específico en mi contexto principal.

NodeNodeNode
fuente
¡Sí! Safari omite dibujar su lienzo porque usa un lienzo más grande como "permitido". ¡Vea las dimensiones máximas que he publicado arriba! Eso es max. lienzo en este safari / dispositivos.
Dado
Tus tamaños son bastante arbitrarios, ¿no? ¿Tienes alguna documentación? ¿Cuál fue su proceso de prueba?
NodeNodeNode
3

Traté de averiguar el límite mediante programación: estableciendo el tamaño del lienzo a partir de 35000, reduciendo en 100 hasta que se encuentre un tamaño válido. En cada paso, escriba el píxel inferior derecho y luego lo lea. Funciona con precaución.

La velocidad es aceptable si cualquiera de anchura o altura se establece en un valor bajo (por ejemplo 10-200.) De esta manera: get_max_canvas_size('height', 20).

Pero si se llama sin ancho o alto get_max_canvas_size(), el lienzo creado es tan grande que leer el color de UN SOLO píxel es muy lento y, en IE, se bloquea gravemente.

Si esta prueba similar se pudiera realizar de alguna manera sin leer el valor de los píxeles, la velocidad sería aceptable.

Por supuesto, la forma más fácil de detectar el tamaño máximo sería alguna forma nativa de consultar el ancho y la altura máximos. Pero Canvas es 'un estándar de vida', por lo que puede que llegue algún día.

http://jsfiddle.net/timo2012/tcg6363r/2/ (¡Tenga en cuenta que su navegador puede bloquearse!)

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;
  }
}
Timo Kähkönen
fuente
2
Esto se optimizaría en gran medida utilizando en.wikipedia.org/wiki/Exponential_search
Brendan Annable
1
Es realmente una locura que el error de crear un lienzo tan grande sea silencioso, por lo que no hay forma de detectarlo ... no está mal de ninguna manera de su parte, es una locura que los navegadores se lancen sobre nosotros
Colin D
@ColinD Estoy de acuerdo. Sería solo una pequeña información en alguna parte. Sería. Y debería.
Timo Kähkönen
3

Cuando utiliza lienzos WebGL, los navegadores (incluidos los de escritorio) impondrán límites adicionales en el tamaño del búfer subyacente. Incluso si su lienzo es grande, por ejemplo, 16 000 x 16 000, la mayoría de los navegadores generarán una imagen más pequeña (digamos 4096 x 4096) y la ampliarán. Eso podría causar un pixelado feo, etc.

He escrito un código para determinar ese tamaño máximo usando una búsqueda exponencial, si alguien lo necesita. determineMaxCanvasSize()es la función que le interesa.

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];
}

También en un jsfiddle https://jsfiddle.net/1sh47wfk/1/

Michał
fuente
¿Existe documentación oficial sobre el comportamiento de la ampliación?
Magnus Lind Oxlund
1
@MagnusLindOxlund La información de la respuesta se ha determinado experimentalmente. Lo más parecido que encontré después de una búsqueda rápida es esto: khronos.org/registry/webgl/specs/latest/1.0/#2.2 , diciendo: "La restricción anterior no cambia la cantidad de espacio que consume el elemento de lienzo en la página web "cuando habla de limitar el tamaño del búfer de dibujo. Parece que el estándar WebGL no entra en detalles locos sobre cómo se supone que el elemento canvas muestra el búfer de dibujo.
Michał
1

Puede dividirlo y en javascript agregar automáticamente tantos lienzos más pequeños como sea necesario y dibujar los elementos en el lienzo apropiado. Es posible que aún se quede sin memoria eventualmente, pero llegará al límite de un solo lienzo.

JJ_Coder4Hire
fuente
0

No sé cómo detectar el tamaño máximo posible sin alteración, pero puede detectar si un tamaño de lienzo determinado funciona rellenando un píxel y luego leyendo el color de nuevo. Si el lienzo no se ha renderizado, el color que obtenga no coincidirá. W

código parcial:

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);
Sistémico Plural
fuente