Радиальный градиент должен увеличиваться с 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>
вам нужно анимировать смещение одного градиента.
@RobertLongson Я отредактировал свой исходный код, чтобы попытаться анимировать смещение. Но я подозреваю, что сделал это неправильно, потому что это все еще не работает. Ой.
Вы не можете манипулировать атрибутом stop
через CSS, поскольку он «конфликтует» с одноименным свойством CSS offset-path.
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>
@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, вероятно, по-прежнему обеспечивает лучшую кроссбраузерную совместимость.
Смотрите также:
Спасибо! Один вопрос: можно ли применить плавный переход/ослабление к вашему первому обходному пути?
@Алан. В SVG SMIL смягчение немного не интуитивно понятно. Я добавил дополнительную информацию к ответу. Но вы видите, что градиент изменился или на самом деле просто мгновенно изменился (без линейного перехода)?
Спасибо за добавление облегчения. Я только что обнаружил глюк в SMIL-анимации: если навести курсор и быстро отпустить его (не дожидаясь полного расширения градиента), анимация наведения курсора не продолжается с текущей позиции, а начинается со 100%. Например, обходной путь @propety не имеет этого сбоя. Можно ли исправить этот глюк в СМИЛ?
Извините, боюсь, вы не сможете исправить это для SMIL. Вероятно, подход CSS — лучший вариант для ваших нужд.
Понял. Могу ли я задать вам еще один вопрос? Можно ли применить обходной путь @property не к самому внешнему/родительскому элементу SVG (как вы упомянули), а к элементу под ним? Я попробовал настроить таргетинг, но по какой-то причине это не сработало.
В этом и суть: вы не можете применять градиенты CSS (и другие свойства, такие как отступы) к SVG (дочерним) элементам. Самый внешний SVG также является элементом HTML (вот почему он работает). Итак, нет, вы не можете применять градиенты CSS, например, к вложенным элементам SVG.
В качестве другого обходного пути вы также можете использовать <foreignObject>
для градиентной заливки. Честно говоря, я не большой их поклонник, поскольку они могут быть весьма непоследовательными при кроссбраузерном рендеринге. Возможно, вы также могли бы использовать градиенты в HTML/CSS и вместо этого разместить векторные элементы внутри вместо того, чтобы включать все в родительский SVG. Если вы не можете достичь того, чего пытаетесь сделать, вы можете добавить новый вопрос, описывающий, что не работает, на более подробном примере.
Большое спасибо за все предложения. Я попытаюсь изменить свой SVG-документ так, чтобы целевой элемент SVG стал родительским, чтобы я мог использовать обходной путь @property
. Если не получится, посмотрю <foreignObject>
. Если это не сработает, я опубликую новый вопрос с более подробной информацией. Спасибо за терпение.
Чтобы анимировать радиальный градиент в 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>
Извините, этот код не работает.