Радиальный градиент SVG не анимируется при наведении курсора мыши

Радиальный градиент должен увеличиваться с 10% до 100% при наведении курсора, но он просто ничего не делает. Не могу понять, что я делаю не так.

<svg id = "svgDoc" xmlns = "http://www.w3.org/2000/svg" xmlns:xlink = "http://www.w3.org/1999/xlink" viewBox = "0 0 500 500" shape-rendering = "geometricPrecision" text-rendering = "geometricPrecision">

<style><![CDATA[
#svgDoc {
    pointer-events: all
}

#svgDoc:hover #svgGrad {
    animation: svgGrad_f_p 3000ms linear 1 normal forwards
}

@keyframes svgGrad_f_p {
    0% {
        offset: 10%
    } 
    100% {
        offset: 100%
    }
}
]]>
</style>

<defs>
<radialGradient id = "svgGrad-fill" cx = "0" cy = "0" r = "0.5" spreadMethod = "pad" gradientUnits = "objectBoundingBox" gradientTransform = "translate(0.5 0.5)">
<stop id = "svgGrad-fill-0" offset = "0%" stop-color = "#ff0000"/>
<stop id = "svgGrad-fill-1" offset = "10%" stop-color = "#000"/>
</radialGradient>
</defs>

<rect id = "svgGrad" width = "500" height = "500" rx = "0" ry = "0" fill = "url(#svgGrad-fill)"/>
</svg>
stackoverflow.com/q/57218443/8620333
Temani Afif 28.07.2024 11:42

вам нужно анимировать смещение одного градиента.

Robert Longson 28.07.2024 12:00

@RobertLongson Я отредактировал свой исходный код, чтобы попытаться анимировать смещение. Но я подозреваю, что сделал это неправильно, потому что это все еще не работает. Ой.

John 28.07.2024 13:58
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Введение в CSS
Введение в CSS
CSS является неотъемлемой частью трех основных составляющих front-end веб-разработки.
Как выровнять Div по центру?
Как выровнять Div по центру?
Чтобы выровнять элемент <div>по горизонтали и вертикали с помощью CSS, можно использовать комбинацию свойств и значений CSS. Вот несколько методов,...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
CSS: FlexBox
CSS: FlexBox
Ранее разработчики использовали макеты с помощью Position и Float. После появления flexbox сценарий полностью изменился.
0
3
106
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вы не можете манипулировать атрибутом stop через CSS, поскольку он «конфликтует» с одноименным свойством CSS offset-path.

Обходной путь 1. SMIL-анимация, вызываемая атрибутом begin.

Обходным решением может быть анимация градиента с помощью SMIL-анимации, например:

<h3>Hover me</h3>
<svg id = "svgDoc" xmlns = "http://www.w3.org/2000/svg" viewBox = "0 0 500 500">
  <defs>
    <radialGradient id = "svgGrad-fill" cx = "0" cy = "0" r = "0.5" spreadMethod = "pad" gradientUnits = "objectBoundingBox" gradientTransform = "translate(0.5 0.5)">
      <stop id = "svgGrad-fill-0" offset = "0%" stop-color = "#ff0000" />
      <stop id = "svgGrad-fill-1" offset = "10%" stop-color = "#000" >
           <animate attributeName = "offset" fill = "freeze" values = "0.1;1" dur = "1s" repeatCount = "1" begin = "svgGrad.mouseover"  /> 
           <animate attributeName = "offset" fill = "freeze" values = "1;0.1" dur = "1s" repeatCount = "1" begin = "svgGrad.mouseout"  /> 
      </stop>
    </radialGradient>
  </defs>

  <rect id = "svgGrad" width = "500" height = "500" rx = "0" ry = "0" fill = "url(#svgGrad-fill)" />
</svg>

К счастью, элемент <animate> SVG позволяет вам добавлять события для запуска (или остановки) воспроизведения. См. «Документация mdn: начало»

<animate attributeName = "offset" fill = "freeze" values = "1;0.1" dur = "1s" repeatCount = "1" begin = "svgGrad.mouseout"  /> 

