angle-uparrow-clockwisearrow-counterclockwisearrow-down-uparrow-leftatcalendarcard-listchatcheckenvelopefolderhouseinfo-circlepencilpeoplepersonperson-fillperson-plusphoneplusquestion-circlesearchtagtrashx

Использование иконок на Вашем сайте Flask и уменьшение 'First Contentful Paint'.

Используйте макрос пиктограмм Jinja для размещения пиктограмм на ваших страницах и используйте <symbol> для однократного определения пиктограмм и многократного использования на одной и той же странице.

29 мая 2020
post main image
https://unsplash.com/@codingtim

Существует множество типов векторных иконок. В этой заметке я смотрю только на иконки SVG, и ограничиваюсь навигационными иконками, иногда называемыми также иконками интерфейса. Эти иконки не только красиво выглядят на сайтах, но и имеют цвет и масштаб, как шрифты. И они очень функциональны. Представьте себе кнопку с текстом ''Правка''. Замените этот текст карандашом, и вы получите больше места на странице, пока еще очень ясно, что произойдет, когда вы нажмете на кнопку.

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

Наверное, самыми известными иконками являются иконка поиска и иконка логина/аккаунта (человека). На экране вашего мобильного телефона не так много места, как на компьютере desktop . Во многих случаях панель меню на телефоне меняется, а горизонтальные пункты меню помещаются в выпадающее меню, которое можно активировать нажатием на иконку-гамбургер. Чтобы функции поиска и входа в систему/аккаунта были всегда доступны, часто окно поиска заменяется иконкой поиска, а тексты входа в систему/аккаунта заменяются иконкой пользователя.

Способы добавления иконок на ваш сайт

Самый простой способ - добавить иконки в виде шрифта. Вы загружаете шрифт (мы не используем CDN из соображений конфиденциальности), добавляете семейство шрифтов и все. Когда я запустил этот сайт, я выбрал Font Awesome. Почему? Я искал в интернете иконки bootstrap, и там было много хитов с упоминанием Font Awesome. Font Awesome действительно имеет очень хороший набор бесплатных иконок. Добавить их на свои страницы очень просто. Например, чтобы добавить иконку user, вы добавляете код:

	<i class="fas fa-user"></i>

Другой способ добавления иконок - встроенный. Вместо добавления кода для выбора иконки из шрифта Font Awesome мы добавляем фактический код SVG-icon. Вы можете скачать код SVG-icon с сайта Font Awesome , код user-icon:

	<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="user" class="svg-inline--fa fa-user  fa-w-14" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M224 256c70.7 0 128-57.3 128-128S294.7 0 224 0 96 57.3 96 128s57.3 128 128 128zm89.6 32h-16.7c-22.2 10.2-46.9 16-72.9 16s-50.6-5.8-72.9-16h-16.7C60.2 288 0 348.2 0 422.4V464c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48v-41.6c0-74.2-60.2-134.4-134.4-134.4z"></path></svg>

Есть и другие способы, но с большинством из них вы теряете способность к раскрашиванию и масштабированию. Хороший обзор представлен в статье 'Использование SVG', смотрите ссылки ниже.

Иконки и время загрузки страницы

При создании сайта одним из наиболее важных аспектов является время, необходимое для появления страницы на экране. Многие поисковые системы сделали время загрузки вашего сайта рейтинговым фактором. Chromium Developer Tools имеет хороший инструмент для измерения этого времени под названием "Аудиты". Это программа Lighthouse , которая дает Вам рейтинги производительности, доступности, передового опыта и SEO. Для производительности 'First Contentful Paint' очень важна.

Lighthouse показал мне, что загрузка и рендеринг файлов Font Awesome занимает почти одну секунду! Если вы заглянете на сайт Font Awesome , то увидите, что в шрифте имеется около 1500 бесплатных иконок. Очень хорошо, но я использую только 25 из них.

Поскольку я хотел сократить время загрузки, я подумал об использовании пользовательского шрифта иконок. Например, Icomoon, смотрите ссылки ниже, позволяет вам создать шрифт пиктограммы только с теми пиктограммами, которые вы используете. Очень хорошо, но я решил использовать встроенные иконки, потому что это исключает загрузку дополнительных файлов. Недостатком является то, что размер страницы увеличивается. Приятное обсуждение аспектов производительности можно найти в статье 'Inline SVG vs Icon Fonts [CAGEMATCH]', смотрите ссылки ниже.

