2020-04-19
zelder
2020-04-20
19/04
2020

Создание своего Fontawesome из своих SVG.


Задача.
На входе есть много своих SVG файлов.
Требуемый результат:
Возможность подцеплять эти иконки (из SVG) по аналогии с fontawesome, простой разметкой типа: <i class="fmy fmy-myiconid"></i>, где myiconid на основе имени файла каждого из SVG.
Поддержка браузеров:
Полная поддержка на IE8+.
Частичная поддержка на IE5+ (в конце статьи в разделе "Использование" приведен пример).
Решение:
1. на основе всех SVG на входе создать шрифты (eot, ttf, woff и подобные).
2. создать файлы стилей к этим шрифтам.
Средства:
Gulp
ASP.NET Core MVC (не обязательно, но в этой статье реализовывалось для проекта под Core MVC)

Реализация

Для начала подцепим необходимые библиотеки для gulp, в файле package.json:
{
  "version": "1.0.0",
  "name": "asp.net",
  "private": true,
  "devDependencies": {
    "gulp": "4.0.2",
    "gulp-less": "4.0.1",
    "gulp-clean-css": "4.3.0",
    "gulp-concat-css": "3.1.0",
    "gulp-svg-sprite": "1.5.0",
    "gulp-iconfont": "10.0.3",
    "gulp-rename": "2.0.0",
    "fs": "0.0.2",
    "gulp-string-replace": "1.1.2"
  }
}

Реализация задач в gulpfile.js:
var gulp = require("gulp"),
    fs = require('fs'),
    cleanCss = require("gulp-clean-css"),
    less = require("gulp-less"),
    concatCss = require('gulp-concat-css'),
    svgSprite = require('gulp-svg-sprite'),
    iconfont = require('gulp-iconfont'),
    replace = require('gulp-string-replace'),
    rename = require('gulp-rename');

var OPTS = {
    runTimestamp: Math.round(Date.now() / 1000),
    fonts: {
        zld: {
            buildPath: 'wwwroot/out/fonts/zld', // тут будут лежать шрифты на выходе (ttf, woff etc)
            svgs: ['wwwroot/src/fonts/zld/svgs/*.svg' ], // наши входящие SVG-файлы
            fontName: 'ZelderFont', // название шрифта
            className: 'fmy', // желаемый префикс к классу названий иконок, который будет фигурировать в разметке
            codesCssOutFile: 'wwwroot/src/fonts/zld/dist/codes.css', // в этот файл мы сгенерируем стили для каждой иконки
            fontCssTemplate: 'wwwroot/src/fonts/zld/font.csstmpl', // входящий файл-шаблон для общих стилей шрифта (на основе него будет сгененирован еще один CSS)
            fontRelativeForCssPath: '../../../../out/fonts/zld/', // относительный путь (от CSS) до шрифтов
            distOutPath: 'wwwroot/src/fonts/zld/dist',
            distOutFontCssFile: 'font.css',
        }
    }
}
/****
 * FONT (by SVG)
 * // https://github.com/nfroidure/gulp-iconfont
 * На выходе будут файлы шрифтов + два файла:
 *  dist/font.css
 *  dist/codes.css
 */
gulp.task('font:zld:buildsvg', function (cb) {
    return gulp.src(OPTS.fonts.zld.svgs)
        // собираем шрифты на основе входящих SVGs
        .pipe(
            iconfont({
                fontName: OPTS.fonts.zld.fontName,
                prependUnicode: false,
                formats: ['ttf', 'eot', 'woff', 'woff2', 'svg'],
                timestamp: OPTS.runTimestamp,
                fontHeight: 1002,
                normalize: true
            })
        )
        // по всем символам создаем стили и кладем в CSS-файл
        //- >> можно было бы из коробки через шаблоны: https://github.com/nfroidure/gulp-iconfont#make-your-css
        //- >> но под винду надо с бубном
        .on('glyphs', (glyphs) => {
            var content = "";
            for (var i = 0; i < glyphs.length; i++) {
                var glyph = glyphs[i];
                var codepoint = glyph.unicode[0].codePointAt(0).toString(16).toUpperCase();
                content += '.' + OPTS.fonts.zld.className + '-' + glyph.name + ':before { content: "\ ' + codepoint + '"; }\r\n'; // Синтаксис -> после 'content:' два обратных слэша без пробела
            }
            fs.writeFile(OPTS.fonts.zld.codesCssOutFile, content, function () { });
        })
        .pipe(gulp.dest(OPTS.fonts.zld.buildPath));
});
// заполняем шаблон-CSS с общими настройками шрифта для стилей
//- >> свой велосипед, т.к. из коробки без бубна никак
gulp.task('font:zld:templates', function (cb) {
    var replaceOptions = { logs: { enabled: false } };
    return gulp.src([OPTS.fonts.zld.fontCssTemplate])
        .pipe(replace(new RegExp('%FontName%', 'g'), OPTS.fonts.zld.fontName, replaceOptions))
        .pipe(replace(new RegExp('%FontPath%', 'g'), OPTS.fonts.zld.fontRelativeForCssPath, replaceOptions))
        .pipe(replace(new RegExp('%ClassName%', 'g'), OPTS.fonts.zld.className, replaceOptions))
        .pipe(rename(OPTS.fonts.zld.distOutFontCssFile))
        .pipe(gulp.dest(OPTS.fonts.zld.distOutPath));
});
// полная сборка шрифта
gulp.task('font:zld', gulp.series('font:zld:buildsvg', 'font:zld:templates'));
// default
gulp.task('default', gulp.series('font:zld'));