Добавление смягчения перехода

Нам нужны атрибуты calcMode = "spline" и keySplines, чтобы определить пользовательское замедление – аналогично замедлению CSS:

  • Легкость: keySplines = "0.42 0 1 1"
  • Легкость выхода: keySplines = "0.42 0 0.58 1"
  • Легкость: keySplines = "0.25 0.1 0.25 1"

Как и в случае с атрибутом keyTimes, мы можем указать несколько значений в виде массива, разделенного точкой с запятой.
В вашем случае у нас есть только 2 состояния анимации (начало: 0,1/10%, конец: 1/100%), как описано атрибутом keyTimes. Поэтому нам нужно только одно значение Безье. В противном случае нам нужно повторить keySplines в соответствии с keyTimes, где keySplines=keyTimes .length-1.

Вот пример сравнения результатов смягчения

body{
font-size:3vmin;
display:grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
gap:1em;
}
<div class = "col">
<h3>linear</h3>
<svg viewBox = "0 0 500 500">
  <defs>
    <radialGradient id = "svgGrad-fill" cx = "50%" cy = "50%" r = "0.5" spreadMethod = "pad" gradientUnits = "objectBoundingBox">
      <stop offset = "0%" stop-color = "#ff0000" />
      <stop offset = "10%" stop-color = "#000">
        
       <animate attributeName = "offset" fill = "freeze" values = "0.1;1;0.1" keyTimes = "0;0.5;1"  dur = "1s" repeatCount = "indefinite" begin = "0" />

        <!--
        <animate attributeName = "offset" fill = "freeze" values = "0.1;1" keyTimes = "0;1" dur = "1s" repeatCount = "1" begin = "svgGrad.mouseover" />
        <animate attributeName = "offset" fill = "freeze" values = "1;0.1" keyTimes = "0;1" dur = "1s" repeatCount = "1" begin = "svgGrad.mouseout" />
-->
      </stop>
    </radialGradient>
  </defs>

  <rect id = "svgGrad" width = "500" height = "500" rx = "0" ry = "0" fill = "url(#svgGrad-fill)" />
</svg>
</div>


<div class = "col">
<h3>Ease-in-out</h3>
<svg viewBox = "0 0 500 500">
  <defs>
    <radialGradient id = "svgGradFillEaseInOut" cx = "50%" cy = "50%" r = "0.5" spreadMethod = "pad" gradientUnits = "objectBoundingBox">
      <stop offset = "0%" stop-color = "#ff0000" />
      <stop offset = "10%" stop-color = "#000">
        
        <animate attributeName = "offset" fill = "freeze" values = "0.1;1;0.1" keyTimes = "0;0.5;1" calcMode = "spline" keySplines = "0.42 0 0.58 1; 0.42 0 0.58 1" dur = "1s" repeatCount = "indefinite" begin = "0" />
        <!--
        <animate attributeName = "offset" fill = "freeze" values = "0.1;1" keyTimes = "0;1" calcMode = "spline" keySplines = "0.42 0 0.58 1" dur = "1s" repeatCount = "1" begin = "svgGradEaseInOut.mouseover" />
        <animate attributeName = "offset" fill = "freeze" values = "1;0.1" keyTimes = "0;1" calcMode = "spline" keySplines = "0.42 0 0.58 1" dur = "1s" repeatCount = "1" begin = "svgGradEaseInOut.mouseout" />
-->
      </stop>
    </radialGradient>
  </defs>
  <rect id = "svgGradEaseInOut" width = "500" height = "500" rx = "0" ry = "0" fill = "url(#svgGradFillEaseInOut)" />
</svg>
</div>