Используйте макрос Jinja для размещения иконок на странице

Думая об иконках, наверное, хорошо иметь в виду, что вы хотите изменить иконки завтра. Или даже хотите поддерживать несколько наборов иконок. В качестве теста я выбрал новые иконки Bootstrap 5 в качестве второго набора иконок. Для поддержки обоих наборов пиктограмм я создал следующий макрос Jinja :

{%- macro icon(icon_name, fill=false, collection=none, inline=false) -%}

	{%- if not collection %}
		{%- set collection = 'fontawesome' -%}
	{%- endif %}

	{%- if collection == 'fontawesome' -%}

		{%- if icon_name in ['angle_up', 'chevron_up'] -%}
				
			{%- if inline -%}
				<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="angle-up" class="svg-inline--fa fa-angle-up fa-w-10" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path fill="currentColor" d="M177 159.7l136 136c9.4 9.4 9.4 24.6 0 33.9l-22.6 22.6c-9.4 9.4-24.6 9.4-33.9 0L160 255.9l-96.4 96.4c-9.4 9.4-24.6 9.4-33.9 0L7 329.7c-9.4-9.4-9.4-24.6 0-33.9l136-136c9.4-9.5 24.6-9.5 34-.1z"></path></svg>
			{%- else -%}
				<i class="fas fa-angle-up"></i>
			{%- endif -%}

		{%- elif icon_name in ['arrow_clockwise', 'redo'] -%}
		...

	{%- endif -%}


	{%- if collection == 'bootstrap5' -%}

		{%- if icon_name in ['angle_up', 'chevron_up'] -%}

			{%- if inline -%}
				<svg class="bi bi-chevron-up" width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7.646 4.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1-.708.708L8 5.707l-5.646 5.647a.5.5 0 0 1-.708-.708l6-6z"/></svg>			{%- else -%}
			{%- else -%}

			{%- endif -%}

		{%- elif icon_name in ['arrow_clockwise', 'redo'] -%}
		...

	{%- endif -%}

{%- endmacro -%}

Я могу использовать данный макрос как с иконками Font Awesome , так и Bootstrap 5. Кроме того, я могу указать, хочу ли я использовать пиктограмму типа fill, и хочу ли я использовать пиктограмму SVG-icon в строке. Модифицируя макрос, я также могу выводить значок Font Awesome и значок Bootstrap 5 для визуального контроля. Использовать этот макрос очень просто, на вашей странице вы можете его использовать:

	{{ icon('home') }} Hoogerheid, NL

Предотвращение идентичных встроенных иконок SVG-icons

Быстрый тест показал мне, что встроенные иконки SVG действительно уменьшили 'First Contentful Paint' почти на секунду. Здорово! Но иногда иконка находилась на странице несколько раз. При среднем размере 400 - 600 символов на иконку SVG-icon это может серьезно повлиять на время загрузки страницы. Примером может служить раздел комментариев, где каждый комментарий имеет иконку календаря и @-иконку, а комментариев - сотню.

К счастью, есть способ определить блок иконок на странице и использовать их позже. Мы делаем это с помощью тегов <svg> и <symbol>:

<svg class="d-none">
	<symbol id="icon-angle-up" viewBox="0 0 16 16">
		<title>angle-up</title>
		<path fill-rule="evenodd" d="M7.646 4.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1-.708.708L8 5.707l-5.646 5.647a.5.5 0 0 1-.708-.708l6-6z"/>
	</symbol>
	<symbol id="icon-arrow-clockwise" viewBox="0 0 16 16">
		<title>arrow-clockwise</title>
		<path fill-rule="evenodd" d="M3.17 6.706a5 5 0 0 1 7.103-3.16.5.5 0 1 0 .454-.892A6 6 0 1 0 13.455 5.5a.5.5 0 0 0-.91.417 5 5 0 1 1-9.375.789z"/><path fill-rule="evenodd" d="M8.147.146a.5.5 0 0 1 .707 0l2.5 2.5a.5.5 0 0 1 0 .708l-2.5 2.5a.5.5 0 1 1-.707-.708L10.293 3 8.147.854a.5.5 0 0 1 0-.708z"/>
	</symbol>
	...
</svg>

Дополнительную информацию в статье 'SVG 'symbol' a Good Choice for Icons' смотрите по ссылкам ниже. Мы поместили этот блок непосредственно под тегом <body>. Обратите внимание, что в определение мы поместили только пути пиктограммы. Заголовок является необязательным. Чтобы использовать иконку, нам достаточно указать ее идентификатор:

	<svg class="svg-icon"><use xlink:href="#icon-calendar"></use>

