svg之矢量(属性)动画

时间:2022-11-21 12:48:09

效果图

svg之矢量(属性)动画

首先,矢量图对比png的优点有很多,比如放大不变形,占用内存小等等特点。

那么废话不多说,我们看看怎么由svg做成最后的属性动画。

大体步骤为:
svg --> xml -->动画

首先我们需要svg图片,途径有很多什么UI给切图啊,阿里矢量图库啊,或者也可以自己生成,这边就不多说。得到SVG图片后,我们需要把它转化成XML,这边转化成XML后我们就可以像用png图片的那样用了。

下面网址是用来把SVG 转XML的:
github

得到的xml如下:

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="90dp" android:height="90dp" android:alpha="1.0" android:viewportWidth="1024" android:viewportHeight="1024">
    <path  android:fillColor="#ffffff" android:pathData="M709.193,868.145 A18.261333,18.261333,0,0,1,699.88,834.121 C967.619,676.047,864.525,424.667,860.002,414.049 A18.297905,18.297905,0,0,1,888.43,392.74 L980.663,468.358 A18.285714,18.285714,0,1,1,957.477,496.64 L910.093,457.789 C928.805,551.643,933.023,738.926,718.471,865.609 C715.545,867.328,712.364,868.145,709.194,868.145 Z M144.628,660.76 C140.507,660.76,136.399,659.383,133.035,656.616 L40.7893,580.998 A18.285714,18.285714,0,1,1,63.9756,552.716 L111.372,591.579 C92.6352,497.712,88.4295,310.43,302.982,183.759 A18.285714,18.285714,0,1,1,321.572,215.247 C53.8453,373.309,156.928,624.689,161.463,635.319 A18.285714,18.285714,0,0,1,144.628,660.76 Z M558.665,917.382 C552.021,917.382,545.451,916.285,539.112,914.139 L234.74,810.252 A59.830857,59.830857,0,0,1,200.082,779.739 A59.428571,59.428571,0,0,1,197.278,733.939 L392.569,167.984 A60.257524,60.257524,0,0,1,469.089,130.706 L773.473,234.581 A59.757714,59.757714,0,0,1,808.118,265.082 A59.343238,59.343238,0,0,1,810.922,310.894 L615.643,876.861 A60.257524,60.257524,0,0,1,558.665,917.382 Z M449.56,164.035 C439.43,164.035,430.421,170.411,427.142,179.919 L231.851,745.886 C229.827,751.762,230.217,758.076,232.948,763.672 C235.715,769.341,240.555,773.583,246.565,775.643 L550.937,879.518 C563.201,883.687,576.964,876.873,581.084,864.926 L776.375,298.971 A23.442286,23.442286,0,0,0,761.685,269.202 L457.301,165.315 A24.478476,24.478476,0,0,0,449.56,164.035 Z" />
</vector>

这是效果图中那个两个相同图片的xml文件。
当然我们也可把fillColorpathData都写到values文件下方,看起来会更好看一点。

android:width="90dp" 图片宽
android:height="90dp" 图片高
android:viewportWidth="1024" 内容显示宽
android:viewportHeight="1024" 内容显示高

图片的宽高自然不用说,可以自己DIY的,那么内容显示的宽高呢——这边表示你图片的内容区域要画出来多少。
举个例子:
你有一张照片,图片宽高的大小变化自然是放大缩小,整个图片还在,而当你把viewportWidth缩一半时,就相当于把照片从中间减去了一半,及内容去掉了一半。

我们有了xml现在就开始设计动画。

首先准备工作是在res里面建一个叫 anim 文件,之后的相关动画都可以放到这个文件下面,建好之后我们就开始。

位移动画

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator  android:interpolator="@android:anim/accelerate_interpolator" android:duration="1000" android:propertyName="translateY" android:valueFrom="0" android:valueTo="-60" />
    <objectAnimator  android:startOffset="500" android:duration="1000" android:propertyName="translateY" android:valueFrom="60" android:valueTo="0" />
</set>

这个就是上面看到的飞机飞过的那个,由两个部分,一个是飞出一个是飞入,从代码的单词解释上我们也明白个大概。

下面着重说一下 interpolator 这个属性:

