检测不支持 HTML5 <canvas> 的最佳方法

IT技术 javascript html canvas progressive-enhancement graceful-degradation
2021-01-20 05:21:38

处理浏览器不支持 HTML5<canvas>标签情况的标准方法是嵌入一些后备内容,例如:

<canvas>Your browser doesn't support "canvas".</canvas>

但页面的其余部分保持不变,这可能是不适当的或误导性的。我想要某种检测画布不支持的方法,以便我可以相应地呈现页面的其余部分。你会推荐什么?

6个回答

这是 Modernizr 中使用的技术,基本上所有其他可以使用画布工作的库:

function isCanvasSupported(){
  var elem = document.createElement('canvas');
  return !!(elem.getContext && elem.getContext('2d'));
}

由于您的问题是检测时,它支持,我建议使用它像这样:

if (!isCanvasSupported()){ ...
如果 Canvas 不存在,elem.getContext == undefined. !undefined = true, 和!true = false, 所以这让我们返回一个布尔值,而不是 undefined 或上下文。
2021-03-15 05:21:38
另一种“转换”为布尔值的方法是:(undefined ? true : false虽然有点长)。
2021-03-24 05:21:38
我应该注意到有不同类型的画布支持。早期的浏览器实现不支持toDataURL. Opera Mini 仅支持基本的画布渲染,不支持文本 APIOpera Mini的,可以排除这种方式,只是交叉引用。
2021-03-27 05:21:38
@2astalavista 双重否定 (!!) 就像铸造一样。它将 truey 或 falsey 语句转换为布尔值。例如:var i = 0i 评估为假,但 typeof i 返回“数字”。typeof !!i 返回“布尔值”。
2021-03-28 05:21:38
为什么双重否定(!!)代表?
2021-03-30 05:21:38

在浏览器中检测画布支持有两种流行的方法:

  1. 马特关于检查 存在的建议getContext,也被 Modernizr 库以类似的方式使用:

    var canvasSupported = !!document.createElement("canvas").getContext;
    
  2. 检查HTMLCanvasElement接口是否存在,如WebIDLHTML规范所定义IE 9 团队的一篇博客文章也推荐了这种方法

    var canvasSupported = !!window.HTMLCanvasElement;
    

我的建议是后者的变体(请参阅附加说明),原因如下:

  • 每个已知的支持画布的浏览器——包括 IE 9——都实现了这个接口;
  • 代码在做什么更简洁,一目了然;
  • getContext方法在所有浏览器上都明显变慢,因为它涉及创建 HTML 元素。当您需要尽可能提高性能时(例如在像 Modernizr 这样的库中),这并不理想。

使用第一种方法没有明显的好处。这两种方法都可以被欺骗,但这不太可能是偶然发生的。

补充笔记

可能仍然需要检查是否可以检索 2D 上下文。据了解,一些移动浏览器可以同时为上述检查返回true,但返回null.getContext('2d')这就是为什么 Modernizr 还检查.getContext('2d'). 然而,WebIDL 和 HTML —— 再次 —— 为我们提供了另一个更好、更快的选择:

var canvas2DSupported = !!window.CanvasRenderingContext2D;

请注意,我们可以完全跳过检查 canvas 元素,直接检查 2D 渲染支持。CanvasRenderingContext2D界面也是 HTML 规范的一部分。

必须使用检测 WebGL支持getContext方法因为即使浏览器可能支持WebGLRenderingContext如果浏览器由于驱动程序问题而无法与 GPU 交互并且没有软件实现getContext()也可能返回null在这种情况下,首先检查接口允许您跳过检查getContext

var cvsEl, ctx;
if (!window.WebGLRenderingContext)
    window.location = "http://get.webgl.org";
else {
    cvsEl = document.createElement("canvas");
    ctx = cvsEl.getContext("webgl") || cvsEl.getContext("experimental-webgl");

    if (!ctx) {
        // Browser supports WebGL, but cannot create the context
    }
}

##性能比较 该getContext方法的性能在 Firefox 11 和 Opera 11 中慢 85-90%,在 Chromium 18 中慢约 55%。

    简单对比表,点击在浏览器中运行测试

我运行了您的基准测试,即使是“慢”方法也可以每秒完成约 800,000 次。同样,如果结果被缓存,那么决定使用哪种方法应该基于健壮性,而不是性能(假设健壮性存在差异。)
2021-03-15 05:21:38
这很有趣,但只要测试在一百左右毫秒以内,那不是很好吗?我想无论如何它们都比这快得多。如果你记住一个测试这个的函数,那么你只需要支付一次成本。
2021-03-17 05:21:38
诺基亚 S60 和 Blackberry Storm 是一些会在您提议的 2D 画布检测中误报的设备。不幸的是,移动变得非常麻烦,供应商不遵守规则。:( 所以我们最终会得到更完整(即更慢)的测试来确保准确的结果。
2021-04-07 05:21:38
@DrewNoakes:是的,您几乎总是应该追求兼容性而不是速度。我的论点是,我正在驳斥 Paul 的兼容性声明,这是基于我在他的评论中提到的至少一个有问题的浏览器中我自己的测试。我一直无法测试其他浏览器,但我仍然不相信存在问题。您应该始终致力于在不牺牲兼容性的情况下获得最佳性能。我不是在谈论微优化,但是如果您正在运行数百个测试并且它们都未经优化,那么是的,它会有所作为。
2021-04-07 05:21:38
@Paul:这很有趣,我测试了 BlackBerry Storm 模拟器,它们false都为您的示例和我的示例返回,似乎它们不提供CanvasRenderingContext2D接口。到目前为止,我还无法测试 S60,但我仍然很好奇并且可能很快就会测试。
2021-04-13 05:21:38

我通常getContext在创建画布对象时运行检查

(function () {
    var canvas = document.createElement('canvas'), context;
    if (!canvas.getContext) {
        // not supported
        return;
    }

    canvas.width = 800;
    canvas.height = 600;
    context = canvas.getContext('2d');
    document.body.appendChild(canvas);
}());

如果支持,那么您可以继续画布设置并将其添加到 DOM。这是Progressive Enhancement 的一个简单示例,我(个人)更喜欢 Graceful Degradation。

@brainjam - 不,我在代码末尾附近使用该变量。我尝试遵循JSLint 的“建议”(在这种情况下……var每个函数只有 1 个语句)。
2021-03-28 05:21:38
这是, context第二行的流浪者吗?
2021-04-04 05:21:38

为什么不试试Modernizr呢?它是一个提供检测功能的 JS 库。

引用:

你有没有想过在你的 CSS 中使用 if 语句来获得像 border-radius 这样很酷的特性?好吧,有了 Modernizr,您就可以做到这一点!

我们在 Modernizr 中使用的测试是这样的:return !!document.createElement('canvas').getContext 这绝对是最好的测试方式。
2021-04-04 05:21:38
Modernizr 是一个有用的库,但是为了检测画布支持而拉入整个库会有点浪费。如果您还需要检测其他功能,那么我会推荐它。
2021-04-06 05:21:38
try {
    document.createElement("canvas").getContext("2d");
    alert("HTML5 Canvas is supported in your browser.");
} catch (e) {
    alert("HTML5 Canvas is not supported in your browser.");
}