SVG: border outline для группы элементов

203
10 апреля 2018, 02:12

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

<g id="group">
  <circle cx="125" cy="125" r="65" fill="orange" />
  <line x1="50" y1="50" x2="200" y2="200" stroke="blue" stroke-width="4" />
</g>

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

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

Я уже пробовал разные решения, но ни одно из них не работает, как ожидалось, например:

  • Фильтр SVG с использованием feColorMatrix и feMorphology (см. Этот пост). Но в этом случае цвет элементов изменяется при применении фильтра.
  • Стилизация с stroke и stroke-width приводит к прямоугольной рамке вокруг группы, что тоже не то, что я хочу.

Какие есть идеи, как получить границу вокруг группы, как показано на картинке?

Answer 1

Трудно получить dashed stroke, показанный на изображении, которое вы предоставили. Но сплошной контур сделать возможно. Вот пример:

<svg width="250" height="250" viewBox="0 0 250 250"> 
  <defs> 
    <filter id="groupborder" filterUnits="userSpaceOnUse" 
            x="0" y="0" width="250" height="250"> 
      <feMorphology operator="dilate" in="SourceAlpha" 
                    radius="8" result="e1" /> 
      <feMorphology operator="dilate" in="SourceAlpha" 
                    radius="10" result="e2" /> 
      <feComposite in="e1" in2="e2" operator="xor" 
                   result="outline"/> 
      <feColorMatrix type="matrix" in="outline" 
                     values="1 0 0 0 0 
                             0 1 0 0 0 
                             0 0 1 0 0 
                             0 0 0 .3 0" result="outline2"/> 
      <feComposite in="outline2" in2="SourceGraphic" 
                   operator="over" result="output"/> 
    </filter> 
  </defs> 
  <g id="group" filter="url(#groupborder)"> 
    <circle cx="125" cy="125" r="65" fill="orange" /> 
    <line x1="50" y1="50" x2="200" y2="200" stroke="blue" stroke-width="4" /> 
  </g> 
</svg>

Ниже, как это работает:

<feMorphology operator="dilate" in="SourceAlpha" radius="8" result="e1" />

Фильтр feMorphology использует dilate (расширенную) операцию для для утолщения графических элементов.

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

<feMorphology operator="dilate" in="SourceAlpha" radius="10" result="e2" />

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

<feComposite in="e1" in2="e2" operator="xor" result="outline"/>

Эти расширенные результаты объединяются с использованием операции XOR, которая оставляет черный контур.

<feColorMatrix type="matrix" in="outline" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 .3 0" result="outline2"/>

Этот фильтр умножает альфа-компонент контура на 0,3, поэтому он выглядит серым, а не сплошным черным.

<feComposite in="outline2" in2="SourceGraphic" operator="over" result="output"/>

Наконец, добавьте этот вывод в исходное изображение.

Источник: SVG: border outline for group of elements @squeamish ossifrage Прим. переводчика

На мой взгляд эта тема интересна, открывает широкие горизонты для творчества и может иметь практическое применение.

  • применение группы фильтров дает возможность получить контур любой сложности без применения патчей и стандартных команд обводки

  • Изменяя параметры фильтра feColorMatrix можно получить практически любой цвет контура

  • применение фильтра feMorphology позволяет изменять ширину обводки и зазор между основной фигурой и обводкой

Пример получения зелёного контура

Для этого выставляем параметры фильтра feColorMatrix

<feColorMatrix type="matrix" in="outline" values="0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0" result="outline2"/>

<svg width="250" height="250" viewBox="0 0 250 250" > 
  <defs> 
    <filter id="groupborder" filterUnits="userSpaceOnUse" 
            x="-20%" y="-20%" width="300" height="300"> 
      <feMorphology operator="dilate" in="SourceAlpha" 
                    radius="12" result="e1" /> 
      <feMorphology operator="dilate" in="SourceAlpha" 
                    radius="4" result="e2" /> 
      <feComposite in="e1" in2="e2" operator="xor" 
                   result="outline"/> 
      <feColorMatrix type="matrix" in="outline" 
                     values="0 0 0 0 0 
                             0 0 0 1 0 
                             0 0 0 0 0 
                             0 0 0 1 0" result="outline2"/> 
      <feComposite in="outline2" in2="SourceGraphic" 
                   operator="over" result="output"/> 
    </filter> 
  </defs> 
  <g id="group" filter="url(#groupborder)"> 
    <circle cx="125" cy="125" r="65" fill="orange" /> 
    <line x1="50" y1="50" x2="200" y2="200" stroke="blue" stroke-width="4" /> 
  </g> 
</svg>

Пример получения красного контура с расширением обводки

Для этого расширяем радиус действия у фильтра

