SVG 备用方案

Avatar of Chris Coyier
Chris Coyier 发表

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费额度!

更新:这是我们发布的 关于 SVG 备用方案的更全面指南

最近,互联网上流传着一种由 Alexey Ten 提出的 非常巧妙的技术,用于为 SVG 提供图像备用方案。它在经典的无 SVG 支持浏览器 IE 8 及更低版本和 Android 2.3 中都能实现您想要的效果。如果我们深入研究,会发现一些非常有趣的东西,包括一些意想不到的行为,这有点令人沮丧。

Alexey 的技术看起来像这样

<svg width="96" height="96">
  <image xlink:href="svg.svg" src="svg.png" width="96" height="96" />
</svg>

这个想法是基于 Jake Archibald 重新审视浏览器渲染 <image> 标签的方式类似于 <img> 的事实。这只是过去时代的一个奇怪的别名,可以被利用。

显示内容

就支持和不支持浏览器的显示内容而言,该技术非常接近完美。IE 8 不支持 SVG,它会显示备用 PNG。Android 2.3 也是如此。

我感到困惑的部分是 iOS 3 和 4。这些操作系统的原生浏览器支持 SVG,以 <img src="*.svg"> 或 CSS background-image 的形式。但是使用此技术,会显示 PNG 而不是 SVG。问题在于 iOS 3 和 4 不支持“内联”SVG(例如,在 HTML 中使用 <svg> 标签直接使用 SVG)(支持图表)。SVG 支持并非简单的是/否问题。它取决于 SVG 的使用方式。

重点:在这些情况下,看到 <img> 工作而 <image> 不工作很奇怪。并且由于这些浏览器确实支持 SVG,因此不使用它很可惜。

下载内容

当然,我们也关心下载的内容,因为这会影响页面性能。同样,这主要是个好消息。现代浏览器(如 Chrome)只下载 SVG。Android 2.3 只下载 PNG。

但是,我们在 Internet Explorer 9、10 和 11 版本(此时为预览版)中遇到了问题。在 IE 9 中,您可以在“网络”时间轴中看到两个图像都出现了。

PNG 最终的结果为“已中止”,并列为接收了 0 B,但仍然会影响下载时间。

IE 10 的情况类似,它似乎中止得更快,并快速切换到 SVG,而没有太多停机时间。

Scott Jehl 建议使用 Charles Proxy 更准确地测试正在下载的内容。Charles 验证了正在请求 PNG。

响应正文的大小确实是 0 B,因此中止发生得足够快,不会传输太多数据。尽管请求和响应标头组合为 782 B,并且发生的时间约为 300 毫秒。Yoav Weiss 指出,如果页面顶部有阻塞脚本,问题可能会更严重。

另请注意,使用代理可能会影响下载的内容,正如 Steve Souders 在这篇文章中 指出的那样,Jason Grigsby 的这篇文章 关于 Charles Proxy

Andy Davies 的研究表明,在 Windows 7 上的 IE 11 中,PNG 请求根本不会中止。

发现

我从这个 Pen 中进行了显示测试,但是下载测试我确保只使用页面上存在的一种技术,并使用 调试视图,这样页面上除了原始技术之外什么也没有。

查看 Chris Coyier 制作的 Pen SVG 测试@chriscoyier)在 CodePen

因此,备用方案

Alexey Ten 的方法仍然很巧妙,如果您可以接受 iOS 显示问题和 IE 下载问题,它仍然可以使用。以下是一些其他备用方案技术,具体取决于您如何使用 SVG。

如果您将 SVG 用作背景图像…

Modernizr 有一个 SVG 测试。因此,您可以使用它注入到 HTML 元素上的类名来声明备用方案。

.my-element {
  background-image: url(image.svg);
}
.no-svg .my-element {
  background-image: url(image.png);
}

这应该不会有任何双重下载问题,但没有 Modernizr 依赖项。

一种完全没有依赖项的非常巧妙的技术是使用带有多个背景和旧语法线性渐变的小 CSS 技巧

.my-element {
  background-image: url(fallback.png);
  background-image: 
    linear-gradient(transparent, transparent),
    url(image.svg);
}

以前有一种技术只是在这里使用了多个背景。但这并没有完全完成工作,因为 Android 2.3 支持它但不支持 SVG,因此出现了问题。这将旧语法渐变与多个背景相结合,因此可以在任何地方工作。参考

如果这两个都支持,浏览器将使用第二个声明(使用 SVG),否则回退到第一个声明(使用 PNG)。

如果您将 SVG 用作内联 <svg>…

David Ensinger 发布了一种技术,在 <svg> 中使用 <foreignObject> 标签。但问题是,无论如何都会加载备用方案,导致始终双重下载,而不是像 <image> 技术那样仅偶尔下载。

