<canvas>元素的最大大小


112

我正在使用一个高度为600to 1000像素,宽度为数万或数十万像素的canvas元素。但是,经过一定数量的像素(显然是未知的)之后,画布不再显示我使用JS绘制的形状。

有谁知道是否有限制?

在Chrome 12和Firefox 4中都进行了测试。


2
@Šime他说tens OR hundreds of thousands……
Tadeck

6
我也经历过 我有一个8000x8000画布,可以正常工作,但是当我将其放大时,内容将消失,并且不会绘制。在iPad上无法使用的尺寸要小得多。我想知道它是否存在某种内存限制。
约书亚

28
当您在两年前找到自己的评论而您仍在处理相同的问题时,这很奇怪。
2013年

4
@约书亚,还有一年后!
尤金(Eugene Yu)

1
问题是如何捕捉这些错误或更好的警告,它们是什么?我无法检测到设备硬件,因此必须对错误做出反应。
br4nnigan

Answers:


115

2014年10月13日更新

所有经过测试的浏览器都限制了canvas元素的高度/宽度,但是许多浏览器也限制了canvas元素的总面积。可以测试的浏览器的限制如下:

铬:

最大高度/宽度:32,767像素
最大区域:268,435,456像素(例如16,384 x 16,384)

Firefox:

最大高度/宽度:32,767像素
最大面积:472,907,7​​76像素(例如22,528 x 20,992)

IE浏览器:

最大高度/宽度:8,192像素
最大面积:不适用

IE Mobile:

最大高度/宽度:4,096像素
最大面积:不适用

其他:

我目前无法测试其他浏览器。有关其他限制,请参阅此页面上的其他答案。


在大多数浏览器中,超过最大长度/宽度/面积会使画布无法使用。(即使在可用区域中,它也会忽略任何绘制命令。)IE和IE Mobile将遵守可用空间中的所有绘制命令。


3
因此,相关的问题...如果他们需要的画布大于允许的最大值,该如何解决这个限制?
aroth

2
@aroth,我最终围绕着画布API编写了一个包装器,该包装器使用了多个堆叠的<canvas>元素,并自动将绘图指令应用于适当的上下文。
Brandon Gano 2014年

1
听起来非常有用。不认为它在github上吗?
aroth 2014年

5
Safari(非iOS版本)使用32K,例如Chrome.Firefox。另外,如果您想重新创建一些包装代码,则由于IE的微不足道的8K限制,我启动了自己的开源版本: github.com/adam-roth/wrapped-canvas
aroth

7
iPhone 6 Plus上的iOS 9.3.1 Safari限于16777216像素(例如4096 x 4096)。我怀疑这是较新的iOS设备上的通用数字
matb33 '16

16

在画布高度大于8000的Firefox上,我遇到了内存不足的错误,chrome似乎处理得更高,至少达到32000。

编辑:运行更多测试后,我发现Firefox 16.0.2有一些非常奇怪的错误。

首先,我似乎从内存(在javascript中创建)画布获得了与HTML声明的画布不同的行为。

其次,如果您没有适当的html标记和meta字符集,则画布可能会限制为8196,否则可以增加到32767。

第三,如果您获得了画布的2d上下文,然后更改了画布的大小,那么您也可能会被限制为8196。在获取2d上下文之前,只需设置画布大小即可使您拥有32767,而不会出现内存错误。

我一直无法始终得到内存错误,有时仅在首页加载时出现,然后进行后续的高度更改即可。这是我使用http://pastebin.com/zK8nZxdE测试的html文件。


4
“第二,如果您没有适当的html标记和meta字符集,则画布可能会限制为8196,否则您最多可以使用32767”-在这里,正确的html标记和meta字符集是什么意思?
杰克·艾德利

您确定是8192(2 ^ 13)吗?8196是一个奇怪的数字。
jamesdlin

14

iOS最大画布大小(宽x高):

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

在2014年3月进行了测试。


这些值是任意的。请参阅下面的我的帖子,其中引用了safari的内容指南
dcbarans

11

若要进一步说明@FredericCharette答案:根据safari的内容指南,在“了解iOS资源限制”部分下:

对于RAM小于256 MB的设备,canvas元素的最大大小为3百万像素,对于大于或等于256 MB RAM的设备,canvas的最大大小为5百万像素。

因此,任何大小变化的5242880(5 x 1024 x 1024)像素都可以在大型存储设备上使用,否则为3145728像素。

5百万像素画布(宽x高)的示例:

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上可以正常使用但在iOS上为空白之后,我找到了这个答案,还找到了stackoverflow.com/questions/12556198/…并使我的Phonegap应用程序正常工作。)
Magnus Smith

仅供参考,Apple已从“ Safari /知道iOS资源限制”中删除了特定的大小信息。现在,它仅是关于通过使用小图像来最小化带宽的一般建议。
ToolmakerSteve

@ matb33报道关于该问题的评价是iPhone 6似乎有限制16 * 1024 * 1024
ToolmakerSteve

11

2018年更新:

随着时间的流逝,画布的局限性发生了变化。可悲的是,没有改变的是,浏览器仍然没有通过Canvas API提供有关画布大小限制的信息。

对于那些希望以编程方式确定浏览器的最大画布尺寸或测试对自定义画布尺寸的支持的用户,请查看canvas-size

从文档:

HTML canvas元素受到现代和旧版浏览器的广泛支持,但是每种浏览器和平台组合都具有独特的大小限制,当超出限制时,将使画布无法使用。不幸的是,浏览器无法提供确定其局限性的方法,也无法在创建了无法使用的画布后提供任何形式的反馈。这使得处理大型画布元素成为一个挑战,尤其是对于支持各种浏览器和平台的应用程序而言。

该微库提供了浏览器支持的HTML画布元素的最大面积,高度和宽度,以及测试自定义画布尺寸的能力。通过在创建新画布元素之前收集此信息,应用程序能够在每个浏览器/平台的大小限制内可靠地设置画布尺寸。

README中有一个演示链接和测试结果,还有一个有关性能和虚拟机注意事项已知问题部分。

完全公开,我是图书馆的作者。我在2014年创建了它,最近又重新访问了一个与画布相关的新项目的代码。令我感到惊讶的是,在2018年仍然缺少可用于检测画布大小限制的工具,因此我更新了代码并发布了它,并希望它可以帮助其他遇到类似问题的人。


8

根据w3的规范,width / height接口是一个无符号的长整数-所以0到4,294,967,295(如果我还记得那个数字的话-可能有几个)。

编辑:奇怪的是,它说无符号长,但测试显示只是一个普通的长值作为最大:2147483647 的jsfiddle - 47级的作品,但多达48个,并将其恢复为默认值。


嗯 有趣,但我什至没有达到15亿美元。
严重dev11年

编辑,对不起(这就是我没有先测试就得到的)-添加了一个jsfiddle来显示。
WSkid,2011年

7

即使画布允许您放置height = 2147483647,但是当您开始绘制时,什么也不会发生

仅当我将高度恢复到32767时才会进行绘制


7

iOS有不同的限制。

使用iOS 7模拟器,我可以证明此限制为5MB,如下所示:

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

在PC上,
我认为没有限制,但是可以,您可以摆脱内存异常。

在移动设备上-
这是移动设备画布的限制:-

对于RAM小于256 MB的设备,canvas元素的最大大小为3百万像素,对于大于或等于256 MB RAM的设备,canvas的最大大小为5百万像素。

因此,例如-如果您想支持Apple的较早硬件,则画布的大小不能超过2048×1464。

希望这些资源能帮助您将自己拉出来。


3

Safari(所有平台)的限制要低得多。

已知的iOS / Safari局限性

例如,我有一个6400x6400px的画布缓冲区,上面绘制了数据。通过跟踪/导出内容并在其他浏览器上进行测试,我发现一切都很好。但是在Safari上,它将跳过此特定缓冲区在我的主要上下文上的绘制。


eep!Safari跳过绘制画布,因为您将较大的画布用作“允许”。请参阅我上面发布的最大尺寸!最高 在此Safari /设备上使用画布。
Dado

您的尺寸相当随意,不是吗?你有文件吗?您的测试流程是什么?
NodeNodeNode 2014年

3

我试图以编程方式找出限制:设置画布大小从35000开始,递减100直到找到有效大小。在每一步中,写入右下像素,然后读取。它有效-谨慎。

如果宽度或高度被设置为一些低的值(如:10-200)这样的速度是可以接受的:get_max_canvas_size('height', 20)

但是,如果调用时不带宽度或高度之类的get_max_canvas_size(),则创建的画布太大,以致于读取SINGLE像素颜色非常慢,并且在IE中会导致严重的挂起。

如果某种类似的测试可以在不读取像素值的情况下进行,则速度可以接受。

当然,最简单的检测最大尺寸的方法是查询最大宽度和高度的某些本地方法。但是Canvas是“生活水平”,所以也许有一天会到来。

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


1
如此大的画布创建失败的失败真是太疯狂了,所以没有办法检测到……无论从哪方面来说都还不错,对于浏览器来说,这是一件疯狂的事
Colin D

@ColinD我同意。那只是某处的一小部分信息。它会。并且应该。
TimoKähkönen'2

3

当您使用WebGL画布时,浏览器(包括桌面浏览器)将对基础缓冲区的大小施加额外的限制。即使您的画布很大,例如16,000x16,000,大多数浏览器也会渲染较小的图片(例如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/


是否有任何有关扩大行为的官方文档?
马格纳斯·林德·奥克斯隆德(Marnus Lind Oxlund),

1
@MagnusLindOxlund答案中的信息已通过实验确定。经过快速搜索后,我发现的最接近的东西是:khronos.org/registry/webgl/specs/latest/1.0/#2.2,说:“上述约束不会改变canvas元素在网页上消耗的空间量当谈到限制绘图缓冲区的大小时。似乎WebGL标准并未涉及关于canvas元素应如何显示绘图缓冲区的疯狂细节。
米哈尔

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);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.