Taille maximale d'un élément <canvas>

112

Je travaille avec un élément canvas d'une hauteur de 600à 1000pixels et d'une largeur de plusieurs dizaines ou centaines de milliers de pixels. Cependant, après un certain nombre de pixels (évidemment inconnu), le canevas n'affiche plus les formes que je dessine avec JS.

Est-ce que quelqu'un sait s'il y a une limite?

Testé à la fois dans Chrome 12 et Firefox 4.

seriousdev
la source
2
@ Šime He said tens OR hundreds of thousands...
Tadeck
6
J'en fais l'expérience aussi. J'ai un canevas 8000x8000 qui fonctionne bien, mais quand je l'agrandit, le contenu disparaît et il ne dessine tout simplement pas. La taille à laquelle il ne fonctionne pas est beaucoup plus faible sur mon iPad. Je me demande si c'est une limitation de mémoire de quelque sorte.
Joshua
28
C'est bizarre quand vous trouvez votre propre commentaire de 2 ans plus tôt et que vous êtes toujours aux prises avec le même problème.
Joshua
4
@Joshua et encore un an plus tard!
Eugene Yu
1
La question est de savoir comment puis-je détecter ces erreurs, ou de meilleurs avertissements, ce qu'ils sont? Je ne peux pas détecter le matériel des appareils, je dois donc réagir à une erreur.
br4nnigan

Réponses:

115

Mis à jour le 13/10/2014

Tous les navigateurs testés ont des limites à la hauteur / largeur des éléments de canevas, mais de nombreux navigateurs limitent également la surface totale de l'élément de canevas. Les limites sont les suivantes pour les navigateurs que je peux tester:

Chrome:

Hauteur / largeur maximale: 32 767 pixels
Zone maximale: 268 435 456 pixels (par exemple, 16 384 x 16 384)

Firefox:

Hauteur / largeur maximale: 32 767 pixels
Zone maximale: 472 907 776 pixels (par exemple, 22 528 x 20 992)

C'EST À DIRE:

Hauteur / largeur maximale: 8192 pixels
Zone maximale: N / A

Mobile IE:

Hauteur / largeur maximale: 4096 pixels
Zone maximale: N / A

Autre:

Je ne peux pas tester d'autres navigateurs pour le moment. Reportez-vous aux autres réponses sur cette page pour connaître les limites supplémentaires.


Le dépassement de la longueur / largeur / zone maximale sur la plupart des navigateurs rend le canevas inutilisable. (Il ignorera toutes les commandes de dessin, même dans la zone utilisable.) IE et IE Mobile honoreront toutes les commandes de dessin dans l'espace utilisable.

Brandon Gano
la source
3
Alors, question connexe ... comment contourner la limite s'ils ont besoin d'une toile plus grande que le maximum autorisé?
aroth
2
@aroth, j'ai fini par écrire un wrapper autour de l'API de canevas qui utilisait plusieurs <canvas>éléments empilés et appliquait automatiquement les instructions de dessin au contexte approprié.
Brandon Gano
1
Cela semble très utile. Vous ne pensez pas que c'est sur github?
aroth
5
Safari (version non-iOS) utilise 32K comme Chrome.Firefox. De plus, si vous souhaitez essayer de recréer une partie de ce code wrapper, j'ai commencé une version open source de mes propres grâce à dérisoires 8K limite IE: github.com/adam-roth/wrapped-canvas
aroth
7
iOS 9.3.1 Safari sur un iPhone 6 Plus est limité à une zone de 16777216 pixels (par exemple, 4096 x 4096). Je soupçonne qu'il s'agit d'un numéro commun sur les nouveaux appareils iOS
matb33
16

J'ai rencontré des erreurs de mémoire sur Firefox avec des hauteurs de canevas supérieures à 8000, le chrome semble gérer beaucoup plus haut, au moins jusqu'à 32000.

EDIT: Après avoir exécuté quelques tests supplémentaires, j'ai trouvé des erreurs très étranges avec Firefox 16.0.2.

