让svg内部元素运行CSS动画
本帖最后由 马黑黑 于 2024-9-19 18:38 编辑 <br /><br /><style>#artbox { margin: auto; font-size: 18px; }
#artbox > p { margin: 12px 0; }
#artbox mark { margin: 0 4px; padding: 0 4px; background: lightblue; }
.txtRed { color: red; }
svg { border: 1px solid; }
</style>
<div id="artbox">
<p>svg有自己一整套动画系统,诸如 set、animate、animateMotion 和 animateTransform 等,都可以在svg中以标签的形式为svg子元素制作动画。遗憾的是svg动画实现过程有点麻烦,不仅动画属性繁多,而且每一个动画标签只能负责一个基于子元素的属性变更(如改变圆心坐标)动画,做一个可能不算复杂的动画也需要多个动画标签来配套完成。CSS动画则简便得多,一切基于元素不同属性的动画都可以放在 @keyframes 选择器中进行表述,然后元素通过 animate 属性引用 @keyframes 选择器的动画名称即可,animation 还可以引用多个 @keyframes 动画并以不同的方式和运行周期运动动画。</p>
<p>先看纯svg的动画例子。下面的示例,矩形元素rect自上而下垂直落下的同时旋转360度:</p>
<svg width="400" height="200" viewBox="0 0 400 200">
<rect x="180" y="0" width="40" height="40" fill="olive">
<animateTransform attributeName="transform" type="rotate" from="0 200 20" to="360 200 220" dur="5s" begin="0s" additive="sum" repeatCount="indefinite"/>
<animateTransform attributeName="transform" type="translate" from="0 0" to="0 200" dur="5s" begin="0s" additive="sum" repeatCount="indefinite"/>
</rect>
</svg>
<p>代码:</p>
<div class="hE"><pre></pre></div>
<p>查看代码,rect标签内两次使用了 <span class="txtRed">animateTransform</span> 动画标签,一次设置rect的旋转 <span class="txtRed">rotate</span>,另一次设置rect的位移 <span class="txtRed">translate</span>。animateTransform动画多个属性联合使用时需要配置 <span class="txtRed">additive</span> 属性,值为 <span class="txtRed">sum</span> 表示动画叠加 <span class="txtRed">repalce</span> 表示动画替换——即后一个动画替换前一个动画。需要特别注意,旋转动画的 <span class="txtRed">from</span> 和 <span class="txtRed">to</span> 值要设置从{200 20}到{200 220}的rect自己的中心点坐标,不设置或设置不当rect的运动将不是垂直下落;当然,可以给rect加入style属性设置 transform-box 和 transform-origin,像这样,<mark>style="transform-box: fill-box; transform-origin: center"</mark>,如此from和to属性赋值使用单值即旋转角度就可以了。</p>
<p>前述CSS动画的灵活性其实也可以用于svg,换言之,如果需要,我们完全可以避开svg原生动画标签,转而通过CSS来定制svg内部元素基于@keyframes+animation的动画。现在,我们使用CSS动画实现上例的效果:</p>
<svg width="400" height="200" viewBox="0 0 400 200">
<style>
.down { fill: olive; stroke: none; transform-box: fill-box; transform-origin: center; animation: down 5s linear infinite; }
@keyframes down { to { transform: translateY(200px) rotate(360deg); } }
</style>
<rect x="180" y="0" width="40" height="40" class="down"></rect>
</svg>
<p>代码:</p>
<div class="hE"><pre></pre></div>
<p>查看代码会发现,CSS写在<mark><svg> ... </svg></mark>标签内,这和做HTML帖子将CSS放在<mark><body> ... </body></mark>标签之内是同一个道理。CSS代码中,<span class="txtRed">.down</span> 选择器 ① 强制转换原点基于自己的中心。这是非常有必要的,因为即使是使用CSS @keyframes动画,svg内的元素依然不会自己改变坐标系;② 设定填充样式 <span class="txtRed">fill</span>、描边样式 <span class="txtRed">stroke</span> 和动画 <span class="txtRed">animation</span> 属性。动画选择器 <span class="txtRed">@keyframes</span> 则设置了一个动画:自上而下移动+旋转360度,二者放在同一个 transform 属性里进行表述,非常简洁。</p>
<p>有意思的是,svg内CSS设定的@keyframes动画,transform的translate平移数值支持百分比,且百分比基于父元素,这和HTML的CSS相同属性动画值不同,后者的百分比基于子元素即引用动画的元素。原因很简单:针对 transform 形变,HTML元素以自我中心点为运动原点,所以translate平移百分比数值基于自身;svg里的元素则以svg画布左上角为形变原点,所以百分比平移数值基于父元素即svg画布。此外,非常重要的,svg标签加上版本号和命名空间属性存为.svg文档之后是合法的,可以当图片使用,像如下代码:</p>
<div class="hE"><pre>
<svg width="400" height="200" viewBox="0 0 400 200" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<style>
.down { fill: olive; stroke: none; transform-box: fill-box; transform-origin: center; animation: down 5s linear infinite; }
@keyframes down { to { transform: translateY(200px) rotate(360deg); } }
</style>
<rect x="180" y="0" width="40" height="40" class="down"></rect>
</svg>
</pre></div>
<p>svg使用CSS动画除了本文介绍的方法,还可以使用XML的语言规范为svg设置CSS动画,甚至可以和HTML一样使用外部CSS文档。</p>
</div>
<script>
var sc = document.createElement('script');
sc.chartset = 'utf-8';
sc.src = 'https://638183.freep.cn/638183/web/js2024/helight.js';
document.body.appendChild(sc);
var svgs = artbox.querySelectorAll('svg'),
pres = artbox.querySelectorAll('pre');
svgs.forEach((pre,key) => {
var str = svgs.outerHTML.replaceAll('<',"<");
str = str.replaceAll('>', '>');
pres.innerHTML = str;
});
</script>
评分把帖子破坏了{:5_102:}
<style>…………</style> 放在svg外也一样。 使用svg自己的动画系统不如使用CSS的简便且熟悉呢{:4_173:} svg的自己的动画太麻烦了,比如那个transform是从0到200,结果rotate的纵向居中就要跟着是20到220,这样一个不小心就会弄错啊。
还是css好,一个center就不用管了{:4_173:} 能动起来的都厉害,何况还有多种实现方法。。{:4_199:}
花飞飞 发表于 2024-9-19 22:23
能动起来的都厉害,何况还有多种实现方法。。
svg的动画略难一些 红影 发表于 2024-9-19 22:17
svg的自己的动画太麻烦了,比如那个transform是从0到200,结果rotate的纵向居中就要跟着是20到220,这样一 ...
是的。我个人觉得 animateTransform是多余的,应该让 animate 属性里的 transform 能发挥动画功能。 红影 发表于 2024-9-19 22:13
使用svg自己的动画系统不如使用CSS的简便且熟悉呢
不过也是因为先入为主吧,接触CSS动画要早一些 起个网名好难 发表于 2024-9-19 20:07
评分把帖子破坏了
………… 放在svg外也一样。
你存为 .svg 文档时会报错,除非严格按 XML 语法写style 标签 马黑黑 发表于 2024-9-19 22:51
是的。我个人觉得 animateTransform是多余的,应该让 animate 属性里的 transform 能发挥动画功能。
反正我是觉得简单的更容易让人接受。 马黑黑 发表于 2024-9-19 22:52
不过也是因为先入为主吧,接触CSS动画要早一些
是的,所以更习惯些。 红影 发表于 2024-9-19 23:40
是的,所以更习惯些。
CSS动画也是要简洁得多 红影 发表于 2024-9-19 23:40
反正我是觉得简单的更容易让人接受。
这是自然。不过复杂的机制能做出更为精巧的东东,比如光刻机的制作。 马黑黑 发表于 2024-9-20 07:44
CSS动画也是要简洁得多
两者能通用,非常好呢。 马黑黑 发表于 2024-9-20 07:44
这是自然。不过复杂的机制能做出更为精巧的东东,比如光刻机的制作。
也需要学得更多些才行。 马黑黑 发表于 2024-9-19 22:50
svg的动画略难一些
面熟和面生的区别,其实应该都不容易{:4_173:} 老师发的都是最尖端的代码编程,佩服了! 梦江南 发表于 2024-9-21 08:37
老师发的都是最尖端的代码编程,佩服了!
果酱了 花飞飞 发表于 2024-9-20 21:30
面熟和面生的区别,其实应该都不容易
CSS容易得多 马黑黑 发表于 2024-9-21 11:47
CSS容易得多
难怪看着比较亲切{:4_170:}纯svg一个单词快一米长,太吓人了。
页:
[1]
2