<feMorphology operator="dilate" in="SourceAlpha" radius="18" result="e1" />

<svg width="250" height="250" viewBox="0 0 250 250" > 
  <defs> 
    <filter id="groupborder" filterUnits="userSpaceOnUse" 
            x="-20%" y="-20%" width="300" height="300"> 
      <feMorphology operator="dilate" in="SourceAlpha" 
                    radius="18" result="e1" /> 
      <feMorphology operator="dilate" in="SourceAlpha" 
                    radius="3" result="e2" /> 
      <feComposite in="e1" in2="e2" operator="xor" 
                   result="outline"/> 
      <feColorMatrix type="matrix" in="outline" 
                     values="0 0 0 1 0 
                             0 0 0 0 0 
                             0 0 0 0 0 
                             0 0 0 1 0" result="outline2"/> 
      <feComposite in="outline2" in2="SourceGraphic" 
                   operator="over" result="output"/> 
    </filter> 
  </defs> 
  <g id="group" filter="url(#groupborder)"> 
    <circle cx="125" cy="125" r="65" fill="orange" /> 
    <line x1="50" y1="50" x2="200" y2="200" stroke="blue" stroke-width="4" /> 
  </g> 
</svg>

Пример изменения контура при изменении набора внутренних фигур

Просто добавим в группу элементов вторую линию и посмотрим как автоматически изменится обводка, формируемая фильтрами

<svg width="250" height="250" viewBox="0 0 250 250" > 
  <defs> 
    <filter id="groupborder" filterUnits="userSpaceOnUse" 
            x="-20%" y="-20%" width="300" height="300"> 
      <feMorphology operator="dilate" in="SourceAlpha" 
                    radius="18" result="e1" /> 
      <feMorphology operator="dilate" in="SourceAlpha" 
                    radius="3" result="e2" /> 
      <feComposite in="e1" in2="e2" operator="xor" 
                   result="outline"/> 
      <feColorMatrix type="matrix" in="outline" 
                     values="0 0 0 1 0 
                             0 0 0 0 0 
                             0 0 0 0 0 
                             0 0 0 1 0" result="outline2"/> 
      <feComposite in="outline2" in2="SourceGraphic" 
                   operator="over" result="output"/> 
    </filter> 
  </defs> 
  <g id="group" filter="url(#groupborder)"> 
    <circle cx="125" cy="125" r="65" fill="orange" /> 
    <line x1="50" y1="50" x2="200" y2="200" stroke="blue" stroke-width="4" /> 
	<line x1="50" y1="200" x2="200" y2="50" stroke="blue" stroke-width="4" /> 
  </g> 
</svg>

Answer 2

Примеры анимации контуров сформированных фильтрами

  1. Анимация контура при изменении длины внутренней линии

Команда на изменение длины линии:

<polyline points="125,125 125,125" stroke="crimson" stroke-width="4" >  
    <animate attributeName="points" values="125,125 125,125;125,125 200,50;125,125 200,50;125,125 125,125" dur="7s" begin="0s" fill="freeze" restart="whenNotActive"  repeatCount="indefinite"/> 
</polyline> 

Изменяется только конфигурация линии, а контур автоматически отслеживает эти изменения

<svg width="250" height="250" viewBox="0 0 250 250" > 
  <defs> 
    <filter id="groupborder" filterUnits="userSpaceOnUse" 
            x="-20%" y="-20%" width="300" height="300"> 
      <feMorphology operator="dilate" in="SourceAlpha" 
                    radius="18" result="e1" /> 
      <feMorphology operator="dilate" in="SourceAlpha" 
                    radius="3" result="e2" /> 
      <feComposite in="e1" in2="e2" operator="xor" 
                   result="outline"/> 
      <feColorMatrix type="matrix" in="outline" 
                     values="0 0 0 1 0 
                             0 0 0 0 0 
                             0 0 0 0 0 
                             0 0 0 1 0" result="outline2"/> 
      <feComposite in="outline2" in2="SourceGraphic" 
                   operator="over" result="output"/> 
    </filter> 
  </defs> 
  <g id="group" filter="url(#groupborder)"> 
    <circle cx="125" cy="125" r="65" fill="orange" /> 
    <polyline points="125,125 125,125" stroke="purple" stroke-width="4" > 
	<animate attributeName="points" values="125,125 125,125;125,125 200,200;125,125 200,200;125,125 125,125" dur="7s" begin="0s" fill="freeze" restart="whenNotActive" repeatCount="indefinite" />   
	</polyline> 
  <polyline points="125,125 125,125" stroke="violet" stroke-width="4" >	 
	<animate attributeName="points" values="125,125 125,125;125,125 50,50;125,125 50,50;125,125 125,125" dur="7s" begin="0s" fill="freeze" restart="whenNotActive" repeatCount="indefinite" />  