Premièrement, je semble avoir un comportement différent du canevas en mémoire (créé en javascript) par opposition au canevas déclaré html.

Deuxièmement, si vous n'avez pas la balise html et le jeu de caractères méta appropriés, le canevas peut être limité à 8196, sinon vous pouvez aller jusqu'à 32767.

Troisièmement, si vous obtenez le contexte 2D du canevas et que vous modifiez ensuite la taille du canevas, vous pourriez également être limité à 8196. Le simple fait de définir la taille du canevas avant de saisir le contexte 2d vous permet d'avoir jusqu'à 32767 sans avoir d'erreurs de mémoire.

Je n'ai pas pu obtenir systématiquement les erreurs de mémoire, parfois ce n'est que lors du chargement de la première page, puis les changements de hauteur ultérieurs fonctionnent. C'est le fichier html que je testais avec http://pastebin.com/zK8nZxdE .

Ben Burnett
la source
4
"Deuxièmement, si vous n'avez pas la balise html et le jeu de caractères méta appropriés, le canevas peut être limité à 8196, sinon vous pouvez aller jusqu'à 32767" - qu'entendez-vous par balise html et jeu de caractères méta appropriés ici?
Jack Aidley
Vous voulez sûrement dire 8192 (2 ^ 13)? 8196 est un nombre étrange.
jamesdlin
14

Taille maximale du canevas iOS (largeur x hauteur):

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

testé en mars 2014.

Dado
la source
Ces valeurs sont arbitraires. S'il vous plaît voir mon article ci-dessous qui fait référence au guide de contenu du safari
dcbarans
11

Pour développer un peu la réponse @FredericCharette: Selon le guide de contenu de safari dans la section "Connaître les limites des ressources iOS":

La taille maximale d'un élément de canevas est de 3 mégapixels pour les appareils avec moins de 256 Mo de RAM et de 5 mégapixels pour les appareils avec une RAM supérieure ou égale à 256 Mo

Par conséquent, toute variation de taille de 5242880 (5 x 1024 x 1024) pixels fonctionnera sur les grands périphériques de mémoire, sinon il s'agit de 3145728 pixels.

Exemple pour un canevas 5 mégapixels (largeur x hauteur):

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

etc..

Les plus grandes toiles SQUARE sont ("Mio" = 1024x1024 octets):

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
la source
1
Pour être plus précis, la limite de l'iPhone5 est de 3 * 1024 * 1024 = 3145728 pixels. (Après m'être demandé pourquoi ma toile fonctionnait bien sur Android mais vide sur iOS, j'ai trouvé cette réponse, ainsi que stackoverflow.com/questions/12556198/… et j'ai fait fonctionner mon application Phonegap.)
Magnus Smith
Pour info, Apple a supprimé les informations de taille spécifiques de "Safari / Know iOS Resource Limits". Il ne s'agit maintenant que de conseils génériques sur la minimisation de la bande passante en utilisant de petites images.
ToolmakerSteve
@ matb33 a signalé dans un commentaire sur la question que l'iPhone 6 semble avoir une limite de 16 * 1024 * 1024.
ToolmakerSteve
11

Mise à jour pour 2018:

Au fil du temps, les limitations de la toile ont changé. Malheureusement, ce qui n'a pas changé, c'est le fait que le navigateur ne fournit toujours pas d'informations sur les limitations de taille du canevas via l'API Canvas.

Pour ceux qui cherchent à déterminer par programme la taille maximale du canevas du navigateur ou à tester la prise en charge des dimensions de canevas personnalisées, consultez canvas-size .

À partir de la documentation:

L'élément de canevas HTML est largement pris en charge par les navigateurs modernes et hérités, mais chaque combinaison de navigateur et de plate-forme impose des limitations de taille uniques qui rendront un canevas inutilisable lorsqu'il est dépassé. Malheureusement, les navigateurs ne fournissent aucun moyen de déterminer quelles sont leurs limites, ni ne fournissent aucun type de retour après la création d'un canevas inutilisable. Cela rend le travail avec de grands éléments de canevas un défi, en particulier pour les applications qui prennent en charge une variété de navigateurs et de plates-formes.

