最近,互联网上流传着一种由 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 请求根本不会中止。
@stopsatgreen @jaffathecake @chriscoyier 在我的 IE11/Win7 中,响应未中止,并在开发工具和 pcap 中显示 pic.twitter.com/ypitwTYAAH
— Andy Davies (@andydavies) 2013 年 8 月 19 日
发现
我从这个 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,所以这篇文章对你来说意义不大,你应该开始使用它,因为它很棒。 这篇文章 可以帮助你入门。
如果你有更多数据要分享,我们很乐意倾听。
祝你好运!
影响。
你可能想在
onerror
方法中加入this.onerror = null;
,这样如果回退png
请求出错,你就不会出现无限次尝试替换的情况<img src="image.svg" onerror="this.src=image.png;this.onerror=null;">
赞同 Mat 的观点。我无法在推文中写下这些内容 :)
IE 和用作 CSS 选择器的图像存在另一个问题。IE 将选择器“image”的别名设置为“img”。
无论样式是在头部还是单独的 CSS 文件中
http://codepen.io/Kseso/pen/vdzJy/
我喜欢使用 Modernizr 和 JS 在图像元素中交换 SVG 文件。类似于上面所做的。但是,如果我的网站上有许多 SVG 文件,我会使用此脚本回退到 PNG 文件。
不错的技巧,Steve。我使用类似的方法
您对分解的处理很好,但我认为好处远远大于手头的问题。iOS 5+6 基本上占所有 iOS 设备的 96% 以上(根据 这份六月的报告),所以我不确定有多少人应该关心 iOS 3/4。
至于 IE——这可能是人们使用回退的最大原因——我个人一直以来都采取“如果你真的想使用 IE,你就要承担后果”的方式。您将 Modernizr 作为最大、最“安全”的替代方案,但它真的比其他方法好很多吗?
快速创建一个带有默认额外功能的自定义构建,并仅检查 2 个 SVG 选项,库已经输出一个 8kb 的压缩文件。如果我使用 Modernizr,我基本上是在向所有浏览器添加这 8kb,仅仅是为了防止 IE 下载两次?除非您的网站主要是 IE 用户,否则这太荒谬了,我宁愿让 5% 或任何比例的 IE 用户下载额外的图像,在大多数情况下(我想到的是 logo 或类似的东西),它本来也不会太大。
这取决于您在一个网站上使用了多少 SVG 文件。此外,Modernizr 还有许多其他用途。如今,我在几乎所有我制作的网站上都会出于某种原因使用它。
此外,Modernizr 可能包含在您的全局 js 中,并且是单次/缓存成本,而每个 SVG 都是其自身的双重下载成本。不过,您的观点是合理的。像所有事物一样,您需要权衡哪种方法最适合您。
在 HTML 中内联 SVG 不是支持 SVG 格式的问题,而是支持 HTML5 解析规则的问题。在 HTML5 解析器之前,HTML 浏览器无法识别作为 text/html 提供的标记中的 SVG 命名空间中的元素。Firefox 3.6- 和其他尚未实现 HTML5 解析器的浏览器表现与 iOS5- 浏览器类似。但是,它们都支持 application/xhtml+xml 的内联 SVG。
但我认为目前 iOS5- 不会造成太大问题,因为它的份额小于 8%(http://www.14oranges.com/2013/08/),并且根据我的经验,对于这种旧的非视网膜设备,PNG 似乎是一个更好的选择。
这是一篇很好的文章,探讨了这种技术。我一直在关注 Twitter 上的讨论,但很高兴看到所有这些内容都被记录下来。
为了防止在 IE9+ 中下载 png,您是否可以在 svg 中的图像元素周围包装一个限制在 IE8 及以下版本的条件注释?我看到的问题是非 IE 浏览器不支持 SVG,因此它们将看不到 png 回退,因此您需要在条件注释中使用 OR 运算符来考虑 Android 2.3 及以下版本的原生浏览器。
我想是这样的
我没有设备可以在旧版 IE 或旧版 Android 中测试它是否有效,但我相信这应该可以防止您提到的双重下载问题。
我有一个 示例 展示了这个想法的实际应用。我非常接近一个不错的解决方案,但可惜的是,它仍然存在问题。
我能够防止在 IE9+ 中出现双重下载问题。
我在 IE 10、IE 9、IE 8 和 IE 7 中进行了测试。
我发现使用条件注释确实可以防止 IE9+ 中的双重下载,并且在 IE8 和 IE 7 中可以正常显示 .png。
在 IE8/IE7 中,当我使用之前发布的代码片段时,它在 png 旁边呈现了一个空图像图标,用于没有 svg src 属性的图像。因此,我在 svg 中添加了一些内联样式,这似乎可以解决问题,隐藏了在 IE8/IE7 中作为空图像图标呈现的 svg 图像。
以下是代码
缺点
这导致 .png 在旧版 Android 上无法显示。我只在 2.3 版本中进行了测试,但我敢打赌旧版本也是一样的。取而代之的是,呈现一个带有灰色边框的空框。
我将进一步研究这个问题,看看是否可以在解决 Android 问题的同时,仍然防止 IE9+ 中的双重下载。
不过,我认为如果我们有一个原生 HTML 元素可以 允许我们在浏览器不支持我们决定使用的主要图像类型(无论是 .svg、.webp、新的响应式图像格式还是任何其他格式)时提供回退图像,而不会出现双重下载,那将非常有益。
Chris,恐怕在
<img>
和background-image
中使用 Modernizr 来处理 SVG 并不像你建议的那样简单。这都回到你最初对浏览器在不同上下文中(<img>
、背景、内联 等)对 SVG 的支持不同的困惑。Modernizr.svg
仅检测对<embed>
或<object>
中的 SVG 的支持Modernizr.svgasimg
是你在<img>
中使用 SVG 所需的——尽管它要到 v3.0 才会包含(如果你现在想使用它,代码 在这里),而且它很棘手,因为它是非同步的——更多细节如下我们不知道如何检测背景图像中的 SVG,但正如该线程所说,如果你小心的话,可以使用
Modernizr.svgasimg
Modernizr.inlinesvg
是你使用内联 SVG 所需的(尽管你在示例中设法避免了对 Modernizr 的需求——好消息)异步
我说
Modernizr.svgasimg
是异步的……这意味着页面将在测试完成之前开始呈现,因此你的示例将请求两个图像:.my-element {}
将匹配,然后不久之后.no-svgasimg .my-element {}
将匹配。我的 WebP 教程 解释了避免这种情况的方法(
Modernizr.webp
也是一个异步检测,并且具有非常相似的用例)。<img>
的情况很尴尬,最好使用Modernizr.on()
处理——阅读文章以了解这一点——但这里有一个用于背景图像的涵盖所有情况模式(如果你作弊并使用Modernizr.svgasimg
)所以事实证明互联网很难。谁会想到呢?
这里有一些不错的想法,确实。喜欢这个事实。虽然,我希望将来会有更好的选择可用。
关于IE及其预加载扫描程序识别
<image src>
的问题,也许有人可以测试一些疯狂的解析器技巧,看看它们是否能欺骗扫描程序?例如,<image/src=foo xlink:href=foo>
在内联HTML中正确解析。不过,我现在没有必要的操作系统来测试这一点。
是啊,我也没有……
但我认为看看其他人是否对此发表意见会很有趣……
~ 史蒂夫
在IE9和IE10中测试了这一点。这是我使用的代码。http://codepen.io/bjankord/pen/xlgfC
在IE10中,png的图像中止了,与Chris的测试相同。在IE9中,png没有中止,并且已下载。似乎这比Chris的演示效果差 :(
你们大家的工作都棒极了。
这是打字错误吗?我猜它应该是JS,而不是此方法所需的HTML?
我的意思是它需要每个图像上的HTML中的
onerror
位。如果您使用的是注入图像的CMS,则可以更改执行此操作的代码。但例如在CSS-Tricks上,有数千篇包含图像的帖子,这些帖子中不存在该内容(它们不是SVG,但您知道我的意思),因此返回并更新它们以包含它是不切实际的,并且需要使用另一种方法(非常简单)。如前所述,我使用的
switch
和foreignObject
技术同时下载SVG和回退,但我认为只要SVG和回退都经过优化(理想情况下,回退的情况下为base64编码),性能损失并不算太糟糕。我一直对关于这种技术的文章不多感到惊讶,因为对于较小的图像来说,缺点并不那么严重。对于我的网站上的徽标,整个内容为5.55kb,其中base64编码的PNG为2.33kb(我首先通过ImageAlpha然后通过ImageOptim来减少文件大小)。我节省了两个HTTP请求,获得了相当不错的浏览器覆盖率,并且仅以移动浏览器解码base64的性能为代价(尽管陪审团似乎仍然对这个问题存在争议)以及图像缓存的缺乏(或更短的长度)。如果图像很小,我认为将它们都放在一起是有意义的,尽管存在缺点。
也就是说,我对这种
image
别名技术非常感兴趣。感谢Chris始终如一地对所有这些问题采取务实的态度。需要考虑很多因素,您已经很好地解释了所有这些。非常棒的总结。
对于使用
<img>
标签表示SVG,我有一个onerror
方法的略微扩展版本,使用条件注释,以便旧版IE无需任何JavaScript干预或额外的HTTP请求即可获取回退PNGhttp://davidgoss.co.uk/2013/01/30/use-svg-for-your-logo-and-still-support-old-ie-and-android/
我们昨天进行了一些设备测试,并在以下位置记录了结果:http://waterpigs.co.uk/notes/4RdDTh/
tl;dr:除了请求两个文件的IE 10之外,我们的大多数设备都只请求了SVG,旧版iOS移动版Safari和IE8请求了PNG。最令人惊讶的结果是Kindle在支持SVG时请求了PNG。
此外,SVG <image>无法使用百分比进行大小调整这一事实很奇怪且令人恼火。有人知道解决方法吗?
关于大小调整,此Stack Exchange帖子提出了一种关于百分比的解决方案
http://stackoverflow.com/questions/16813829/responsive-inline-svg-content-of-svg-must-fill-parent-width
我自己还没有尝试过,但我正在研究将那里的建议与这里提供的建议结合起来。我认为在未来一年(几年)中,随着设备的发展,我们将看到SVG领域发生重大变化。
这是我提到的解决方案中的一段代码
继续深入DOM,svg元素上的100%高度声明强制svg扩展到过高的包装器。这也是罪魁祸首的另一部分。
我使用的解决方案涉及固有比例。像这样的CSS
如果该线程消失了,他引用了一个示例
http://jsfiddle.net/pcEjd/
Charles显示传输的字节数为零,因为它是缓存它(请参阅304状态)。在我的测试中,整个PNG在有机会中止之前就已经接收到了。
糟糕,我的意思是“缓存命中”。
对于作为背景图像的SVG,我更喜欢您在另一篇SVG文章中使用的技巧。
尽管存在各种响应式图像解决方案及其回退方法以及帖子中讨论的svg,但我们仍然需要在html5规范中引入一个实际的解决方案,以便在未来获得更高的兼容性和可访问性。
我只是使用单个背景定义设置了PNG背景,并使用多个背景定义设置了SVG背景。
旧版IE回退到PNG图像,而现代浏览器使用SVG。
请参阅以下代码示例。
HTML
CSS
这个解决方案非常好,但我发现将链接放在它周围会导致由于嵌套的
<image>
标签而导致一些不必要的空间。如果有人遇到我遇到的同样的问题,我提出了一个希望在大多数情况下都能解决的方案HTML
CSS
我发现使用Scott的“onerror”技术效果很好,但在IE 10中使用“IE8模式”不起作用。它确实在实际的IE8中有效,所以我能忍受它。希望这能节省其他人解决它所需的时间!!
谢谢你们。
我发现从SVG创建所有PNG回退图像可能很耗时,所以我编写了一个小应用程序来加快此过程。任何感兴趣的人都可以在这里尝试测试版:http://svgtoimage.com/ – 我很乐意收到一些反馈。
为什么不让object标签使用它自己的回退机制并跳过Modernizr+CSS步骤?
这是唯一一个对我有用的,尽管我使用的是ie9并将浏览器模式切换到IE8进行测试。非常感谢,现在我不必使用modernizer了。
使用它会导致svg在Firefox Android(25)中缩小,这很可惜,因为对我来说它是最简单的解决方案。
我设法通过这样内联svg部分来解决它
我的意思是图像标签解决方案
解决.png加载中止问题的办法在这里,需要有
您好,
您可以使用HTTP Debugger Pro http://www.httpdebugger.com 分析HTTP流量。
它是一种无代理解决方案,对传输数据没有影响。
谢谢,
Khachatur
从轶事来看,该示例
在我的测试的Android 2.3设备上没有回退到PNG(这些设备可能支持多个背景,但不支持SVG)。
UGHACKH。 令人难过。
你说得对。Android 2.3 确实支持多个背景,但**不支持** SVG(任何方式),所以它失败了。我测试过了。
这种技术确实适用于 IE 6-8,这仍然非常不错,但在 Android 2.3 中出现无图像失败的情况相当糟糕。
Android 最近让我很沮丧。刚刚发现无法检测到正确的 background-clip 支持,这太糟糕了。
结合多个背景图像和线性渐变似乎可以解决这个问题,并保持旧版 Android 的正常运行。
鸣谢:pauginer.tumblr.com/post/36614680636/invisible-gradient-technique
我更希望使用 JS 刮取 DOM 并将 .svg 替换为 .png,但在 Rails 应用程序中,生产环境中的缓存会导致此操作失败。
例如
= image_tag "some_image.svg"
在 DOM 中(生产环境),对于支持它的浏览器,您会看到类似
/assets/some_image-e262ae2535d6490521680316bbe7676e.svg
的内容。但是对于不支持它的浏览器,JS 会完全替换它,但基本上将 .svg 替换为 .png,例如/assets/some_image-e262ae2535d6490521680316bbe7676e.png
。不幸的是,这将不起作用。文件
some_image-e262ae2535d6490521680316bbe7676e.png
不存在。您好,我尝试将您的“使用 SVG 作为...”解决方案与您的 SVG sprite 解决方案(https://css-tricks.cn/svg-symbol-good-choice-icons/)结合起来。但它不起作用。您知道为什么 xlink:href=”svg.svg#id-tag” 在 image 标签中不起作用,但在 use 标签中起作用吗?
干杯
Philip
我想我明白了。答案在这里:http://www.w3.org/TR/SVG/struct.html#ImageElement
“与‘use’不同,‘image’元素不能引用 SVG 文件中的元素。”
但是如何将 svg sprites 与 png 备用方案一起使用呢?