Шаблон для стилей
Пример файла-шаблона для общих стилей шрифта (font.csstmpl):
Файл можно дополнять как угодно, основные тут первые два блока, особенно @font-face:
@font-face {
    font-family: '%FontName%';
    font-style: normal;
    font-weight: 900;
    font-display: block;
    src: url("%FontPath%%FontName%.eot");
    src: url("%FontPath%%FontName%.eot?#iefix") format("embedded-opentype"), url("%FontPath%%FontName%.woff2") format("woff2"), url("%FontPath%%FontName%.woff") format("woff"), url("%FontPath%%FontName%.ttf") format("truetype"), url("%FontPath%%FontName%.svg#%FontName%") format("svg");
}
.%ClassName% {
    font-family: '%FontName%';
    font-weight: 900;
}
.%ClassName% {
    -moz-osx-font-smoothing: grayscale;
    -webkit-font-smoothing: antialiased;
    display: inline-block;
    font-style: normal;
    font-variant: normal;
    text-rendering: auto;
    line-height: 1;
}

/* дополнительные стили */

.%ClassName%-lg {
    font-size: 1.33333em;
    line-height: 0.75em;
    vertical-align: -.0667em;
}
.%ClassName%-xs { font-size: .75em; }
.%ClassName%-sm {  font-size: .875em; }
.%ClassName%-1x { font-size: 1em; }
.%ClassName%-2x { font-size: 2em; }

/* иконка в зеленом кружке */
.%ClassName%-green-o:before {
    border-radius: 50%;
    border: 2px solid green;
    padding: 6px;
    background-color: green;
    color: white;
}

/* иконка в зеленом кружке, при наведении курсора становится на белом кружке */
.%ClassName%-green-o-h:before {
    border-radius: 50%;
    border: 2px solid green;
    padding: 6px;
    background-color: green;
    color: white;
}
.%ClassName%-green-o-h:hover:before, .%ClassName%-green-o-h:focus:before {
    background-color: white;
    color: green;
}


На выходе получаем
Пять файлов ('ttf', 'eot', 'woff', 'woff2', 'svg') с шрифтами в папке 'wwwroot/out/fonts/zld'.
Также получим еще два файла:
Общий файл стилей для шрифта: wwwroot/src/fonts/zld/dist/font.css
Файл стилей для всех иконок: wwwroot/src/fonts/zld/dist/codes.css
Для наглядности, содержимое файла codes.css (если у нас на входе были SVG файлы: myicon1.svg, brain.svg, hands.svg):
.fmy-myicon1:before { content: "\EA01"; }
.fmy-brain:before { content: "\EA02"; }
.fmy-hands:before { content: "\EA03"; }


Подключение
Где-то в разметке ожидается:
<link rel="stylesheet" href="~/out/font.css" asp-append-version="false" />
<link rel="stylesheet" href="~/out/codes.css" asp-append-version="false" />
или, если минифицировали
<link rel="stylesheet" href="~/out/font.min.css" asp-append-version="true" />
или, даже так
<environment include="Development">
    <link rel="stylesheet" href="~/out/font.css" asp-append-version="false" />
    <link rel="stylesheet" href="~/out/codes.css" asp-append-version="false" />
</environment>
<environment names="Staging,Production">
    <link rel="stylesheet" href="~/out/font.min.css" asp-append-version="true" />
</environment>

Обращаю внимание!
Тут пути указаны в папку out, хотя в gulp мы указывали пути в dist. 

Подразумевается, эти два CSS были переложены и минифицированы удобным способом (либо gulp, либо bundleconfig.json).
В этой статье не показано.


Использование
<i class="fmy fmy-myicon1"></i>
<i class="fmy fmy-hands"></i>

@* в этом примере используется код символа шрифта (из созданного нами ранее файла codes.css, с добавлением к коду \u) *@
@* этот вариант работает и в IE5 *@
<div class="fmy" style="color:#f00;">@Html.Raw("\uEA02")</div>

@* с использованием стилей из шаблона font.csstmpl (ничто не мешает добавить свои стили и как-то иначе) *@
<div class="fmy fmy-hdd fmy-green-o"></div>
<div class="fmy fmy-brain fmy-green-o-h"></div>


Возможные проблемы
Важно учесть относительный путь к шрифтам, указываемый в стилях:
OPTS.fonts.zld.fontRelativeForCssPath



.