Cette micro-bibliothèque fournit la zone, la hauteur et la largeur maximales d'un élément de canevas HTML pris en charge par le navigateur, ainsi que la possibilité de tester des dimensions de canevas personnalisées. En collectant ces informations avant la création d'un nouvel élément de canevas, les applications peuvent définir de manière fiable les dimensions du canevas dans les limites de taille de chaque navigateur / plate-forme.

Un lien de démonstration et les résultats des tests sont disponibles dans le README , ainsi qu'une section sur les problèmes connus qui aborde les considérations relatives aux performances et aux machines virtuelles.

Divulgation complète, je suis l'auteur de la bibliothèque. Je l'ai créé en 2014 et j'ai récemment revisité le code d'un nouveau projet lié au canevas. J'ai été surpris de constater le même manque d'outils disponibles pour détecter les limitations de taille de la toile en 2018, j'ai donc mis à jour le code, l'ai publié et j'espère que cela aidera d'autres personnes à rencontrer des problèmes similaires.

jhildenbiddle
la source
8

Selon les spécifications de w3 , l'interface largeur / hauteur est une longueur non signée - donc de 0 à 4 294 967 295 (si je me souviens bien de ce nombre - peut-être quelques-uns).

EDIT: Étrangement, il dit long non signé, mais il teste juste une valeur longue normale comme max: 2147483647. Jsfiddle - 47 fonctionne mais jusqu'à 48 et il revient à la valeur par défaut.

WSkid
la source
Hmm. Intéressant, mais je n'arrive même pas à 1,5 milliard.
seriousdev
Édité, désolé (c'est ce que j'obtiens pour ne pas tester en premier) - a ajouté un jsfiddle à montrer.
WSkid
7

Même si le canevas vous permettra de mettre height = 2147483647, lorsque vous commencez à dessiner, rien ne se passe

Le dessin ne se produit que lorsque je ramène la hauteur à 32767

avikaza123
la source
7

iOS a des limites différentes.

En utilisant le simulateur iOS 7, j'ai pu démontrer que la limite est de 5 Mo comme ceci:

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

mais si j'augmente la taille du canevas d'une seule rangée de pixels:

var canvas = document.createElement('canvas');
canvas.width = 1024 * 5;
canvas.height = 1025;
alert(canvas.toDataURL('image/jpeg'));
// prints "data:," - a broken dataURL
brûlures mates
la source
2
Vous ne devez pas baser ces données sur le simulateur iOS.
Zoltán
5

Sur PC,
je ne pense pas qu'il y ait de restriction mais oui, vous pouvez sortir de l'exception de mémoire.

Sur les appareils mobiles -
Voici les restrictions pour le canevas pour les appareils mobiles: -

La taille maximale d'un élément de canevas est de 3 mégapixels pour les appareils avec moins de 256 Mo de RAM et de 5 mégapixels pour les appareils avec plus ou égal à 256 Mo de RAM.

Par exemple, si vous souhaitez prendre en charge le matériel plus ancien d'Apple, la taille de votre canevas ne peut pas dépasser 2048 × 1464.

J'espère que ces ressources vous aideront à vous retirer.

Manish Gupta
la source
3

Les limitations de Safari (toutes les plates-formes) sont beaucoup plus faibles.

Limitations connues d'iOS / Safari

Par exemple, j'avais un tampon de toile de 6400x6400px avec des données dessinées dessus. En traçant / exportant le contenu et en testant sur d'autres navigateurs, j'ai pu voir que tout allait bien. Mais sur Safari, cela passerait le dessin de ce tampon spécifique dans mon contexte principal.

