Изменение цвета svg изображения по маске, загруженного в HTML с помощью <img>

112
05 ноября 2019, 12:10

Пытаюсь обтравить прямоугольник по маске SVG. проблема в том, что высота прямоугольника динамическая (от 0px до 32px), а SVG-изображение подгружается из файла (фигуры могут быть совершенно разные, загруженные пользователем, и нет возможности жёстко подгружать их код).

Пробовал применять use внутри svg совместно с clipPath (да и с overflow тоже, и с вариациями) - не получается, да и вообще по ходу use работает только для спрайтов (?):

<svg class="icon">
    <use xlink:href="/storage/uploads/default.svg"></use>
    <clipPath id="clipping">
        <rect style="position:relative;z-index:5;" width="32" fill="#78DE16" height="10"></rect>
    </clipPath>
</svg>

Пробовал вставлять svg как CSS фон и вешать overflow: hidden (и также ещё с clipPath) - безрезультатно.

<svg class="icon" style="background-image: url(/storage/uploads/default.svg);width: 32px;height: 32px;position: relative;overflow: hidden;">
        <rect style="position:relative;z-index:5;" width="32" fill="#78DE16" height="10"></rect>
</svg>

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

Как можно сделать такое?

Answer 1

Добавление изображений из внешнего источника

картинка может быть совершенно разной, и каждый рас статически её прописывать не получится. Например, такой: i.imgur.com/B7tqOMS.png

Самый простой способ добавить файл из внешнего источника с помощью тега <img>
Но это будет как отдельный объект и стилизация, маски в этом случае не работают. Но можно применить фильтры, как в первом ответе и тогда можно перекрашивать изображения в любой цвет полностью или частично.

  • Я загрузил вашу картинку на сервер.

  • Добавляю её в приложение с помощью <img>

  • Закрашиваю её с помощью фильтров.

Этот прием интересен ещё тем, что можно стилизовать SVG в формате base64, стилизация которого не доступна обычными способами.

img { 
filter:url(#filter1); 
}
<img src="https://svg-art.ru/files/Face.svg" width="371" height="348" > 
 
<svg  version="1.1" width="371" height="348" viewBox="0 0 371 348" xmlns="http://www.w3.org/2000/svg"> 
 <defs> 
 <filter id="filter1" x="0%" y="0%"> 
      <feFlood flood-color="#35B62E" /> 
      <feOffset  dy="150"> 
      </feOffset> 
      <feComposite operator="in" in2="SourceGraphic" /> 
      <feComposite operator="over" in2="SourceGraphic" /> 
    </filter> 
   </defs> 
  
</svg>

Пример изменения цвета при наведении курсора.

Использованы разные значения атрибута фильтра - dy=150 и dy="65" для достижения разной высоты заливки цветом.

img { 
filter:url(#filter_G); 
} 
 
img:hover { 
filter:url(#filter_R); 
}
<img src="https://svg-art.ru/files/Face.svg" width="371" height="348" > 
 
<svg  version="1.1" width="371" height="348" viewBox="0 0 371 348" xmlns="http://www.w3.org/2000/svg"> 
 <defs> 
 <filter id="filter_G" x="0%" y="0%"> 
      <feFlood flood-color="#35B62E" /> 
      <feOffset  dy="150"> 
      </feOffset> 
      <feComposite operator="in" in2="SourceGraphic" /> 
      <feComposite operator="over" in2="SourceGraphic" /> 
    </filter>  
	 
  <filter id="filter_R" x="0%" y="0%"> 
      <feFlood flood-color="red" /> 
      <feOffset  dy="65"> 
      </feOffset> 
      <feComposite operator="in" in2="SourceGraphic" /> 
      <feComposite operator="over" in2="SourceGraphic" /> 
    </filter>	 
   </defs> 
  
</svg>

Answer 2

Как можно сделать такое?

Если это то, что вы хотите получить на выходе, то можно не загружать извне файлы SVG c цветными прямоугольниками, не связываться масками, clip-path, с командой use, с которой надо будет ещё решать проблемы со стилизацией. А это не просто.

Всё можно сделать проще:

Есть более универсальный способ, как закрасить часть фигуры в любой цвет.

Можно воспользоваться фильтрами SVG

<feFlood flood-color="#35B62E" /> <feOffset dy="20"> </feOffset>

Первый фильтр обеспечивает закраску в нужный цвет.
Второй фильтр посредством атрибута dy="20" устанавливает отступ от начала закраски

<svg  version="1.1" width="64" height="64" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg"> 
 <defs> 
 <filter id="filter1" x="0%" y="0%"> 
      <feFlood flood-color="#35B62E" /> 
      <feOffset  dy="20"> 
      </feOffset> 
      <feComposite operator="in" in2="SourceGraphic" /> 
      <feComposite operator="over" in2="SourceGraphic" /> 
    </filter> 
   </defs> 
  <path  fill="black" d="m32 6 19 11-0.02 22-19 11-19-11 0.02-22z"  filter= "url(#filter1)" /> 
   
</svg>

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

<svg  version="1.1" width="64" height="64" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg"> 
 <defs> 
 <filter id="filter1" x="0%" y="0%"> 
      <feFlood flood-color="#35B62E" /> 
      <feOffset  dx="16"> 
      </feOffset> 
      <feComposite operator="in" in2="SourceGraphic" /> 
      <feComposite operator="over" in2="SourceGraphic" /> 
    </filter> 
   </defs> 
  <circle cx="32" cy="32" r="16" fill="black" filter="url(#filter1)" /> 
   
</svg>

Анимация закраски

Также довольно просто, при необходимости, можно сделать анимацию закраски.

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

В примере ниже закраска начинается при наведении курсора и обратно при уходе курсора

<svg  version="1.1" width="128" height="128" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg"> 
 <defs> 
 <filter id="filter1" x="0%" y="0%"> 
      <feFlood flood-color="#35B62E" /> 
      <feOffset dy="0"> 
        <animate id="anim" attributeName="dy" values="0;20" dur="1s" begin="path1.mouseover" restart="whenNotActive" fill="freeze"/>   
	   <animate id="anim" attributeName="dy" values="20;0" dur="1s" begin="path1.mouseout" restart="whenNotActive" fill="freeze"/> 	 
      </feOffset> 
      <feComposite operator="in" in2="SourceGraphic" /> 
      <feComposite operator="over" in2="SourceGraphic" /> 
    </filter> 
   
 </defs> 
  <path id="path1"  fill="black" d="m32 6 19 11-0.02 22-19 11-19-11 0.02-22z"  filter= "url(#filter1)" stroke-width="1"/> 
 
</svg>

READ ALSO
Почему в JavaScript keyDown не отлавливает больше 4-х одновременных нажатий?

Почему в JavaScript keyDown не отлавливает больше 4-х одновременных нажатий?

Есть виртуальная клавиатура https://codepenio/CitizenOne/pen/rReYqj

103
Как в моем примере проверить key в localStorage при загрузке страницы?

Как в моем примере проверить key в localStorage при загрузке страницы?

Как получить и проверить key при загрузке страницы я могу, но не в этом примере:

110
Ошибка в расширении Emmet LiveStyle в консоли js

Ошибка в расширении Emmet LiveStyle в консоли js

Собственно данная ошибка выводится в хроме в любой складке

135
Как управлять скоростью анимации в canvas?

Как управлять скоростью анимации в canvas?

Коллеги, как думаете, как я могу под конец анимации плавно замедлять отрисовку круга? Опыт javascript = junior

93