效果 代码中函数 XML中字串
越来越快 AccelerateInterpolator() @android:anim/accelerate_interpolator
越来越慢 DecelerateInterpolator() @android:anim/decelerate_interpolator
先快后慢 AccelerateDecelerateInterpolator() @android:anim/accelerate_decelerate_interpolator
先后退一小步然后向前加速 AnticipateInterpolator() @android:anim/anticipate_interpolator
快速到达终点超出一小步然后回到终点 OvershootInterpolator() @android:anim/overshoot_interpolator
到达终点超出一小步然后回到终点 AnticipateOvershootInterpolator() @android:anim/anticipate_overshoot_interpolator
弹球效果,弹几下回到终点 BounceInterpolator() @android:anim/bounce_interpolator
均匀速度 LinearInterpolator() @android:anim/linear_interpolator

颜色渐变

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="sequentially">
    <objectAnimator  android:duration="1000" android:propertyName="fillColor" android:repeatMode="reverse" android:repeatCount="1" android:valueTo="#ffffff" android:valueFrom="#d27033" android:valueType="colorType" />

    <objectAnimator  android:duration="1000" android:propertyName="fillColor" android:repeatMode="reverse" android:repeatCount="1" android:valueTo="#000000" android:valueFrom="#ff00ff" android:valueType="colorType"/>
</set>

说一下其中的两个属性:

android:repeatMode="reverse" 重复模式-->restart:从开始处重复 reverse:镜像重复
android:repeatCount="1" 重复次数--> 可指定次数 infinite:一直重复
另外,我们发现在<set>标签里面有ordering 这个属性,表示动画顺序,里面有一下两个参数
sequentially :按顺序执行
together:一起执行
除上述属性外还可设置时间等,根据具体需求可自行设定

旋转

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator android:duration="1" android:propertyName="pivotX" android:valueTo="512"/>
    <objectAnimator android:duration="1" android:propertyName="pivotY" android:valueTo="512"/>
    <objectAnimator android:duration="800" android:propertyName="rotation" android:valueTo="0" android:valueFrom="360"/>
</set>

这边需要的注意的是valueTo后面等于多少
我们可以看到在这边分别对X,Y设置了数字,我们可以把它理解为旋转中心,前面提到内容区域的大小就是它的宽和高。
比如我选图的内容区域为1024×1024的那么要想围绕旋转中心旋转,则选点为(512,512)。

到这边我们把素材和动画都准备好了,那么现在就开始把这些串联起来。

在drawable新建一个xml用来表示动画的图片,比如airplane_anim_transition.xml,我们看看这里面需要怎样写。(这边举飞机的那个列子)

飞机的xml–>airplane.xml

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:name="root" android:width="90dp" android:height="90dp" android:alpha="1.0" android:viewportHeight="62" android:viewportWidth="62">

     <!--<clip-path-->
        <!--android:name="mask"-->
        <!--android:pathData="@string/qs_anim_path_mask_disable" />-->
    <!--<group android:name="cross">-->
        <!--<path-->
            <!--android:name="cross_1"-->
            <!--android:fillColor="@color/qs_tile_tint_inactive"-->
            <!--android:pathData="@string/qs_anim_path_cross_disable" />-->
    <!--</group>-->

    <group android:name="airplane_mode">
        <path  android:name="body" android:fillColor="#ffffff" android:pathData="@string/airplane_path" />
    </group>
</vector>

airplane_anim_transition.xml

<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" android:drawable="@drawable/airplane" android:ordering="sequentially">
    <target  android:animation="@anim/color_transfor_anim" android:name="body" />
    <target  android:animation="@anim/translate_y" android:name="airplane_mode" />
</animated-vector>

首先建出来的animated-vector标签是必须要有drawable来匹配的,这表示此动画的资源文件,将来资源都是要从这边拿的。
接着含有 target 的便签就表示一个动画的,那么一个animated-vector就可以有多种动画,那么在animated-vector里面也是可以设置各种形式的,这个可以自行添加。

下面说一下**target**这个需要注意的地方。
android:animation="你想要的动画"
android:name="作用到你资源中的哪个部分"

比如上面的我想让飞机变色,那么就给他飞机的path上加一个name然后在target的name中写入相同的就ok。
那么,我们还看到在资源文件中加了group这个标签,这是因为做位移动画需要把你整个的内容path用group包起来。
(一般有:rotation,pivot(X,Y),scale,translate(X,Y))