NodeNodeNode
la source
Ouais! Safari ignore le dessin de votre toile car vous utilisez une plus grande toile comme "autorisé". Voir les dimensions maximales que j'ai publiées ci-dessus! C'est max. toile sur ce safari / appareils.
Dado
Vos tailles sont assez arbitraires, n'est-ce pas? Avez-vous de la documentation? Quel a été votre processus de test?
NodeNodeNode
3

J'ai essayé de déterminer par programme la limite: définir la taille de la toile à partir de 35000, descendre de 100 jusqu'à ce qu'une taille valide soit trouvée. À chaque étape, écrire le pixel en bas à droite, puis le lire. Cela fonctionne - avec prudence.

La vitesse est acceptable si la largeur ou la hauteur est réglée sur une valeur faible (par exemple 10-200) de cette façon: get_max_canvas_size('height', 20) .

Mais s'il est appelé sans largeur ou hauteur comme get_max_canvas_size(), le canevas créé est si grand que la lecture de la couleur SINGLE pixel est très lente, et dans IE provoque un sérieux blocage.

Si ce test pouvait être fait d'une manière ou d'une autre sans lire la valeur des pixels, la vitesse serait acceptable.

Bien sûr, le moyen le plus simple de détecter la taille maximale serait un moyen natif d'interroger la largeur et la hauteur maximales. Mais Canvas est «un niveau de vie», donc peut-être arrivera-t-il un jour.

http://jsfiddle.net/timo2012/tcg6363r/2/ (Attention, votre navigateur peut se bloquer!)

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
la source
2
Cela serait grandement optimisé en utilisant en.wikipedia.org/wiki/Exponential_search
Brendan Annable
1
Il est vraiment insensé que l'échec de la création d'une toile de cette taille soit silencieux, donc il n'y a aucun moyen de détecter ... pas mal du tout de votre part, c'est juste une chose folle pour les navigateurs de se jeter sur nous
Colin D
@ColinD Je suis d'accord. Ce ne serait qu'une petite information quelque part. Ça le ferait. Et devrait.
Timo Kähkönen
3

Lorsque vous utilisez des toiles WebGL, les navigateurs (y compris ceux de bureau) imposent des limites supplémentaires sur la taille du tampon sous-jacent. Même si votre toile est grande, par exemple 16 000x16 000, la plupart des navigateurs afficheront une image plus petite (disons 4096x4096) et la redimensionneront. Cela pourrait provoquer une pixellisation laide, etc.

J'ai écrit du code pour déterminer cette taille maximale en utilisant la recherche exponentielle, si quelqu'un en a besoin. determineMaxCanvasSize()est la fonction qui vous intéresse.

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

Aussi dans un jsfiddle https://jsfiddle.net/1sh47wfk/1/

Michał
la source
Existe-t-il une documentation officielle sur le comportement de mise à l'échelle?
Magnus Lind Oxlund
1
@MagnusLindOxlund Les informations de la réponse ont été déterminées expérimentalement. La chose la plus proche que j'ai trouvée après une recherche rapide est la suivante: khronos.org/registry/webgl/specs/latest/1.0/#2.2 , en disant: "La contrainte ci-dessus ne change pas la quantité d'espace consommée par l'élément canvas sur la page Web "quand il parle de limiter la taille du tampon de dessin. Il semble que la norme WebGL n'entre pas dans les détails fous sur la façon dont l'élément canvas est censé afficher le tampon de dessin.
Michał
1

Vous pouvez le découper et ajouter automatiquement en javascript autant de toiles plus petites que nécessaire et dessiner les éléments sur le canevas approprié. Il se peut que vous manquiez encore de mémoire, mais vous obtiendriez la limite du canevas unique.

JJ_Coder4Hire
la source
0

Je ne sais pas comment détecter la taille maximale possible sans itération, mais vous pouvez détecter si une taille de toile donnée fonctionne en remplissant un pixel, puis en lisant la couleur. Si le canevas n'a pas été rendu, la couleur que vous récupérez ne correspondra pas. W

code partiel:

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);
SystémiquePluriel
la source