<div class = "col">
<h3>Ease-in</h3>
<svg viewBox = "0 0 500 500">
  <defs>
    <radialGradient id = "svgGradFillEaseIn" cx = "50%" cy = "50%" r = "0.5" spreadMethod = "pad" gradientUnits = "objectBoundingBox">
      <stop offset = "0%" stop-color = "#ff0000" />
      <stop offset = "10%" stop-color = "#000">
        <animate attributeName = "offset" fill = "freeze" values = "0.1;1;0.1" keyTimes = "0;0.5;1" calcMode = "spline" keySplines = "0.42 0 1 1; 0.42 0 1 1" dur = "1s" repeatCount = "indefinite" begin = "0" />

        <!--
        <animate attributeName = "offset" fill = "freeze" values = "0.1;1" keyTimes = "0;1" calcMode = "spline" keySplines = "0.42 0 1 1" dur = "1s" repeatCount = "1" begin = "svgGradEaseIn.mouseover" />
        <animate attributeName = "offset" fill = "freeze" values = "1;0.1" keyTimes = "0;1" calcMode = "spline" keySplines = "0.42 0 1 1" dur = "1s" repeatCount = "1" begin = "svgGradEaseIn.mouseout" />
-->
      </stop>
    </radialGradient>
  </defs>
  <rect id = "svgGradEaseIn" width = "500" height = "500" rx = "0" ry = "0" fill = "url(#svgGradFillEaseIn)" />
</svg>
</div>

<div class = "col">
<h3>Ease</h3>
<svg viewBox = "0 0 500 500">
  <defs>
    <radialGradient id = "svgGradFillEase" cx = "50%" cy = "50%" r = "0.5" spreadMethod = "pad" gradientUnits = "objectBoundingBox">
      <stop offset = "0%" stop-color = "#ff0000" />
      <stop offset = "10%" stop-color = "#000">
        
        <animate attributeName = "offset" fill = "freeze" values = "0.1;1;0.1" keyTimes = "0;0.5;1" calcMode = "spline" keySplines = "0.25 0.1 0.25 1; 0.25 0.1 0.25 1" dur = "1s" repeatCount = "indefinite" begin = "0" />

        
        <!--
        <animate attributeName = "offset" fill = "freeze" values = "0.1;1" keyTimes = "0;1" calcMode = "spline" keySplines = "0.25 0.1 0.25 1" dur = "1s" repeatCount = "1" begin = "svgGradEase.mouseover" />
        <animate attributeName = "offset" fill = "freeze" values = "1;0.1" keyTimes = "0;1" calcMode = "spline" keySplines = "0.25 0.1 0.25 1" dur = "1s" repeatCount = "1" begin = "svgGradEase.mouseout" />
-->
      </stop>
    </radialGradient>
  </defs>
  <rect id = "svgGradEase" width = "500" height = "500" rx = "0" ry = "0" fill = "url(#svgGradFillEase)" />
</svg>
</div>

Обходной путь 2. Пользовательский CSS @property

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

@property --offset1 {
  syntax: "<percentage>";
  inherits: false;
  initial-value: 0%;
}


@property --offset2 {
  syntax: "<percentage>";
  inherits: false;
  initial-value: 10%;
}


