Разложение круга SVG на пути

Опубликовано: 2022-03-10
Краткое резюме ↬ Эта статья покажет вам, как превратить круги SVG в контуры, которые вы можете использовать в анимационных и текстовых контурах, а также как превратить контуры в круги. Как только вы поймете, как все это работает, вы сможете добиться некоторых вполне практических эффектов. Давайте копать.

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

С учетом сказанного, я хотел бы более подробно рассмотреть круглые формы в SVG и то, что мы можем с ними делать, когда мы выходим за пределы основного круга. Почему круги? Ну, я люблю круги. Это моя любимая форма.

Во-первых (надеюсь, вы уже видели простой круг в SVG), вот ручка, которая показывает его:

См. «Круг пера» Брайана Расмуссена.

См. «Круг пера» Брайана Расмуссена.

Многое можно сделать с кругом: его можно анимировать, к нему можно применить разные цвета. Тем не менее, есть две очень приятные вещи, которые вы не можете сделать с кругом в SVG 1.1: вы не можете заставить другой графический элемент двигаться вдоль пути круга (используя элемент animateMotion ) и вы не можете формировать текст вдоль пути круга (это будет разрешено только после выпуска SVG 2.0).

Еще после прыжка! Продолжить чтение ниже ↓

Превращение нашего круга в путь

Существует небольшой онлайн-инструмент, который может помочь вам создавать пути из кругов (вы можете попробовать его здесь), но мы собираемся создавать все с нуля, чтобы мы могли узнать, что на самом деле происходит за кулисами.

Чтобы сделать круговой путь, мы собираемся сделать две дуги, то есть полукруги, которые завершают круг на одном пути. Как вы, наверное, заметили в приведенном выше SVG, атрибуты CX , CY и R соответственно определяют, где круг рисуется по осям X и Y, а R определяет радиус круга. CX и CY создают центр круга, поэтому круг рисуется вокруг этой точки.

Копирование этого круга может выглядеть так:

 <path d=" M (CX - R), CY a R,R 0 1,0 (R * 2),0 a R,R 0 1,0 -(R * 2),0 " />

Обратите внимание, что CX совпадает с атрибутом cx окружности; то же самое касается CY и атрибута cy круга, а также R и атрибута r круга. Маленький a используется для определения сегмента эллиптической дуги. Вы можете использовать необязательный Z (или z ), чтобы закрыть путь.

Строчная буква a обозначает начало эллиптической дуги, проведенной относительно текущей позиции, или в нашем конкретном случае:

 <path d=" M 25, 50 a 25,25 0 1,1 50,0 a 25,25 0 1,1 -50,0 " />

Вы можете увидеть волшебство, происходящее в этой ручке:

См. Круг пера с пути Брайана Расмуссена.

См. Круг пера с пути Брайана Расмуссена.

Под дорожкой скрыт круг с красной заливкой. Поигравшись со значениями пути, вы увидите этот круг, пока путь полностью покрывает круг (сам путь представляет собой круг того же размера), и мы будем знать, что делаем все правильно. .

Вы также должны знать, что пока вы рисуете относительные дуги, вам не нужно повторять команду a для каждой дуги, которую вы рисуете. Когда ваши первые 7 входных данных будут выполнены для вашей дуги, вторые 7 входных данных будут использованы для следующей дуги.

Вы можете попробовать это с помощью пера выше, удалив вторую букву a в пути:

 a 25,25 0 1,1 50,0 25,25 0 1,1 -50,0

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

Как работает этот путь

Во-первых, мы переходим к абсолютно позиционированной координате X,Y на изображении. Он там ничего не рисует — он туда просто перемещается. Помните, что для элемента круга CX CY обозначает центр круга; но, как это происходит в эллиптической дуге, истинные CX и CY дуги будут рассчитываться на основе других свойств этой дуги.

Другими словами, если мы хотим, чтобы наш CX был на уровне 50 , а наш радиус был равен 25 , то нам нужно перейти на 50 - 25 (если мы рисуем слева направо, конечно). Это означает, что наша первая дуга нарисована от 25 X, 50 Y , в результате чего наша первая дуга будет 25,25 0 1,0 50,0 .

Давайте разберем, что на самом деле означает значение 25,25 0 1,0 50,0 нашей дуги:

  • 25 : Относительный X-радиус дуги;
  • 25 : относительный радиус Y дуги;
  • 0 1,0 : я не буду говорить о трех средних значениях (вращении, флаге большой дуги и флаге развертки), потому что они не очень важны в контексте текущего примера, если они одинаковы для обеих дуг;
  • 50 : Конечная X-координата (относительная) дуги;
  • 0 : Конечная координата Y (относительная) дуги.