</polyline> 
 
 <polyline points="125,125 125,125" stroke="dodgerblue" stroke-width="4" > 
	<animate attributeName="points" values="125,125 125,125;125,125 50,200;125,125 50,200;125,125 125,125" dur="7s" begin="0s" fill="freeze" restart="whenNotActive" repeatCount="indefinite" />   
</polyline> 
  <polyline points="125,125 125,125" stroke="crimson" stroke-width="4" >	 
	<animate attributeName="points" values="125,125 125,125;125,125 200,50;125,125 200,50;125,125 125,125" dur="7s" begin="0s" fill="freeze" restart="whenNotActive"  repeatCount="indefinite"/>  
 </polyline> 
	 
	 
  </g> 
</svg>

  1. Анимация изменения ширины строки

Для этого добавляется команда анимации изменения ширины строки от 4px до 90px

<circle cx="125" cy="125" r="65" fill="yellow" stroke="yellowgreen" stroke-width="4" stroke-dasharray="14 6.41" > 
   <animate attributeName="stroke-width" values="4;90;90;4;4;90" dur="7s" begin="click" fill="freeze" restart="whenNotActive">   
</circle>     

Начало анимации - клик по фигуре

<svg width="250" height="250" viewBox="0 0 250 250" > 
  <defs> 
    <filter id="groupborder" filterUnits="userSpaceOnUse" 
            x="-20%" y="-20%" width="300" height="300"> 
      <feMorphology operator="dilate" in="SourceAlpha" 
                    radius="10" result="e1" /> 
      <feMorphology operator="dilate" in="SourceAlpha" 
                    radius="3" result="e2" /> 
      <feComposite in="e1" in2="e2" operator="xor" 
                   result="outline"/> 
      <feColorMatrix type="matrix" in="outline" 
                     values="0 0 0 0 0 
                             0 0 0 0.5 0 
                             0 0 0 0 0 
                             0 0 0 1 0" result="outline2"/> 
      <feComposite in="outline2" in2="SourceGraphic" 
                   operator="over" result="output"/> 
    </filter> 
  </defs> 
    <text x="110" y="100" font-size="18">click</text>  
  <g id="group" filter="url(#groupborder)" fill-opacity="0.8"> 
    <circle cx="125" cy="125" r="65" fill="yellow" stroke="yellowgreen" stroke-width="4" stroke-dasharray="14 6.41" >  
	<animate attributeName="stroke-width" values="4;90;90;4;4;90" dur="7s" begin="click" fill="freeze" restart="whenNotActive" > 
  </circle>	  
 
  </g> 
</svg>

  1. Анимация вращения линии

Добавляются две команды анимации: изменение длины линии и вращение линии:

<polyline points="125,125 125,125" stroke="purple" stroke-width="7" >
    <animate id="poly" attributeName="points" values="125,125 125,125;125,125 50,50" dur="2s" begin="0s" fill="freeze" restart="whenNotActive" repeatCount="1" />   
    <animateTransform id="rotate1" attributeName="transform" type="rotate" values="0 125 125;720 125 125;0 125 125" begin="poly.end-1s" repeatCount="indefinite" dur="12s" />
</polyline>
<circle cx="125" cy="125" r="8" fill="#3196C9" stroke="purple" stroke-width="2" />  

<svg width="250" height="250" viewBox="0 0 250 250" > 
  <defs> 
    <filter id="groupborder" filterUnits="userSpaceOnUse" 
            x="-20%" y="-20%" width="300" height="300"> 
      <feMorphology operator="dilate" in="SourceAlpha" 
                    radius="16" result="e1" /> 
      <feMorphology operator="dilate" in="SourceAlpha" 
                    radius="8" result="e2" /> 
      <feComposite in="e1" in2="e2" operator="xor" 
                   result="outline"/> 
      <feColorMatrix type="matrix" in="outline" 
                     values="0 0 0 0 0 
                             0 0 0 0.3 0 
                             0 0 0 1 0 
                             0 0 0 1 0" result="outline2"/> 
      <feComposite in="outline2" in2="SourceGraphic" 
                   operator="over" result="output"/> 
    </filter> 
  </defs> 
  <g id="group" filter="url(#groupborder)"> 
    <circle cx="125" cy="125" r="65" fill="#AAD0A0" stroke="#4A8ECD" stroke-width="2" /> 
	 
    <polyline points="125,125 125,125" stroke="purple" stroke-width="7" > 
	<animate id="poly" attributeName="points" values="125,125 125,125;125,125 50,50" dur="2s" begin="0s" fill="freeze" restart="whenNotActive" repeatCount="1" />    
	<animateTransform id="rotate1" attributeName="transform" type="rotate" values="0 125 125;720 125 125;0 125 125" begin="poly.end-1s" repeatCount="indefinite" dur="12s" /> 
	</polyline> 
  <circle cx="125" cy="125" r="8" fill="#3196C9" stroke="purple" stroke-width="2" /> 
	 
  </g> 
