# Редактируемая система SVG-иконок
# Простой пример
Есть множество способов создания системы SVG-иконок (SVG Icon System), но один из способов, который использует возможности Vue — создание редактируемых встроенных иконок в виде компонентов. Некоторые преимущества подобного подхода:
- Их легко редактировать «на лету»
- Они анимируются
- Можно использовать обычные входные параметры, со значениями по умолчанию, для сохранения стандартного размера или изменения по необходимости
- Они встраиваемые, поэтому HTTP-запросы не требуются
- Они могут быть доступны динамически
Создадим сначала каталог для всех иконок и назовём каждую в едином стиле, чтобы облегчить их поиск:
components/icons/IconBox.vue
components/icons/IconCalendar.vue
components/icons/IconEnvelope.vue
Вот репозиторий с примером для начала работы, где можно увидеть готовую настройку: https://github.com/sdras/vue-sample-svg-icons/ (opens new window)
Теперь создадим компонент базовой иконки (IconBase.vue
), который использует слот:
<template>
<svg xmlns="http://www.w3.org/2000/svg"
:width="width"
:height="height"
viewBox="0 0 18 18"
:aria-labelledby="iconName"
role="presentation"
>
<title
:id="iconName"
lang="en"
>
{{ iconName }} icon
</title>
<g :fill="iconColor">
<slot />
</g>
</svg>
</template>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Можно использовать эту базовую иконку «как есть», но необходимо обновлять viewBox
в зависимости от viewBox
показываемой иконки. В базовом компоненте определяем входными параметрами width
, height
, iconColor
и имя иконки, чтобы динамически их обновлять. Имя используем для содержимого <title>
и для id
для лучшей доступности.
Секция script
станет выглядеть следующим образом: некоторые значения останутся по умолчанию, поэтому иконка будет отрисовываться всегда одинаково, пока не изменим её:
export default {
props: {
iconName: {
type: String,
default: 'box'
},
width: {
type: [Number, String],
default: 18
},
height: {
type: [Number, String],
default: 18
},
iconColor: {
type: String,
default: 'currentColor'
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Заданное по умолчанию свойство currentColor
используется для цвета иконки и заставит её наследовать цвет текста. Но можно передать и другой цвет входным параметром, если захотим.
Компонент можно использовать следующим образом, передавая в слот единственным содержимым IconWrite.vue
, содержащим пути внутри иконок:
<icon-base icon-name="write">
<icon-write />
</icon-base>
2
3
Теперь если потребуется несколько иконок с разными размерами — всё очень просто:
<p>
<!-- можно передавать меньшую `width` и `height` -->
<icon-base
width="12"
height="12"
icon-name="write"
>
<icon-write />
</icon-base>
<!-- или использовать значение по умолчанию, равное 18 -->
<icon-base icon-name="write"><icon-write /></icon-base>
<!-- или сделать её немного больше :) -->
<icon-base
width="30"
height="30"
icon-name="write"
>
<icon-write />
</icon-base>
</p>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Анимируемые иконки
Хранение иконок в компонентах очень удобно если потребуется их анимировать, особенно при взаимодействиях. Встроенные SVG-иконки имеют самую высокую поддержку для какого-либо взаимодействия. Вот простой пример иконки, которая анимируется при клике:
<template>
<svg
@click="startScissors"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 100 100"
width="100"
height="100"
aria-labelledby="scissors"
role="presentation"
>
<title
id="scissors"
lang="en"
>
Анимированная иконка с ножницами
</title>
<path
id="bk"
fill="#fff"
d="M0 0h100v100H0z"
/>
<g ref="leftscissor">
<path d="M..."/>
...
</g>
<g ref="rightscissor">
<path d="M..."/>
...
</g>
</svg>
</template>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import { TweenMax, Sine } from 'gsap'
export default {
methods: {
startScissors() {
this.scissorAnim(this.$refs.rightscissor, 30)
this.scissorAnim(this.$refs.leftscissor, -30)
},
scissorAnim(el, rot) {
TweenMax.to(el, 0.25, {
rotation: rot,
repeat: 3,
yoyo: true,
svgOrigin: '50 45',
ease: Sine.easeInOut
})
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Используем refs
для групп элементов, которые будем перемещать. Также, так как обе стороны ножницы должны перемещаться вместе, создаём функцию, которую повторно используем при обращении к refs
. Использование библиотеки GreenSock помогает разрешить поддержку анимации и проблемы с transform-origin
во всех браузерах.
See the Pen Editable SVG Icon System: Animated icon by Vue (@Vue) on CodePen.
Довольно легко сделано! И легко обновлять «на лету».
Больше анимационных примеров можно посмотреть в репозитории (opens new window)
# Дополнительные замечания
Дизайнеры могут поменять своё мнение, требования к продукту измениться. Сохранение всей логики системы иконок в одном базовом компоненте обеспечит возможность быстрого обновления всех иконок по всему приложению. Даже при использовании загрузчика для иконок, некоторые ситуации могут потребовать пересоздания или редактирования каждой SVG-иконки при глобальных изменениях. Этот метод поможет сэкономить время и уменьшить боль.
# Когда не следует этого делать
Подобная система SVG-иконок действительно полезна, когда есть несколько иконок, которые используются по-разному на всём сайте. Но если дублируете одну и ту же иконку много раз на одной странице (например иконку удаления во всех строках гигантской таблицы), может имеет больше смысла сделать спрайты, скомпилированные в лист, а затем использовать теги <use>
для их загрузки.
# Альтернативные варианты
Другие инструменты для помощи в управлении SVG-иконками включают:
Эти инструменты собирают SVG-иконки на этапе компиляции, что добавляет сложности для редактирования во время выполнения, потому что теги <use>
могут иметь странные баги с кроссбраузерностью при выполнении чего-то сложного. Они также оставляют с двумя вложенными свойствами viewBox
, а значит и двумя системами координат. Это делает реализацию несколько сложнее.