Мы также можем обернуть его в <пролет> с помощью класса, например, для изменения его размера.

Реализация

Есть две вещи, которые необходимо отредактировать при добавлении новой иконки:

  • набор значков символов
  • макрос иконки

Кроме того, мне нужен HTML-код, который я могу поместить на страницу для быстрого визуального осмотра. Очень скоро я устал от редактирования и решил написать скрипт, который можно использовать для генерации этих данных. Также я решил использовать только иконки Bootstrap 5, они мне больше понравились, они лицензированы на MIT и все имеют одинаковый viewBox. Скрипт был написан очень быстро, так что не стреляйте в меня:

import sys

class  SVGIcon():

    def __init__(self,
        icon_collection=None,
        icon_name=None,
        icon_id=None,
        svg_class=None,
        svg_fill=None,
        svg_view_box=None,
        svg_path=None,
        symbol_title=None
        ):

        self.icon_collection = icon_collection
        self.icon_name = icon_name
        self.icon_id = icon_id
        self.svg_class = svg_class
        self.svg_fill = svg_fill
        self.svg_view_box = svg_view_box
        self.svg_path = svg_path
        self.symbol_title = symbol_title


def doit():
    
    svg_icons = []

    icon_name2icon_details = {
        'angle_up': {
            'refs': ['angle_up', 'chevron_up'],
            'svg_path': '<path fill-rule="evenodd" d="M7.646 4.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1-.708.708L8 5.707l-5.646 5.647a.5.5 0 0 1-.708-.708l6-6z"/>',
        },
        'arrow_clockwise': {
            'refs': ['arrow_clockwise', 'redo', 'resend_mail'],
            'svg_path': '<path fill-rule="evenodd" d="M3.17 6.706a5 5 0 0 1 7.103-3.16.5.5 0 1 0 .454-.892A6 6 0 1 0 13.455 5.5a.5.5 0 0 0-.91.417 5 5 0 1 1-9.375.789z"/><path fill-rule="evenodd" d="M8.147.146a.5.5 0 0 1 .707 0l2.5 2.5a.5.5 0 0 1 0 .708l-2.5 2.5a.5.5 0 1 1-.707-.708L10.293 3 8.147.854a.5.5 0 0 1 0-.708z"/>',
        },
        ...
    }

    icon_collection = 'bootstrap5'
    svg_view_box = "0 0 16 16"
    svg_fill = "currentColor"

    for icon_name in icon_name2icon_details:
        icon_details = icon_name2icon_details[icon_name]
        svg_path = icon_details['svg_path']

        svg_icons.append(SVGIcon(
            icon_collection = icon_collection,
            icon_name = icon_name,
            icon_id = 'icon-'  +  icon_name.replace('_', '-'),
            svg_fill = svg_fill,
            svg_view_box = svg_view_box,
            svg_path = svg_path,
            symbol_title = icon_name.replace('_', '-'),
        ))

    # generate svg symbols list
     symbols_list_lines = []
     symbols_list_lines.append( '<svg class="d-none">' )

    for svg_icon in svg_icons:
         symbols_list_lines.append( "\t"  +  '<symbol id="'  +  svg_icon.icon_id  +  '" viewBox="'  +  svg_icon.svg_view_box  +  '">' )
         symbols_list_lines.append( "\t\t"  +  '<title>'  +  svg_icon.symbol_title  +  '</title>' )
         symbols_list_lines.append( "\t\t"  +  svg_icon.svg_path )
         symbols_list_lines.append( "\t"  +  '</symbol>' )

     symbols_list_lines.append( '</svg>' )
     symbols_list  = "\n".join(symbols_list_lines)

    # generate svg symbols preview
    preview_lines = []
    for svg_icon in svg_icons:
        preview_lines.append( 'abc'  +  ' '  +  '<span class="font-size: 1em;"><svg class="svg-icon">'  +  '<use xlink:href="#'  +  svg_icon.icon_id  +  '"></use></svg>'  +  '</span>'  +  ' '  +  svg_icon.icon_name )

     preview_list  = '<p>'  +  "</p>\n<p>".join(preview_lines)  +  '</p>'

    # generate icon macro code
    icon_macro_lines = []
    first = True        
    for icon_name in icon_name2icon_details:
        icon_details = icon_name2icon_details[icon_name]
        if 'refs' not in icon_details:
            print('refs missing for {}'.format(icon_name))
            sys.exit()
        refs = icon_details['refs']
        # get icon
        found = False
        for svg_icon in svg_icons:
            if svg_icon.icon_name == icon_name:
                found = True
                break
        if not found:
            print('cannot find icon, icon_name = {}'.format(icon_name))
            sys.exit()
                
        if_start = 'elif'
        if first:
            if_start = 'if'
            first = False

        # expand refs
        ref_items = []
        for ref in refs:
            ref_items.append( "'"  +  ref  +  "'" )
            ref_list = '['  +  ', '.join(ref_items)  +  ']'

        icon_macro_lines.append( "\t"  +  '{%- '  +  if_start  +  ' icon_name in '  +  ref_list  +  ' -%}' )
        icon_macro_lines.append( '<span class="font-size: 1em;"><svg class="svg-icon">'  +  '<use xlink:href="#'  +  svg_icon.icon_id  +  '"></use></svg>'  +  '</span>' )

    icon_macro_lines.append( "\t"  +  '{%- endif -%}' )
     icon_macro_list  = "\n".join(icon_macro_lines)

    print('symbols_list:')
    print('{}'.format(symbols_list))
    print('preview_list:')
    print('{}'.format(preview_list))
    print('icon_macro_list:')
    print('{}'.format(icon_macro_list))