</svg>

  1. Анимация stroke-dasharray

    <circle cx="125" cy="125" r="65" fill="yellow" stroke="yellowgreen" stroke-width="8" stroke-dasharray="20.41 0" > 
      <animate attributeName="stroke-dasharray" values="20.41 0;0 20.41;10 10.41;20.41 0" begin="click" dur="3.5s"  repeatCount="indefinite"/>
    </circle> 
    

<svg width="250" height="250" viewBox="0 0 250 250" > 
  <defs> 
    <filter id="groupborder" filterUnits="userSpaceOnUse" 
            x="-20%" y="-20%" width="300" height="300"> 
      <feMorphology operator="dilate" in="SourceAlpha" 
                    radius="6" result="e1" /> 
      <feMorphology operator="dilate" in="SourceAlpha" 
                    radius="3" result="e2" /> 
      <feComposite in="e1" in2="e2" operator="xor" 
                   result="outline"/> 
      <feColorMatrix type="matrix" in="outline" 
                     values="0 0 0 0 0 
                             0 0 0 0.5 0 
                             0 0 0 0 0 
                             0 0 0 1 0" result="outline2"/> 
      <feComposite in="outline2" in2="SourceGraphic" 
                   operator="over" result="output"/> 
    </filter> 
  </defs> 
   <text x="110" y="90" font-size="20">click</text>  
  <g id="group" filter="url(#groupborder)" fill-opacity="0.7"> 
    <circle cx="125" cy="125" r="65" fill="yellow" stroke="yellowgreen" stroke-width="8" stroke-dasharray="20.41 0" >  
	<animate attributeName="stroke-dasharray" values="20.41 0;0 20.41;10 10.41;20.41 0" begin="click" dur="3.5s"  repeatCount="indefinite"/> 
  </circle>	 
   
  </g> 
</svg>

  1. Анимация stroke-width, stroke-dasharray

    <circle cx="125" cy="125" r="65" fill="yellow" stroke="yellowgreen" stroke-width="74" stroke-dasharray="1 19.41" > 
    <animate attributeName="stroke-dasharray" values="0 20.41;16 4.41;16 4.41; 0 20.41" begin="click" dur="3.5s"  repeatCount="indefinite"/>
    

<svg width="250" height="250" viewBox="0 0 250 250" > 
  <defs> 
    <filter id="groupborder" filterUnits="userSpaceOnUse" 
            x="-20%" y="-20%" width="300" height="300"> 
      <feMorphology operator="dilate" in="SourceAlpha" 
                    radius="6" result="e1" /> 
      <feMorphology operator="dilate" in="SourceAlpha" 
                    radius="3" result="e2" /> 
      <feComposite in="e1" in2="e2" operator="xor" 
                   result="outline"/> 
      <feColorMatrix type="matrix" in="outline" 
                     values="0 0 0 0 0 
                             0 0 0 0.5 0 
                             0 0 0 0 0 
                             0 0 0 1 0" result="outline2"/> 
      <feComposite in="outline2" in2="SourceGraphic" 
                   operator="over" result="output"/> 
    </filter> 
  </defs> 
   <text x="110" y="90" font-size="20">click</text>  
  <g id="group" filter="url(#groupborder)" fill-opacity="0.7"> 
    <circle cx="125" cy="125" r="65" fill="yellow" stroke="yellowgreen" stroke-width="74" stroke-dasharray="1 19.41" >  
	<animate attributeName="stroke-dasharray" values="0 20.41;16 4.41;16 4.41; 0 20.41" begin="click" dur="3.5s"  repeatCount="indefinite"/> 
  </circle>	 
   
  </g> 
</svg>

READ ALSO
Сделать яйцо на Пасху на HTML

Сделать яйцо на Пасху на HTML

Задача – сделать пасхальное яйцо используя любые WEB технологии

236
Убрать .html во всех файлах статичного сайта

Убрать .html во всех файлах статичного сайта

Как убрать расширениеhtml в адресной строке браузера

239
macOS San Francisco, windows Segoe UI?

macOS San Francisco, windows Segoe UI?

На маке и под виндой, нужно отображение контента шрифтом по умолчанию; то бишь в macOS San Francisco, в windows Segoe UI

207
Геометрическая фигура на HTML/CSS

Геометрическая фигура на HTML/CSS

Как сделать на HTML/CSS такую геометрическую фигуру?

212