Вторая дуга 25,25 0 1,0 -50,0 . Имейте в виду, что эта дуга начнет рисовать с того места, где остановилась последняя дуга. Конечно, радиусы X и Y одинаковы ( 25 ), но конечная координата X равна -50 относительно текущей.

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

Чтобы показать вам, что мы можем нарисовать круг разными способами, я приготовил маленькую ручку с различными примерами:

См. «Все круги ручки» Брайана Расмуссена.

См. «Все круги ручки» Брайана Расмуссена.

Если вы присмотритесь, вы увидите наш исходный круг вместе с пятью различными примерами того, как рисовать пути поверх этого круга. У каждого пути есть дочерний элемент desc , описывающий использование значений CX , CY и R для построения круга. Первый пример — это тот, который мы обсуждали здесь, в то время как в трех других используются варианты, которые должны быть понятны при чтении кода; в последних примерах используются четыре полукруглые дуги вместо двух, что несколько повторяет процесс, описанный в спецификации SVG 2, ссылка на которую приведена выше.

Круги накладываются друг на друга с использованием естественной z-индексации SVG, когда элементы, которые появляются позже в разметке, размещаются поверх тех, которые появляются раньше.

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

Каждый круг имеет свой цвет заливки; фактический элемент круга желтый и будет сообщать консоли «Вы нажали на круг» всякий раз, когда на него нажимают. Вы также можете, конечно, просто прочитать код, так как элементы desc довольно просты.

Переход от пути к кругу

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

Рекомендуемое чтение : « Советы по созданию и экспорту лучших SVG для Интернета», Сара Суэйдан.

В качестве примера возьмем следующий SVG из Википедии. Когда вы посмотрите на код этого файла, вы увидите, что в нем много бесполезного редактора, как только вы пропустите его через SVGOMG Джейка Арчибальда! (о котором вы можете узнать больше здесь), вы получите что-то вроде следующего файла, который был довольно оптимизирован, но круги в документе по-прежнему отображаются как пути:

См. Ручку Википедии «Сцепление с винтовой головкой типа A» Брайана Расмуссена.

См. Ручку Википедии «Сцепление с винтовой головкой типа A» Брайана Расмуссена.

Итак, давайте посмотрим, сможем ли мы выяснить, какими должны быть эти круги, если бы они были настоящими элементами круга, учитывая то, что мы знаем о том, как работают пути. Первый путь в документе, очевидно, не является кругом, в то время как следующие два (показаны только атрибут d ):

 M39 20a19 19 0 1 1-38 0 19 19 0 1 1 38 0z
 M25 20a5 5 0 1 1-10 0 5 5 0 1 1 10 0z

Итак, помня, что второе a можно опустить, давайте перепишем их, чтобы придать им немного больше смысла. (Первый путь — большой круг.)

 M39 20 a19 19 0 1 1-38 0 a19 19 0 1 1 38 0z

Очевидно, что эти дуги следующие:

 aR R 0 1 1 - (R * 2) 0 aR R 0 1 1 (R * 2) 0

Это означает, что наш радиус круга равен 19 , но каковы наши значения CX и CY ? Я думаю, что наш M39 на самом деле CX + R , что означает, что CX равен 20 и CY тоже 20 .

Допустим, вы добавляете в круг после всех путей вот так:

 <circle fill="none" stroke-width="1.99975" stroke="red" r="19" cx="20" cy="20" />

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

 M25 20 a5 5 0 1 1-10 0 5 5 0 1 1 10 0z

Очевидно, радиус равен 5 , и я уверен, что наши значения CX и CY такие же, как и раньше: - 20 .

Примечание : если CX = 20 , то CX + R = 25 . Круг находится внутри большего круга в центре, поэтому, очевидно, он должен иметь одинаковые значения CX и CY .

Добавьте следующий круг в конце путей:

 <circle fill="yellow" r="5" cx="20" cy="20" />

Теперь вы можете убедиться, что это правильно, взглянув на следующее перо:

См. Винтовую муфту Pen Wikipedia Type A_ с примерами кругов Брайана Расмуссена.

См. Винтовую муфту Pen Wikipedia Type A_ с примерами кругов Брайана Расмуссена.

Теперь, когда мы знаем, какими должны быть круги, мы можем удалить эти ненужные пути и фактически создать круги — как вы можете видеть здесь:

См. Винтовую муфту Pen Wikipedia типа A, оптимизированную Брайаном Расмуссеном.

См. Винтовую муфту Pen Wikipedia типа A, оптимизированную Брайаном Расмуссеном.

Использование нашего кругового пути для переноса текста

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

См. все круги, обернутые ручкой. Текст Брайана Расмуссена.

См. все круги, обернутые ручкой. Текст Брайана Расмуссена.

Глядя на разные пути, вы увидите крошечные различия между каждым из них (подробнее об этом чуть позже), но сначала можно увидеть небольшую кросс-браузерную несовместимость, особенно заметную на первом пути:

Разработчик Firefox
Хром
Microsoft Edge

Причина, по которой начальная буква «S» слова «Smashing» находится под таким забавным углом в решении Firefox, заключается в том, что мы фактически начали рисовать наш путь (из-за используемой нами команды vR). Это более очевидно в версии для Chrome, где вы можете ясно увидеть первый клин в форме пирога нашего круга, который мы нарисовали:

Chrome не следует всем клиньям, поэтому это результат, когда вы меняете текст на «Smashing Magazine».

Причина в том, что в Chrome есть ошибка, связанная с наследованием атрибута textLength объявленного в родительском text элементе. Если вы хотите, чтобы они оба выглядели одинаково, поместите атрибут textLength в элемент textPath , а также в текст. Почему? Потому что оказывается, что Firefox Developer имеет ту же ошибку, если атрибут textLength не указан в text элементе (это имело место уже несколько лет).

В Microsoft Edge совершенно другая ошибка; он не может обрабатывать пробелы между Text и дочерним элементом TextPath . После того, как вы удалите пробелы и поместите атрибут textLength в элементы text и textPath , все они будут выглядеть относительно одинаково (с небольшими вариациями из-за различий в шрифтах по умолчанию и т. д.). Итак, три разных бага в трех разных браузерах — вот почему люди часто предпочитают работать с библиотеками!

Следующее перо показывает, как проблемы могут быть устранены:

См. Перо, все круги обернуты Текст с фиксированной длиной текста Брайана Расмуссена.

См. Перо, все круги обернуты Текст с фиксированной длиной текста Брайана Расмуссена.

Я также удалил различные цвета заливки, чтобы было легче увидеть перенос текста. Удаление цветов заливки означает, что моя небольшая функция, позволяющая вам циклически перемещаться по путям и смотреть, как они выглядят, не будет работать, если я не добавлю атрибут pointer-events="all" , поэтому я добавил и его.

Примечание . Подробнее о причинах этого можно прочитать в разделе «Управление взаимодействием SVG с помощью свойства событий указателя», объясненном Тиффани Б. Браун.

Мы уже обсудили перенос многодугового пути, теперь давайте посмотрим на другие. Поскольку у нас есть один путь, по которому мы оборачиваемся, текст всегда будет двигаться в одном и том же направлении.

Изображение Дорожка Объяснение
M CX, CY
a R, R 0 1,0 -(R * 2), 0
a R, R 0 1,0 R * 2, 0
и использует функцию translate для перемещения +R по оси X.
Начальная позиция для нашего textPath (поскольку мы ее никак не указали) определяется нашей первой конечной дугой -(R * 2) с учетом радиуса, который имеет сама дуга.
M (CX + R), CY
a R,R 0 1,0 -(R * 2),0
a R,R 0 1,0 (R * 2),0
То же, что и предыдущий путь.
M CX CY
m -R, 0
a R,R 0 1,0 (R * 2),0
a R,R 0 1,0 -(R * 2),0
Поскольку мы заканчиваем в (R * 2 ) в нашей первой дуге, мы, очевидно, начнем с противоположной позиции. Другими словами, этот начинается там, где закончились два наших предыдущих пути.
M (CX - R), CY
a R,R 0 1, 1 (R * 2),0
a R,R 0 1, 1 -(R * 2),0
Это начинается с той же позиции, что и предыдущая из-за (R * 2) , но выполняется по часовой стрелке, потому что мы установили для свойства флага развертки (отмечено желтым) значение 1 .

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

Разбивая наши пути на части

Есть много вещей, которые вы можете делать с текстом на вашем пути, например, добиваться стилистических эффектов с помощью элементов tspan , устанавливать смещение текста или анимировать текст. По сути, все, что вы делаете, будет ограничено самим путем. Но, разбив наши многодуговые пути на отдельные дуговые пути, мы можем поиграть с направлением нашего текста, z-индексацией различных частей нашего текста и добиться более сложных анимаций.

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

Предположим, что наш круг CX 295, CY 200, R 175 . Теперь, следуя методу Circular path, мы видим следующее:

 M (CX - R), CY a R,R 0 1,1 (R * 2),0 a R,R 0 1,1 -(R * 2),0 

См. Pen SVG Amethyst Брайана Расмуссена.

См. Pen SVG Amethyst Брайана Расмуссена.

Я не буду говорить о контуре или размере текста, цвете заливки или обводки. Мы все должны понять это к настоящему времени и быть в состоянии сделать так, как мы хотим. Но, взглянув на текст, мы сразу видим некоторые недостатки или ограничения:

  • Весь текст работает в одном направлении;
  • Было бы неплохо, если бы часть текста располагалась за аметистом, особенно там, где написано ЖУРНАЛ. Чтобы буквы «М» и «Е» выровнялись по кругу, буква «А» должна быть на боковой нижней точке аметиста, что по-другому кажется неуравновешенным. (Я чувствую, что буква «А» должна быть точно расположена и направлена ​​вниз в этой точке.)

