ChatGPT解决这个技术问题 Extra ChatGPT

<canvas> 元素的最大尺寸

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

有谁知道有没有限制?

在 Chrome 12 和 Firefox 4 中测试。

@Šime 他说tens OR hundreds of thousands...
我也有这种经历。我有一个 8000x8000 的画布,可以正常工作,但是当我把它变大时,内容消失了,它就不会画了。在我的 iPad 上,它无法工作的尺寸要小得多。我想知道它是否是某种内存限制。
当您在 2 年前找到自己的评论并且您仍在处理相同的该死问题时,这很奇怪。
@Joshua,一年后!
问题是我如何捕捉这些错误或更好的警告,它们是什么?我无法检测到设备硬件,所以我必须对错误做出反应。

B
Brandon Gano

2014 年 10 月 13 日更新

所有测试的浏览器都对画布元素的高度/宽度有限制,但许多浏览器也限制了画布元素的总面积。对于我能够测试的浏览器,限制如下:

铬合金:

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

火狐:

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

IE:

最大高度/宽度:8,192 像素最大面积:N/A

IE手机:

最大高度/宽度:4,096 像素最大面积:N/A

其他:

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

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


那么,相关的问题......如果他们需要一个大于允许的最大值的画布,如何解决这个限制?
@aroth,我最终围绕 canvas API 编写了一个包装器,它使用多个堆叠的 <canvas> 元素并自动将绘图指令应用于适当的上下文。
听起来很有用。不要以为它在github上?
Safari(非 iOS 版本)与 Chrome.Firefox 一样使用 32K。此外,如果您想尝试重新创建一些包装代码,由于 IE 微不足道的 8K 限制,我创建了自己的开源版本:github.com/adam-roth/wrapped-canvas
iPhone 6 Plus 上的 iOS 9.3.1 Safari 的区域限制为 16777216 像素(例如,4096 x 4096)。我怀疑这是新 iOS 设备中的常见数字
j
jhildenbiddle

接受的答案已过时且不完整。

浏览器施加不同的画布大小限制,但这些限制通常会根据可用的平台和硬件而变化。这使得很难做出诸如“[browser] 的最大画布 [area/height/width][value]”之类的陈述,因为 [value] 可以根据操作系统、可用 RAM 或 GPU 类型而改变。

有两种处理大型 HTML <canvas> 元素的方法:

将画布尺寸限制为已知可在所有支持的平台上工作的尺寸。在渲染之前以编程方式确定客户端上的画布限制。

那些希望以编程方式确定客户端画布限制的人员应考虑使用 canvas-size

GitHub:https://github.com/jhildenbiddle/canvas-size

NPM:https://www.npmjs.com/package/canvas-size

从文档:

现代和旧版浏览器广泛支持 HTML 画布元素,但每个浏览器和平台组合都会施加独特的大小限制,超过时会导致画布无法使用。不幸的是,浏览器没有提供一种方法来确定它们的限制是什么,也没有在创建不可用的画布之后提供任何类型的反馈。这使得使用大型画布元素成为一项挑战,尤其是对于支持各种浏览器和平台的应用程序。这个微型库提供浏览器支持的 HTML 画布元素的最大面积、高度和宽度,以及测试自定义画布尺寸的能力。通过在创建新的画布元素之前收集这些信息,应用程序能够在每个浏览器/平台的大小限制内可靠地设置画布尺寸。

各种平台和浏览器组合的测试结果可在此处获得:

测试结果:https://github.com/jhildenbiddle/canvas-size#test-results

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


B
Ben Burnett

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

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

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

其次,如果您没有正确的 html 标记和元字符集,画布可能会被限制为 8196,否则您可以达到 32767。

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

我无法始终如一地得到内存错误,有时它只是在第一页加载时,然后随后的高度变化才起作用。这是我用 http://pastebin.com/zK8nZxdE 测试的 html 文件。