上面这是一个整体的动画,我们也可以把一个矢量图分成几个部分分别做动画。下面面的xml为开头的那个xml的分开形式。即如图
svg之矢量(属性)动画

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="90dp" android:height="90dp" android:alpha="1.0" android:viewportWidth="1024" android:viewportHeight="1024">

    <group android:name="ss1">
        <path  android:name="s1" android:fillColor="#ffffff" android:pathData="M709.193,868.145 A18.261333,18.261333,0,0,1,699.88,834.121 C967.619,676.047,864.525,424.667,860.002,414.049 A18.297905,18.297905,0,0,1,888.43,392.74 L980.663,468.358 A18.285714,18.285714,0,1,1,957.477,496.64 L910.093,457.789 C928.805,551.643,933.023,738.926,718.471,865.609 C715.545,867.328,712.364,868.145,709.194,868.145 Z "/>
    </group>

    <group android:name="ss2">
        <path  android:name="s2" android:fillColor="#ffffff" android:pathData="M144.628,660.76 C140.507,660.76,136.399,659.383,133.035,656.616 L40.7893,580.998 A18.285714,18.285714,0,1,1,63.9756,552.716 L111.372,591.579 C92.6352,497.712,88.4295,310.43,302.982,183.759 A18.285714,18.285714,0,1,1,321.572,215.247 C53.8453,373.309,156.928,624.689,161.463,635.319 A18.285714,18.285714,0,0,1,144.628,660.76 Z "/>
    </group>

    <group android:name="ss3">
        <path  android:name="s3" android:fillColor="#ffffff" android:pathData="M558.665,917.382 C552.021,917.382,545.451,916.285,539.112,914.139 L234.74,810.252 A59.830857,59.830857,0,0,1,200.082,779.739 A59.428571,59.428571,0,0,1,197.278,733.939 L392.569,167.984 A60.257524,60.257524,0,0,1,469.089,130.706 L773.473,234.581 A59.757714,59.757714,0,0,1,808.118,265.082 A59.343238,59.343238,0,0,1,810.922,310.894 L615.643,876.861 A60.257524,60.257524,0,0,1,558.665,917.382 Z M449.56,164.035 C439.43,164.035,430.421,170.411,427.142,179.919 L231.851,745.886 C229.827,751.762,230.217,758.076,232.948,763.672 C235.715,769.341,240.555,773.583,246.565,775.643 L550.937,879.518 C563.201,883.687,576.964,876.873,581.084,864.926 L776.375,298.971 A23.442286,23.442286,0,0,0,761.685,269.202 L457.301,165.315 A24.478476,24.478476,0,0,0,449.56,164.035 Z" />
   </group>
</vector>

能够看出来我们把这个图分为了三个部分,前两个path为图中左右两个箭头,最后一个path为中间的矩形框。我们也可以分别给个部分内容做动画效果。大家就可以随意发挥了。

那么我们怎么把一个整体的图形分成多个path呢?

android的pathData中数据是这样的规律
M=MoveTo(M X,Y) 将画线移动到指定坐标
L=lineTo(L X,Y) 画直线到指定坐标位置
Z=ClosePath()   关闭路径

由此我们就可看出在pathData中每遇到Z就可认为是一段内容的结束。同时也可以解释为什么前文你把viewportWidth缩小就会是内容的损失而不是缩小,因为这边当要移动到某个大于viewportWidth的坐标的话是不就到画布外面了(我们可以把viewportWidth理解为画布的宽,另一个自然为其高)

另外path标签里面还有几个特殊属性

trimPathStart:从路径起始点开始向后裁剪的相对距离,可取值0/1,0:路径起始点,1:路径结束点,可实现路径动画
trimPathEnd:从路径结束点开始先前裁剪 取值同上
trimPathOffset:从开始点和结束点裁剪相对距离,取值0/1,0:不裁剪 1:该路径完全不绘制

也可在path标签上包裹<clip-path>
此标签标识遮罩路径,蒙层路径,即只有在其范围内才会绘制,作用范围在当前group标签内

最后一步就是把动画开起来。很简单,如下:

public void startAnim(View view) {
        Drawable airDw = getDrawable(R.drawable.airplane_anim_transition);
        airplane.setImageDrawable(airDw);
        Drawable rotatDw = getDrawable(R.drawable.rotation_anim_transition);
        rotation.setImageDrawable(rotatDw);
        Drawable otherDw = getDrawable(R.drawable.rotation_other_anim_transition);
        rotationOther.setImageDrawable(otherDw);
        if (airDw instanceof Animatable) {
            ((Animatable) airDw).start();
        }
        if (rotatDw instanceof Animatable) {
            ((Animatable) rotatDw).start();
        }
        if (otherDw instanceof Animatable) {
            ((Animatable) otherDw).start();
        }
    }

其中startAnim是点击事件,airplane、rotation、rotationOther为ImageView。当然你也可以对这些动画设置监听事件

到此就结束了!!!!

下面为Demo的路径,调整不了为0积分,没办法啦!!!
https://download.csdn.net/download/qq_33717425/10479131