【问题标题】:How to cache SVG icons on an external CDN while avoiding FOMI?如何在避免 FOMI 的同时在外部 CDN 上缓存 SVG 图标?
【发布时间】:2017-06-29 09:09:24
【问题描述】:

我知道如何在我的网站上加载 SVG 图标,但我不知道如何满足以下所有限制:

  1. 能够在 CSS 中使用 SVG 图标
  2. 没有丢失图标的闪烁 (FOMI)
  3. 最小初始页面大小
  4. 缓存的 SVG
  5. 能够使用 CDN
  6. 必须能够使用fill: currentColor 使图标与当前文本颜色匹配,就像icon-fonts 一样
  7. 奖励:Pixel-align the SVGs 所以它们总是看起来很锋利

1,2,3 和 4 可以通过使用外部精灵图来满足,如下所示:

<svg viewBox="0 0 100 100">
    <use xmlns:xlink="http://www.w3.org/1999/xlink"
         xlink:href="/assets/sprite-4faa5ef477.svg#icon-asterisk-50af6"></use>
</svg>

但在浏览器修复 CORS issue 之前,我们不能使用 CDN。

我们可以patch in 支持外部域,但我很确定这不适用于 CSS,因为它只监视 DOM(抱歉,尚未测试),而且它还会导致您的浏览器对无法获取的文件的一大堆失败请求(页面上的每个图标一个)。

如果我们内联整个 SVG(增加页面大小,无缓存)或 AJAX(导致 FOMI),我们可以使用 CDN。

那么,有没有满足所有5 7 个约束的解决方案?

基本上,我希望 SVG 和图标字体一样方便,否则切换是没有意义的。 SVG 支持多种颜色并且更易于访问,但我无法让它们看起来一样好,或者加载效率不高。

【问题讨论】:

  • 你可以把你的 SVG 变成图标字体fontello.com
  • @HolgerWill 是的。我目前正在使用带有 icomoon 的图标字体,但每个人都在说 SVG 近来风靡一时,我正试图弄清楚它们到底有多好。当然,它们支持多种颜色,但是they don't look as crisp 并且它们不适用于 CDN,所以现在 SVG 看起来有点糟糕。因为FontCustom 似乎已经死了,所以使用 webpack 构建它们会更容易一些。
  • 我猜想 Polymer 所做的事情...把你的图标放在一个 html 文件中,并结合自定义元素使用 html-includes,也许

标签: html css ajax performance svg


【解决方案1】:

我能得到的最接近的方法是在图像元素中加载 SVG,然后像“老式”image sprite 一样使用它。据我所知,这满足了您的所有限制。我能想到的唯一缺点是您无法使用 CSS 修改 SVG 的特定部分。然而,这不是您的限制之一(如果我错了,请纠正我)并且仍然可以修改所有图标,正如您在我的演示中看到的那样。我创建了一个fiddle,为了完整起见还包括一个代码 sn-p。

为了模拟 CDN,我创建了一个 SVG 文件并将其上传到某个图像托管服务。如果这项服务现在停止,我向未来的读者道歉。 SVG 文件中的所有图标都彼此相邻(我现在创建了一个黑色的正方形、圆形和三角形)。因此,与 SVG sprite maps 的区别在于图标位于 SVG 本身中,而不是在 defs 中。将多个 SVG 组合到一个中应该很容易,我还没有寻找可以自动执行此过程的工具。

.icon {
  display: inline-block;
  vertical-align: top;
  width: 30px; /* can be anything */
  height: 30px;
  background-image: url('http://imgh.us/icons_36.svg');
  
  border: 1px solid #000; /* just to see where the icon is */
}

/* sizes */
.icon.large {
  width: 50px;
  height: 50px;
  background-size: 150px auto;
}

/* icons */
.icon.circle { background-position: -30px 0; }
.icon.large.circle { background-position: -50px 0; }
.icon.triangle { background-position: -60px 0; }
.icon.large.triangle { background-position: -100px 0; }

