Странности обводки в SVG
Экспериментируя с SVG можно обнаружить много странных моментов. Получить странное при манипуляциях с масштабированием ещё как-то ожидаемо, но внезапно сюрпризы преподнесло свойство stroke-width
.
stroke-width
задает тощину обводки для фигуры. Если в HTML рамка отрисовывается от внешнего края внутрь фигуры и увеличивает её размеры (что можно исправить с помощью box-sizing
), то в SVG обводка ведет себя иначе: во-первых, она не растягивает фигуру, во-вторых она отрисовывается и внутрь, и наружу относительно внешнего края фигуры.
Чтобы увидеть как это работает, возьмем пару простых фигур:
See the Pen rKpFk by yoksel (@yoksel) on CodePen.
Круг с диаметром 60px, квадрат — 60х60px.
С помощью CSS добавим фигурам рамку:
Рамка зеленого цвета, толщиной 30 пикселей. Чтобы видеть как она накладывается на фигуру, рамке задана полупрозрачность (stroke-opacity: .5
).
Результат:
See the Pen omKIc by yoksel (@yoksel) on CodePen.
Очевидно, что рамка отрисовывается от внешнего края фигуры в обоих направлениях: и внутрь, и наружу.
Обычная рамка ведет себя довольно понятно и предсказуемо, но самое интересное начинается, если задать рамку, превышающую ширину фигуры больше чем в два раза. Чем больше разница — тем интересней результат. Чтобы было хорошо видно, я задала рамку равную трехкратному значению ширины (60 * 3 = 180):
See the Pen AkaKc by yoksel (@yoksel) on CodePen.
У квадрата с рамкой всё в порядке, а вот с кружком происходит нечто странное: если толщина обводки больше чем в два-три раза превышает ширину фигуры, между кружком и рамкой образуются белые поля, а толщина рамки уменьшается.
Чем больше разница между значениями, тем больше белые поля и тем тоньше рамка (радиус 10, толщина обводки — 200):
See the Pen SVG: r=10 + stroke-width=150 by yoksel (@yoksel) on CodePen.
Совсем не то, что хотелось бы получить.
Ещё более интересный результат получается, если добавить свойство stroke-dasharray
.
Код рамки:
Результат:
See the Pen vDpqg by yoksel (@yoksel) on CodePen.
Выглядит так, как будто рамка доходит до середины фигуры и продолжает отрисовываться оттуда.
Всё это как бы намекает нам, что толщина рамки не должна быть больше двухкратного значения ширины или диаметра фигуры, хотя можно поступить и наоборот: задавать очень большие значения и получать интересные варианты лучей.
Думаю, редко кому придет в голову задавать обводке запредельные значения ширины, но я обнаружила эту странность в поведении, когда хотела сделать именно это: взять маленький кружок и задать ему большую рамку, чтобы увеличить таким образом его размер.
Зачем это может быть нужно?
Предположим, есть круглая маска и картинка под ней:
See the Pen mpvhb by yoksel (@yoksel) on CodePen.
Я хочу анимировать маску, чтобы она пропорционально растягивалась и сужалась относительно центра картинки. Первым делом в голову, конечно, приходит transform
. Код анимации:
Подключаем анимацию:
See the Pen dKrft by yoksel (@yoksel) on CodePen.
Так как центр трансформаций в SVG находится в левом верхнем углу области отрисовки, фигура движется совсем не так, как хотелось бы. Задаем центр трансформаций, который нужен нам:
И всё вместе:
Результат:
See the Pen fKqAI by yoksel (@yoksel) on CodePen.
В Chrome анимация будет работать как задумано: маска увеличивается от центра и затем уменьшается в обратном направлении. В Firefox она не будет работать вообще: Firefox не анимирует трансформации внутри масок.
Способ с трансформацией не работает. Какие ещё варианты есть? Вот как раз в этом случае можно попробовать анимировать толщину обводки.
Почему это сработает?
Небольшое лирическое отступление: в отличие от clipPath
, который обрезает фигуру по векторному контуру, игнорируя заливки и обводки контура, в работе маски принимает участие всё её графическое содержимое.
Для примера возьмем кружок c обводкой и используем его в mask
и clipPath
:
See the Pen ouLIh by yoksel (@yoksel) on CodePen.
Для фигур внутри clipPath
можно было бы попробовать анимировать transform
, но они тоже не анимируются в Firefox. Таким образом, в нашем распоряжении остаются только маски и манипуляции с обводками. Конец лирического отступления.
Используя обводки, пробую повторить поведение маски, пропорционально увеличивающейся относительно центра. Для этих целей я беру маску с маленьким кружком внутри:
Задаю ей белую заливку и добавляю обводку:
Добавляю анимацию толщины обводки:
Весь итоговый код:
Результат:
See the Pen oebyE by yoksel (@yoksel) on CodePen.
В отличие от способа с transform
, обводка замечательно анимируется в Firefox, и это можно было бы использовать, если бы не ограничение на толщину обводки, описанное выше.
Таким образом, с одной стороны, задавать толстую рамку для маленького элемента кажется странной идеей, с другой — это можно было бы использовать в практических целях, так что жаль, что stroke-width
отрисовывается не так как хотелось бы.
Некоторой пикантности добавляет тот факт, что как раз в Firefox анимация обводки в маске дает именно желаемый результат: маска увеличивается от центра к краям без каких-либо зазоров между фигурой и обводкой — фигура получается цельная.
Ещё интереснее ведет себя Safari: в нем фигура маски цельная, а результат применения маски к картинке — с зазорами.
Таким образом, при желании получить анимированую маску, можно сочетать анимацию transform
для Хрома (и других webkittens) с анимацией stroke-width
для Firefox, например: так:
See the Pen Animated mask for Firefox and webkittens by yoksel (@yoksel) on CodePen.
Способ странный, но это работает.
Правда, обе анимации не будут работать в IE, потому что он вообще не поддерживает анимации в SVG.
Анимируя stroke-width
в маске можно делать разные другие интересные штуки, но это уже тема для отдельного поста.
Версии браузеров на момент написания поста: Chrome 36, Firefox 31, Safari 7, Opera 23, IE 11.
Картинка с пионами отсюда.
- Ссылки по теме:
- Clipping, Masking and Compositing
- Stroke Properties
- CSS и SVG маски
- SVG-фигуры и трансформации
- Метки:
- SVG