“其次,如果您没有正确的 html 标记和元字符集,画布可能会被限制为 8196,否则您可以达到 32767”——这里正确的 html 标记和元字符集是什么意思?
你的意思是 8192 (2^13)? 8196是一个奇怪的数字。
D
Dado

iOS 最大画布尺寸(宽 x 高):

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

2014 年 3 月测试。


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

要扩展 @FredericCharette 的答案:根据“了解 iOS 资源限制”部分下的 safari's content guide

对于 RAM 小于 256 MB 的设备,画布元素的最大大小为 3 兆像素,对于 RAM 大于或等于 256 MB 的设备,最大大小为 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

更具体地说,iPhone5 的限制是 3 * 1024 * 1024 = 3145728 像素。 (在想知道为什么我的画布在 Android 上很好但在 iOS 上是空白之后,我找到了这个答案,还找到了 stackoverflow.com/questions/12556198/… 并让我的 Phonegap 应用程序正常工作。)
仅供参考,Apple 已从“Safari / Know iOS Resource Limits”中删除了特定的大小信息。现在它只是关于通过使用小图像来最小化带宽的一般建议。
@matb33 在对 iPhone 6 似乎有限制 16 * 1024 * 1024 的问题的评论中报告。
W
WSkid

根据 w3 specs,宽度/高度接口是一个无符号长 - 所以 0 到 4,294,967,295(如果我没记错这个数字 - 可能会少一些)。

编辑:奇怪的是,它说无符号长,但它测试显示只是一个正常的长值作为最大值:2147483647。Jsfiddle - 47 有效,但最多 48 并且它恢复为默认值。


唔。有趣,但我什至没有达到 15 亿。
已编辑,抱歉(这就是我没有先测试的结果)——添加了一个 jsfiddle 来显示。
a
avikaza123

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

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


m
matt burns

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

您不应将此类数据基于 iOS 模拟器。
M
Manish Gupta

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

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

对于 RAM 小于 256 MB 的设备,画布元素的最大大小为 3 兆像素,而对于 RAM 大于或等于 256 MB 的设备,画布元素的最大大小为 5 兆像素。

所以举个例子——如果你想支持苹果的旧硬件,你的画布大小不能超过 2048×1464。

希望这些资源能帮助您摆脱困境。


N
NodeNodeNode

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

Known iOS/Safari Limitations

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


耶! Safari 会跳过绘制画布,因为“允许”使用更大的画布。请参阅我在上面发布的最大尺寸!这是最大的。在这个 safari/devices 上画布。
你的尺寸很随意,不是吗?你有任何文件吗?你的测试过程是什么?
T
Timo Kähkönen

我试图以编程方式找出限制:从 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;
  }
}

这将通过使用 en.wikipedia.org/wiki/Exponential_search 得到极大优化
创建这么大的画布的失败是无声的,这真是太疯狂了,所以没有办法检测到......对你来说无论如何都不错,浏览器突然出现在我们身上只是一件疯狂的事情
@ColinD 我同意。这只是某处的一小部分信息。它会。并且应该。
M
Michał

当您使用 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/


是否有关于扩大行为的任何官方文件?
@MagnusLindOxlund 答案中的信息已通过实验确定。我在快速搜索后发现的最接近的内容是:khronos.org/registry/webgl/specs/latest/1.0/#2.2,当它谈到限制绘图缓冲区的大小时,它说:“上面的约束不会改变画布元素在网页上消耗的空间量”。似乎 WebGL 标准并没有详细说明 canvas 元素应该如何显示绘图缓冲区。
J
JJ_Coder4Hire

您可以将其分块并在 javascript 中根据需要自动添加尽可能多的较小画布并在适当的画布上绘制元素。您最终可能仍然会耗尽内存,但会通过单个画布限制来解决。


S
SystemicPlural

我不知道如何在没有迭代的情况下检测最大可能大小,但是您可以通过填充像素然后读取颜色来检测给定的画布大小是否有效。如果画布尚未渲染,那么您返回的颜色将不匹配。 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);