Если мы хотим исправить эти проблемы, нам нужно разделить наш единственный путь на два. В следующем ручке я разделил путь на два пути (и поместил их в область textPath defs для ссылки):

См. Pen SVG Amethyst two paths Брайана Расмуссена.

См. Pen SVG Amethyst two paths Брайана Расмуссена.

Опять же, если наш CX равен 295, CY 200, R 175 , то два пути имеют следующий формат (для верхнего полукруглого пути):

 M (CX - R), CY a R,R 0 1,1 (R * 2),0

И нижеследующее:

 M (CX + R), CY a R,R 0 1,1 -(R * 2),0

Однако у нас все еще есть круговой текст, который движется в одном направлении. Чтобы исправить это для всего, кроме Edge, все, что вам нужно сделать, это добавить атрибут side="right" к text элементу, который содержит textPath 'MAGAZINE'.

Как заставить текст двигаться в другом направлении

Если мы хотим поддерживать как можно больше браузеров, мы должны изменить путь и не полагаться на атрибут side , который не полностью поддерживается. Что мы можем сделать, так это скопировать наш верхний полукруг, но изменить развертку с 1 на 0 :

До:

 M 120, 200 a 175,175 0 1, M 120, 200 a 175,175 0 1, 1 350,0 350,0

После:

 M 120, 200 a 175,175 0 1, M 120, 200 a 175,175 0 1, 0 350,0 350,0

Но теперь наш текст отрисовывается по внутреннему кругу, определяемому разверткой, и в разных браузерах он будет выглядеть не так красиво. Это означает, что нам придется переместить положение нашего контура, чтобы выровнять его с буквой «S» слова «Smashing», увеличить окончание пути X и установить некоторое смещение к тексту. Как видите, между Firefox и другими версиями текста также есть небольшая разница, которую мы можем улучшить, увеличив атрибут textLength для text элемента, а также удалив пробелы из textPath (поскольку Firefox, очевидно, считает пробелы значимыми).

Решение:

См. Pen SVG Amethyst, два пути, исправленные Брайаном Расмуссеном.

См. Pen SVG Amethyst, два пути, исправленные Брайаном Расмуссеном.

Измените Z-индекс части нашего кругового текста

Наконец, мы хотим, чтобы наш текст располагался как спереди, так и сзади аметиста. Ну, это легко. Помните, что z-индексация элементов SVG основана на том, где они находятся в разметке? Итак, если у нас есть два элемента, элемент 1 будет отрисован за элементом 2 . Затем все, что нам нужно сделать, это переместить text элемент вверх в нашей разметке SVG, чтобы он был нарисован перед аметистом.

Вы можете увидеть результат ниже, в котором части слова «ЖУРНАЛ» скрыты нижней точкой аметиста.

См. z-индекс двух путей Pen SVG Amethyst Брайана Расмуссена.

См. z-индекс двух путей Pen SVG Amethyst Брайана Расмуссена.

Если вы посмотрите на разметку, вы увидите, что нижний полукруг текста был перемещен перед путем, по которому рисуется аметист.

Анимация частей нашего круга

Итак, теперь у нас есть возможность сделать круговой текст, полностью контролируя направленность частей нашего текста, поместив текст в два полукруга. Это, конечно, также можно использовать для создания анимации текста. Создание кроссбраузерной SVG-анимации — тема отдельной статьи (или многих других статей). Эти примеры будут работать только в Chrome и Firefox из-за использования синтаксиса анимации SMIL вместо ключевых кадров CSS или таких инструментов, как Greensock. Но это дает хороший показатель эффектов, которых можно добиться, анимируя разложенный круг.

Возьмите следующую ручку:

См. Pen SVG Amethyst два пути, анимированные Брайаном Расмуссеном.

См. Pen SVG Amethyst два пути, анимированные Брайаном Расмуссеном.

Пожалуйста, нажмите кнопку «Повторить» на кодовой ручке, чтобы увидеть анимацию в действии. Две части нашего кругового текста начинают анимироваться одновременно, но имеют разную продолжительность, поэтому заканчиваются в разное время. Поскольку мы анимируем атрибут textLength , мы поместили две директивы animate под каждым текстом — одну для text элемента (чтобы работал Firefox) и одну для элемента textpath (чтобы работал Chrome).

Заключение

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

Дальнейшее чтение на SmashingMag:

  • Переосмысление адаптивного SVG
  • Анимация файлов SVG с помощью SVGator
  • Стилизация и анимация SVG с помощью CSS
  • Управление взаимодействием SVG с помощью свойства Pointer Events