После этого я просто копирую - вставляю части symbols_list, preview_list и icon_macro_list в базовый шаблон, файл предварительного просмотра и макрос-файл.

Некоторые проблемы

В моих таблицах списка столбцов, которые можно отсортировать, сортировка была прикреплена к последнему слову имени столбца. Когда столбец становится меньше, я хочу, чтобы иконка была прикреплена к последнему слову. Это больше не работает. Я еще не нашел решения для этого, но не рассматривал его в деталях.

Другое дело - вертикальное расположение иконок. Для этого есть решения, одно из них описано в 'Align SVG Icons to Text and Say Goodbye to Font Icons', смотрите ссылки ниже. После начала использования иконок Bootstrap 5, как описано выше, выяснилось, что позиционирование делать не нужно, но, возможно, вы захотите взглянуть на это.

Резюме

Использование иконок SVG действительно улучшает внешний вид сайта и навигацию. Так как я использую только 25 иконок, я решил использовать метод SVG-символов для включения иконок на страницах, что добавляет дополнительные 11,5 КБ. По сравнению с решением Font Awesome 'First Contentful Paint' упал почти на одну секунду! А использование функции icon() макроса Jinja даёт мне возможность изменять иконки в одном месте вместо того, чтобы обновлять все страницы. В данный момент все иконки загружаются в каждую страницу. В дальнейшем я могу оптимизировать это, включив только те иконки, которые используются на странице.

Ссылки / кредиты

Align SVG Icons to Text and Say Goodbye to Font Icons
https://blog.prototypr.io/align-svg-icons-to-text-and-say-goodbye-to-font-icons-d44b3d7b26b4

Blog Optimization: Replacing Font Awesome with SVG
https://www.wouterbulten.nl/blog/tech/blog-optimization-replacing-font-awesome-with-svg/

Flask | JINJA 2: render_template_string() with macro imported in context
https://stackoverflow.com/questions/61338841/flask-jinja-2-render-template-string-with-macro-imported-in-context

Icomoon
https://icomoon.io

Inline SVG vs Icon Fonts [CAGEMATCH]
https://css-tricks.com/icon-fonts-vs-svg/

Insert image after each list item
https://stackoverflow.com/questions/946403/insert-image-after-each-list-item

Is there a way to use SVG as content in a pseudo element :before or :after
https://stackoverflow.com/questions/19255296/is-there-a-way-to-use-svg-as-content-in-a-pseudo-element-before-or-after

Lighthouse | Tools for Web Developers | Google Developers
https://developers.google.com/web/tools/lighthouse

SVG 'symbol' a Good Choice for Icons
https://css-tricks.com/svg-symbol-good-choice-icons/

Using SVG
https://css-tricks.com/using-svg/

Подробнее

Flask Icons Jinja2

Оставить комментарий

Комментируйте анонимно или войдите в систему, чтобы прокомментировать.

Комментарии

Оставьте ответ

Ответьте анонимно или войдите в систему, чтобы ответить.