这有点复杂,但 Artur A 发布了这个想法,它似乎解决了双重下载问题,并且可以在任何地方工作

<!DOCTYPE html>
<html>

<head>
    <title>HTML5 SVG demo</title>
    <style>
     .nicolas_cage {
         background: url('nicolas_cage.jpg');
         width: 20px;
         height: 15px;
     }
     .fallback {
     }
    </style>
</head>

<body>
  
  <svg xmlns="http://www.w3.org/2000/svg" width="0" height="0">
    <style>
    <![CDATA[ 
      .fallback { background: none; background-image: none; display: none; }
    >
    </style>
  </svg>

  <!-- inline svg -->
  <svg xmlns="http://www.w3.org/2000/svg" width="40" height="40">
  <switch>
     <circle cx="20" cy="20" r="18" stroke="grey" stroke-width="2" fill="#99FF66" />
     <foreignObject>
         <div class="nicolas_cage fallback"></div>
     </foreignObject>
  </switch>
  </svg>

  <!-- external svg -->
  <object type="image/svg+xml" data="circle_orange.svg">
    <div class="nicolas_cage fallback"></div>
  </object>

</body>

</html>

或者,您可以执行以下操作

<svg> ... inline SVG stuff ... </svg>
<div class="my-svg-alternate"></div>

然后再次使用 Modernizr SVG 测试来获取支持 HTML 类,然后…

.my-svg-alternate {
  display: none;
}
.no-svg .my-svg-alternate {
  display: block;
  width: 100px;
  height: 100px;
  background-image: url(image.png);
}

或者将 SVG 包裹在该 div 中,以便可以将该 div 用于一致的大小调整。

如果您使用内联 SVG,则很有可能是在使用 <use>,在这种情况下,脚本 svg3everybody 做得非常好。如果支持您使用内联 SVG 的方式,它就会正常工作。如果它在 IE(不支持)中被外部引用,它会通过 ajax 加载它使其工作。如果它根本无法工作,它有一个语法,您可以使用它来指定 PNG 备用方案。

如果您将 SVG 用作 <object>…

您可以使用 object 标签本身作为元素,在 Modernizr 测试后对其进行样式设置。

<object type="image/svg+xml" data="image.svg" class="logo"></object>
.no-svg .logo {
  display: block;
  width: 100px;
  height: 100px;
  background-image: url(image.png);
}

如果您将 SVG 用作 <img>…

有一种技术可以动态交换失败的 SVG 文件

<img src="image.svg" onerror="this.src='image.png'">

这需要特殊的 HTML,如您所见,因此,如果这对您来说不可行或不切实际,您可以使用 Modernizr 交换源。这使用了 Modernizr 的 JS API,而不是类名

if (!Modernizr.svg) {
  $("img[src$='.svg']")
    .attr("src", fallback);
}

其中 fallback 是非 SVG 图像备用方案所在 URL 的字符串。您可以将其保存在 data-fallback 属性中,使用一致的 URL 模式(其中只需将 .svg 替换为 .png),或您可以想到的其他任何智能方法。SVGeezy 是一个执行此操作的库,并使用 一种巧妙的检测方法

这些方法的问题在于它们需要一个 <img src>,并且该 src 将被预取,您无法阻止它。因此,您可能会遇到潜在的双重下载问题,这始终是不好的。

或者你可以使用与内联 SVG 和对象技术类似的技术,其中一个隐藏的 DIV 会显示一个回退内容。

另一个非常不错的选择是使用 Picturefill<picture> 元素允许内容类型的回退。你需要 polyfill,因为 picture 还没有得到很好的支持。虽然这解决了双重下载的问题,这很好,但它在没有 JavaScript 的情况下无法工作。但话说回来,其他方法也一样。它看起来像这样

<picture>

  <source srcset="graph.svg" type="image/svg+xml">

  <img srcset="graph-small.png, graph-medium.png 400, graph-large.png 800" alt="A lovely graph.”>

</picture>

请记住,一种回退方式就是根本没有回退

如果你正在使用 SVG 作为背景图像,那可能只是装饰性的,对网站的运行并不重要,所以这种情况可能不需要回退。

类似地,对于任何类型的 SVG 修饰,例如按钮中带有文字的图标。如果没有那个 SVG,它仍然是一个带有文字的按钮,所以可以接受的回退。

好的,就是这样

如果你还没有使用 SVG,所以这篇文章对你来说意义不大,你应该开始使用它,因为它很棒。 这篇文章 可以帮助你入门。

如果你有更多数据要分享,我们很乐意倾听。

祝你好运!