.gradient{
  background: radial-gradient( #FF0000 var(--offset1), #000 var(--offset2));
  transition:1s --offset1, 1s --offset2;
}

.gradient:hover{
  --offset1: 5%;
  --offset2:100%;
}
<h3>Gradient CSS</h3>
<svg id = "svgDoc2" class = "gradient" xmlns = "http://www.w3.org/2000/svg" viewBox = "0 0 500 500">
</svg>

Это не будет работать с переменными CSS, поскольку мы не можем перенести само свойство background.

В этом подходе мы применяем градиент CSS к самому внешнему (родительскому) элементу SVG, принимая стили градиента CSS так же, как элемент HTML (в отличие от внутренних элементов SVG).

Однако подход SMIL, вероятно, по-прежнему обеспечивает лучшую кроссбраузерную совместимость.

Смотрите также:

Спасибо! Один вопрос: можно ли применить плавный переход/ослабление к вашему первому обходному пути?

John 29.07.2024 17:15

@Алан. В SVG SMIL смягчение немного не интуитивно понятно. Я добавил дополнительную информацию к ответу. Но вы видите, что градиент изменился или на самом деле просто мгновенно изменился (без линейного перехода)?

herrstrietzel 30.07.2024 02:38

Спасибо за добавление облегчения. Я только что обнаружил глюк в SMIL-анимации: если навести курсор и быстро отпустить его (не дожидаясь полного расширения градиента), анимация наведения курсора не продолжается с текущей позиции, а начинается со 100%. Например, обходной путь @propety не имеет этого сбоя. Можно ли исправить этот глюк в СМИЛ?

John 30.07.2024 20:46

Извините, боюсь, вы не сможете исправить это для SMIL. Вероятно, подход CSS — лучший вариант для ваших нужд.

herrstrietzel 30.07.2024 20:52

Понял. Могу ли я задать вам еще один вопрос? Можно ли применить обходной путь @property не к самому внешнему/родительскому элементу SVG (как вы упомянули), а к элементу под ним? Я попробовал настроить таргетинг, но по какой-то причине это не сработало.

John 30.07.2024 21:19

В этом и суть: вы не можете применять градиенты CSS (и другие свойства, такие как отступы) к SVG (дочерним) элементам. Самый внешний SVG также является элементом HTML (вот почему он работает). Итак, нет, вы не можете применять градиенты CSS, например, к вложенным элементам SVG.

herrstrietzel 30.07.2024 21:24

В качестве другого обходного пути вы также можете использовать <foreignObject> для градиентной заливки. Честно говоря, я не большой их поклонник, поскольку они могут быть весьма непоследовательными при кроссбраузерном рендеринге. Возможно, вы также могли бы использовать градиенты в HTML/CSS и вместо этого разместить векторные элементы внутри вместо того, чтобы включать все в родительский SVG. Если вы не можете достичь того, чего пытаетесь сделать, вы можете добавить новый вопрос, описывающий, что не работает, на более подробном примере.

herrstrietzel 30.07.2024 21:30

Большое спасибо за все предложения. Я попытаюсь изменить свой SVG-документ так, чтобы целевой элемент SVG стал родительским, чтобы я мог использовать обходной путь @property. Если не получится, посмотрю <foreignObject>. Если это не сработает, я опубликую новый вопрос с более подробной информацией. Спасибо за терпение.

John 30.07.2024 21:48

Чтобы анимировать радиальный градиент в SVG, вам необходимо убедиться, что анимируемые свойства совместимы с анимацией CSS. В SVG вы не можете напрямую анимировать свойство смещения остановок градиента с помощью CSS. Вместо этого вам нужно использовать SMIL (язык синхронизированной интеграции мультимедиа) для анимации свойств $VG.

Вот как вы можете изменить SVG для анимации радиального градиента с помощью SMIL:

<svg id = "svgDoc" xmlns = "http://www.w3.org/2000/svg" xmlns:xlink = "http://www.w3.org/1999/xlink" viewBox = "0 0 500 500" shape-rendering = "geometricPrecision" text-rendering = "geometricPrecision">

<defs>
  <radialGradient id = "svgGrad-fill" cx = "50%" cy = "50%" r = "50%" spreadMethod = "pad" gradientUnits = "objectBoundingBox">
    <stop id = "svgGrad-fill-0" offset = "0%" stop-color = "#ff0000">
      <animate attributeName = "offset" from = "0%" to = "10%" dur = "3s" begin = "svgDoc:hover" repeatCount = "indefinite" />
    </stop>
    <stop id = "svgGrad-fill-1" offset = "10%" stop-color = "#000">
      <animate attributeName = "offset" from = "10%" to = "100%" dur = "3s" begin = "svgDoc:hover" repeatCount = "indefinite" />
    </stop>
  </radialGradient>
</defs>

<rect id = "svgGrad" width = "500" height = "500" rx = "0" ry = "0" fill = "url(#svgGrad-fill)"/>
</svg>

Извините, этот код не работает.

John 30.07.2024 19:21

Другие вопросы по теме