Как кэшировать значки SVG на внешнем CDN, избегая FOMI?

Я знаю, как загружать значки SVG на моем сайте, но я не могу понять, как удовлетворить все следующие ограничения:

  • Возможность использования значков SVG в CSS
  • Отсутствие пропущенных значков (FOMI)
  • Минимальный размер начальной страницы
  • Кэшированные SVG
  • Возможность использования CDN
  • Должна иметь возможность использовать fill: currentColor, чтобы значок соответствовал текущему цвету текста, точно так же, как иконы-шрифты
  • Бонус: Пиксель-выравнивание SVG, чтобы они всегда выглядели острыми.

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>

Но мы не можем использовать CDN, пока браузеры не зафиксируют проблему CORS.

Мы можем заплатить в поддержку внешних доменов, но я уверен, что это не сработает для CSS, потому что он только наблюдает за DOM (извините, еще не протестировано), а также заставляет ваш браузер делать целую кучу неудачных запросов к файлу, который он не может получить (по одному для каждого значка на странице).

Мы можем использовать CDN, если вместо этого мы либо встраиваем весь SVG (увеличенный размер страницы, ни кеширование), либо мы используем AJAX (вызывает FOMI).

Итак, существуют ли какие-либо решения, удовлетворяющие всем ограничениям 5 7?

В принципе, я хочу, чтобы SVG были так же удобны, как иконы-шрифты, или нет переключения точек. SVG поддерживают несколько цветов и являются более доступными, но я не могу заставить их выглядеть так же хорошо или загружать как можно эффективнее.

Ответ 1

Ближайшим, что я могу получить, является загрузка SVG в элемент изображения, а затем его использование, как "старомодный" образ спрайт. Это, насколько я могу судить, удовлетворяет всем вашим ограничениям. Единственным недостатком, о котором я могу думать, является то, что вы теряете возможность изменять определенные части SVG с помощью CSS. Это, однако, не одно из ваших ограничений (исправьте меня, если я ошибаюсь), и все еще можно изменить весь значок, как вы можете видеть в моей демонстрации. Я создал скрипт и для полноты также включает фрагмент кода.

Чтобы эмулировать CDN, я создал файл SVG и загрузил его в службу размещения изображений. Приношу свои извинения будущим читателям, если эта услуга теперь отключена. SVG файл просто содержит в себе все значки (я создал черный квадрат, круг и треугольник). Таким образом, разница в SVG-спрайт-картах заключается в том, что значки находятся в самом 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>

Ответ 2

Мое лучшее предположение - использовать данные uris, которые довольно хорошая поддержка браузера. Через что-то вроде Grunticon или их веб-приложение Grumpicon.

Выходной файл - 2 css и 1 js, который должен работать без проблем с вашим CDN.

обработанный вывод очень гибкий и настраиваемый.

Ответ 4

У меня была почти такая же проблема. Это, вероятно, не удовлетворяет требованию FOMI, но это интересный взлом, который выбил меня из колеи. По сути, этот скрипт просто меняет каждый img в DOM, который импортирует SVG со встроенным 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);
    })
};

В противном случае, я столкнулся с этим в Твиттере сегодня https://twitter.com/chriscoyier/status/1124064712067624960, и применение этого CSS к div позволило мне создать цветную иконку svg, которая может храниться в CDN

.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;
}

Хотя поддержка браузера еще не идеальна.

Надеюсь, это поможет кому-то 😄