【问题标题】:Maximum size of a <canvas> element<canvas> 元素的最大尺寸
【发布时间】:2011-08-30 04:52:51
【问题描述】:

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

有人知道有没有限制吗?

在 Chrome 12 和 Firefox 4 中都经过测试。

【问题讨论】:

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

标签: javascript html canvas


【解决方案1】:

2014 年 10 月 13 日更新

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

铬:

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

火狐:

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

IE:

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

IE 手机:

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

其他:

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


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

【讨论】:

  • 那么,相关的问题......如果他们需要一个大于允许的最大值的画布,如何解决这个限制?
  • @aroth,我最终围绕 canvas API 编写了一个包装器,它使用多个堆叠的 &lt;canvas&gt; 元素并自动将绘图指令应用于适当的上下文。
  • 听起来很有用。不要以为它在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 设备中的常见数字
【解决方案2】:

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

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

有两种处理大型 HTML &lt;canvas&gt; 元素的方法:

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

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

来自文档:

现代和传统都广泛支持 HTML 画布元素 浏览器,但每个浏览器和平台的组合都强加了独特的 尺寸限制,超过时会使画布无法使用。 不幸的是,浏览器没有提供一种方法来确定他们的 限制是,他们也不提供任何形式的反馈后 已创建不可用的画布。这使得使用大画布 元素是一项挑战,尤其是对于支持 各种浏览器和平台。

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

这里提供了各种平台和浏览器组合的测试结果:

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

【讨论】:

    【解决方案3】:

    我在画布高度大于 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 是一个奇怪的数字。
    【解决方案4】:

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

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

    于 2014 年 3 月测试。

    【讨论】:

    • 这些值是任意的。请参阅下面我的帖子,其中引用了 safari 的内容指南
    【解决方案5】:

    扩展一点@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 的问题。
    【解决方案6】:

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

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

    【讨论】:

    • 嗯。有趣,但我什至没有达到 15 亿。
    • 已编辑,抱歉(这就是我没有先测试的结果)——添加了一个 jsfiddle 来显示。
    【解决方案7】:

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

    只有当我将高度恢复到 32767 时才会进行绘图

    【讨论】:

      【解决方案8】:

      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 模拟器。
      【解决方案9】:

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

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

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

      例如 - 如果你想支持 Apple 的旧硬件,你的画布大小不能超过 2048×1464。

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

      【讨论】:

        【解决方案10】:

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

        Known iOS/Safari Limitations

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

        【讨论】:

        • 是的! Safari 会跳过绘制画布,因为“允许”使用更大的画布。请参阅我在上面发布的最大尺寸!这是最大的。在这个 safari/devices 上画布。
        • 你的尺寸很随意,不是吗?你有任何文件吗?你的测试过程是什么?
        【解决方案11】:

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

        【讨论】:

        • 这将通过使用en.wikipedia.org/wiki/Exponential_search得到极大优化
        • 创建这么大的画布的失败是无声的,这真是太疯狂了,所以没有办法检测到......对你来说无论如何都不错,这对浏览器来说只是一件疯狂的事情春天来了
        • @ColinD 我同意。这只是某处的一小部分信息。它会。并且应该。
        【解决方案12】:

        当您使用 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 元素应该如何显示绘图缓冲区。
        【解决方案13】:

        您可以对其进行分块,并在 javascript 中根据需要自动添加尽可能多的较小画布,并在适当的画布上绘制元素。您最终可能仍会耗尽内存,但会超出单个画布限制。

        【讨论】:

          【解决方案14】:

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

          【讨论】:

            猜你喜欢
            • 2015-08-09
            • 2016-08-22
            • 1970-01-01
            • 2017-05-05
            • 2022-01-05
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多