/* styles */
.icon.info {
  /* based on http://stackoverflow.com/a/25524145/962603,
   * but you can of course also use an SVG filter (heh) */
  filter: invert(100%) sepia(100%) saturate(50000%) hue-rotate(90deg) brightness(70%);
}
.icon.highlight {
  /* based on http://stackoverflow.com/a/25524145/962603,
   * but you can of course also use an SVG filter (heh) */
  filter: invert(100%) sepia(100%) saturate(10000%) hue-rotate(30deg) brightness(50%);
}
<span class="icon square"></span>
<span class="icon circle"></span>
<span class="icon triangle"></span>
<span class="icon circle highlight"></span>
<span class="icon triangle large info"></span>

【讨论】:

  • 哇咔咔。我只是假设我能够做到fill: currentColor 等等。这是一个秘密要求,但我没有列出它,所以我会给你赏金,但我会更新这个问题。创造性思维!
  • 你太棒了!非常感谢。我将尝试提出一个解决方案,让您能够做到这一点。我想一种方法是按照我的建议做,然后使用 AJAX 加载所有内容并在加载时替换图标?不过那会很麻烦……
  • 我现在实际上正在做类似的事情,但我不喜欢它,因为在 AJAX 请求完成后图标会改变颜色。我必须做更多的实验;也许如果我立即在async 脚本中启动 AJAX 请求以立即加载 spritemap,那么 FOMI 就不会那么糟糕了。
【解决方案2】:

我最好的猜测是使用data uris,它有pretty great browser support。通过Grunticon 或他们的网络应用程序Grumpicon 之类的。

输出是 2 个css 文件和 1 个js,它们应该与您的 CDN 无缝协作。

rendered output 非常灵活且可自定义。

【讨论】:

  • 数据 URI 不可缓存。在 CSS 中使用可能没问题,但我不希望在初始页面加载 HTML 中重复 200 次 1000 字节数据 URI。
  • 很公平,希望你能想出办法。 :)
【解决方案3】:

我遇到了几乎同样的问题。这可能不满足 FOMI 要求,但这是一个有趣的 hack,让我摆脱了束缚。基本上,这个脚本只是将 DOM 中导入 svg 的每个 img 与内联 SVG 交换,因此您可以根据需要设置样式。

// replaces img tags with svg tags if their source is an svg
// allows SVGs to be manipulated in the DOM directly
// ? returns a Promise, so you can execute tasks AFTER fetching SVGs

let fetchSVGs = () => {

//gets all the SRCs of the SVGs
let parentImgs = Array.from(document.querySelectorAll('img')).map((img) => {
    if(img.src.endsWith('.svg')) {
        return img
    }
});

let promises = [];
parentImgs.forEach((img) => {
    promises.push(
        fetch(img.src).then((response) => {
            // Error handling
            if (response.status !== 200) {
                console.log('Looks like there was a problem. Status Code: ' +
                    response.status);
                return;
            }
            // saves the SVG
            return response.text();
        })
    )
});

// All fetch() calls have been made
return Promise
    .all(promises)
    .then((texts)=> {
        texts.forEach((text, i) => {
            let img = parentImgs[i];

            let div = document.createElement('div');
            div.innerHTML = text;
            img.parentNode.appendChild(div);
            let svg = div.firstChild;
            img.parentNode.appendChild(svg);

            // makes the SVG inherit the class from its parent
            svg.classList = img.className;

            // removes the junk we don't need.
            div.remove();
            img.parentNode.removeChild(img);

        })
    })
    .catch((error) => {
        console.log(error);
    })
};

否则,我今天在 Twitter 上看到了这个 https://twitter.com/chriscoyier/status/1124064712067624960 并将这个 CSS 应用到 div 让我可以制作一个可以存储在 CDN 中的可着色 svg 图标

.icon-mask {
  display: inline-block;
  width: 80px;
  height: 80px;
  background: red;
   -webkit-mask: url(https://cdnjs.cloudflare.com/ajax/libs/simple-icons/3.0.1/codepen.svg);
   -webkit-mask-size: cover;
}

浏览器支持还不完善。

希望这对某人有帮助?

【讨论】:

    【解决方案4】:

    对于缓存,您可以尝试 HTML5 应用缓存 https://www.w3schools.com/html/html5_app_cache.asp

    【讨论】:

    猜你喜欢
    • 2022-01-23
    • 1970-01-01
    • 2014-09-06
    • 2012-08-19
    • 1970-01-01
    • 2016-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多