svg动画之 animateTransform(一)
<style>.papa > p { margin: 10px 0; }
.hCode { display: block; padding: 10px 10px 10px 50px; font: normal 15px/20px Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; background: #F7EFE6; overflow-x: auto; tab-size: 4; position: relative; }
.hCode::before { position: absolute; content: attr(data-line); left: 0; top: 0; padding: 10px; font: inherit; color: #999; text-align: right; border-right: 1px solid tan; }
.rred { color: red; }
.zs { color: green; }
</style>
<div class="papa">
<h2>svg动画之 animateTransform(一)</h2>
<p>animateTransform,从动画名称我们就可以看出,这是一个基于基础变形 transform 的动画。transform 也称转换、形变等,其功能是令对象旋转(rotate)、移位(translate)、缩放(scale)和倾斜(skewX/Y)。虽然 animate 动画元素可以通过改变运动对象的属性达成动画效果,却不能对 transform 转换做任何工作,因为这个工位专由 animateTransform 动画元素负责。</p>
<p>之前介绍 animate 动画时我们讲到的绝大多数动画属性同样适用于 animateTransform,不过 animateTransform 需要一个额外的属性 type,用以指明 transform 的变形种类是 rotate、translate、scale 和 skewX/Y 中的哪一个,例如:</p>
<pre class="hCode">
type="translate"
</pre>
<p>我们先来看看由 animateTransform 驱动的动画效果,请将鼠标指针移至下方图形:</p>
<svg style="border: 1px solid tan">
<g id="g1" transform="rotate(15,60 60)">
<circle cx="60" cy="60" r="8" transform="translate(60,0) scale(6,1) translate(-60,0)" fill="olive" />
<circle cx="60" cy="60" r="8" transform="translate(0,60) scale(1,6) translate(0,-60)" fill="olive" />
<circle cx="60" cy="60" r="10" fill="tan" />
</g>
<animateTransform
xlink:href="#g1"
attributeName="transform"
type="rotate"
from="15,60 60"
to="375, 60 60"
dur="3s"
begin="mouseover"
restart="whenNotActive"
/>
</svg>
<p>代码:</p>
<pre class="hCode">
<svg style="border: 1px solid tan">
<g id="g1" transform="rotate(15,60 60)">
<circle cx="60" cy="60" r="8" transform="translate(60,0) scale(6,1) translate(-60,0)" fill="olive" />
<circle cx="60" cy="60" r="8" transform="translate(0,60) scale(1,6) translate(0,-60)" fill="olive" />
<circle cx="60" cy="60" r="10" fill="tan" />
</g>
<animateTransform
xlink:href="#g1"
attributeName="transform"
<span class="rred">type="rotate"</span>
from="15,60 60"
to="375, 60 60"
dur="3s"
begin="<span class="rred">mouseover</span>"
restart="whenNotActive"
/>
</svg>
</pre>
<p>代码解析:首先,我们绘制了三个圆心坐标一致的小圆,前两个小圆在原地分别沿水平和竖直方向放大六倍,并将这三个小圆放到 g 标签里面构成一个小组,g 标签拥有id标识,在原地旋转15度。接着,我们请出 animateTransform 动画元素,它与 g 标签并列,所以通过 xlink:href 属性指明作用对象为 id="g1" 的 g 标签;动画诸多属性中,与之前介绍的 animate 属性绝大多数完全一样,就多一个 type 属性,如前已述,它用来描述 transform 的动画类型。</p>
<p>当然,这里还有需要注意的地方:一是 g 标签带组问题。g 标签之内的元素作为一个整体由 g 带领,我们的 animateTransform 动画针对的是 g 元素,换言之,动画仅驱动 g 运动,g 标签内的元素因 g 的运动而运动;其二,就是转动(rotate)的衔接问题。上例,g 标签自身转动了15度,如果希望 g 小组转一圈,则 to 的属性值应为 15+360=375 度;其三,例中 begin 属性的 mouseover 值,和 click 一样,属于 SMIL 设计中对 JS 的借鉴,容易理解和记忆。</p>
<p>下面再看一个移位转换示例,type="translate",单击小圆运行动画:</p>
<svg style="width: 300px; height: 150px; border: 1px solid gray;">
<circle cx="30" cy="30" r="30" fill="tan">
<animateTransform
attributeName="transform"
type="translate"
values="0,0; 240,90; 0,0"
dur="3s"
begin="click"
restart="whenNotActive"
fill="freeze"
/>
</circle>
</svg>
<p>代码和解释:</p>
<pre class="hCode">
<svg style="width: 300px; height: 150px; border: 1px solid gray;">
<circle cx="30" cy="30" r="30" fill="tan">
<animateTransform
attributeName="transform" <span class="zs"><-- 指定transform为运动属性 --></span>
type="translate" <span class="zs"><-- 指明transform类型为translate --></span>
values="0,0; 240,90; 0,0" <span class="zs"><-- 运动轨迹 --></span>
dur="3s" <span class="zs"><-- 周期运动时长 --></span>
begin="click" <span class="zs"><-- 设定单击为动画触发事件 --></span>
restart="whenNotActive" <span class="zs"><-- 定义重启动画方式 :非活动时可以重启 --></span>
fill="freeze" <span class="zs"><-- 定义动画结束时的状态 :保持最后的姿势 --></span>
/>
</circle>
</svg>
</pre>
<p>再重点解释一下 values 属性的设计:translate是将运动对象进行位移,它支持{x,y}同时移动,写成 translate(x,y)。例中的 values 属性值共三组:第一组 {0,0},小圆元素位移从起点开始,出发点坐标实际上就是小圆所占矩形区域的左上角XY坐标而不是圆心坐标 {cx,cy};第二组 {240,90},在svg的右下角,值参考的也是小圆所占矩形区域的左上角XY坐标,不是圆心。为什么是240和90呢?拿svg的宽高减去圆的直径就可以明白了;第三组 {0,0},位移到原位。所以,这个 values 属性值的设计,就是让圆从左上角出发,到右下角后再位移到左上角的路线。</p>
<p>阅读至此,应该感受到,animateTransform 动画元素(标签)其实并不复杂,但要掌握它需要有个前提:要弄清svg中基础变形和svg动画相关的基本知识。知识需要前后关联,完好的认知体系就能相对完整地建立起来。</p>
</div>
<script>
let btns = document.querySelectorAll('.btnok'),
stages = document.querySelectorAll('.stage'),
hCodes = document.querySelectorAll('.hCode'),
hLineNums = document.querySelectorAll('.hLineNum');
hCodes.forEach((item,key) => {
let lines = hCodes.innerText.trim().split('\n').length;
let str = '';
for(let i = 0; i < lines; i ++) {
str += i + 1 + '\n';
}
item.dataset.line = str;
});
</script>
位移的这个比较简单,直接作用在小圆上,应该先讲这个再讲那个旋转的,旋转的那个更难点{:4_173:} animateTransform可以直接作用于元素,也可以作用于 g 标签带的组,且是作用于 g 标签,“g 标签内的元素因 g 的运动而运动”。
上述例子,位移的点位选取左上角,旋转选取的旋转中心是图形中心。 前几堂课讲的 animate 是通过改变对象属性达成动画效果,而animateTransform则可以直接对对象进行旋转、位移、缩放、倾斜的操作,达成动画效果。{:4_204:}
哦,我知道为什么先介绍旋转了,原来是按旋转、位移、缩放、倾斜的顺序介绍的。那么下面一节课应该就是介绍缩放和倾斜了吧,好期待啊{:4_173:} 红影 发表于 2023-11-21 09:46
前几堂课讲的 animate 是通过改变对象属性达成动画效果,而animateTransform则可以直接对对象进行旋转、位 ...
对的,并且,可能不太难 红影 发表于 2023-11-21 09:36
位移的这个比较简单,直接作用在小圆上,应该先讲这个再讲那个旋转的,旋转的那个更难点
其实是一样简单的。你把 g 标签换成一个矩形什么的不就成了?
g 是一个集合,把 g 和它所带的子元素看成一个单位,一切也可以简单。 马黑黑 发表于 2023-11-21 12:20
对的,并且,可能不太难
是的,这个倒是不那么难呢。 马黑黑 发表于 2023-11-21 12:26
其实是一样简单的。你把 g 标签换成一个矩形什么的不就成了?
g 是一个集合,把 g 和它所带的子元素看 ...
嗯嗯,把它看成独立的,其实就跟小圆是一回事了。 红影 发表于 2023-11-21 15:56
嗯嗯,把它看成独立的,其实就跟小圆是一回事了。
g 的作用就是分组,组里所有的东东可以有自己的特性,但都是一个组的,相当于一家人,搬家时是一起搬的 红影 发表于 2023-11-21 15:55
是的,这个倒是不那么难呢。
特别是,越过一些门槛后 马黑黑 发表于 2023-11-21 18:03
g 的作用就是分组,组里所有的东东可以有自己的特性,但都是一个组的,相当于一家人,搬家时是一起搬的
步调一致{:4_173:} 马黑黑 发表于 2023-11-21 18:04
特别是,越过一些门槛后
这些效果都接触过,只是达到的手段不同而已。 红影 发表于 2023-11-21 19:55
这些效果都接触过,只是达到的手段不同而已。
既然已经接触了svg,那么,就尽可能系统地接触它。svg有坐标系统以及坐标系统的变换,这回带来更多的可能。 马黑黑 发表于 2023-11-22 07:42
既然已经接触了svg,那么,就尽可能系统地接触它。svg有坐标系统以及坐标系统的变换,这回带来更多的可能 ...
是的,坐标系统的确认还是挺有关系的。 红影 发表于 2023-11-22 10:28
是的,坐标系统的确认还是挺有关系的。
svg这么设计,肯定有其理由 马黑黑 发表于 2023-11-22 12:15
svg这么设计,肯定有其理由
虽然不知道那理由是什么,但跟着做肯定不会有问题{:4_173:} 红影 发表于 2023-11-22 13:53
虽然不知道那理由是什么,但跟着做肯定不会有问题
弄懂理由那要读研才行,现在俺木有时间带烟酒生 马黑黑 发表于 2023-11-22 18:07
弄懂理由那要读研才行,现在俺木有时间带烟酒生
烟酒生都没本事弄懂啊,那我更不敢想了{:4_173:} 红影 发表于 2023-11-22 19:59
烟酒生都没本事弄懂啊,那我更不敢想了
你博士要厉害一点 马黑黑 发表于 2023-11-22 20:51
你博士要厉害一点
算了吧,我这种从来没搏过的人,博什么士啊{:4_173:}