SVG矢量动画机制

阅读原文

SVG矢量动画机制

  Google在Android 5.X中增加了对 SVG 矢量图形的支持, 这对于创建新的高效率动画具有非常重大的意义。首先,来了解一下什么是SVG.

  • 可伸缩矢最图形 (Scalable Vector Graphics)

  • 定义用于网络的基于矢量的图形
    使川 XML 格式定义图形

  • 图像任放大或改变尺寸的情况下其图形质量不会有所损失

  • 万维网联盟的标准
    · 与诸如 DOM 和 XSL 之类的 W3C 标谁是一个整体

  SVG在Web上的应用非常广泛,在Android5.X之前的Android版本上,可以通过一些第三方开源库来在Android 中使用 SVG. 而在Android 5.X 之后. Android 中添加了对SVG的<path>标签的支持.从而让开发者可以使用SVG 来创建更加下富的动画效果.

那么,SVG对比传统的 Bitmap,究竞有什么好处呢?

  • Bitmap(位图)通过在每个像素点上存储色彩信息来表达图像,而SVG是一个绘阁标淮,与Bitmap对比,SVG最大的优点就是放大不会失真。

  • 而且Bitmap 需要为不同分辨率设计多套 图标, 而矢量图则不需要。


2.1 SVG 中 <path> 标签.

  用<path>标签创建 SVG,就像用指令的方式来控制一只画笔,例如移动画笔到某一坐标位置,画一条线,画一条曲线,结束.<path>标签所支持的指令有以下儿种。

  • M = moveto(M X,Y):将画笔移动到指定的坐标位置,但未发生绘制

  • L = lineto(L X.Y): 両直线到指定的坐标位置

  • H = horizontal lineto(H X): 画水平线到指定的 X 坐标位置

  • V = vertical lineto(V Y):画垂直线到指定的Y坐标位置

  • C = curveto(C XI,YI.X2,Y2,ENDX,ENDY): 三次贝赛曲线

  • S = smooth curveto(S X2,Y2.ENDX,ENDY): 三次贝赛曲线

  • Q = quadratic Belzier curve(Q X.Y.ENDX,ENDY): 二次 贝赛曲线

  • T = smooth quadratic Belzier curveto(T ENDX,ENDY):映射前面路径后的终点

  • A=clliptical Arc(A RX.RY,XROTATION,FLAGI,FLAG2,X,Y): 弧线

  • Z=closepath:关闭路径

在使用上面的指令时,需要注意以下儿点:

  • 坐标轴以(0.0)为中心,X轴水平向右,Y轴水平向下.

  • 所有指令大小写均可.大写绝对定位,参照全局坐标系:小写相对定位,参照父容器坐标系.

  • 指令和数据间的空格可以省略.

  • 同一指令出现多次可以只用一个.


2.2 Android View坐标轴知识补充

image

View的坐标体系是以左上角为坐标原点,向右为X轴正方向,向下为Y轴正方向。

View绘制,主要是通过Android的2D绘图机制来完成,时机是onDraw方法中,其中包括画布Canvas,画笔Paint。

下面给出示例代码。相关API不是介绍的重点,重点是Canvas的save和restore方法,通过save以后可以对画布进行一些放大缩小旋转倾斜等操作,这两个方法一般配套使用,其中save的调用次数可以多于restore。

@Override
     protected void onDraw(Canvas canvas)
     {
           super.onDraw(canvas);
           Bitmap bitmap =  ImageUtils.drawable2Bitmap(mDrawable);
           canvas.drawBitmap(bitmap, getLeft(), getTop(),  mPaint);
           canvas.save();

           // 注意,这里的旋转是指画布的旋转
           canvas.rotate(90);
           mPaint.setColor(Color.parseColor("#FF4081"));
           mPaint.setTextSize(30);
           canvas.drawText("测试", 100, -100, mPaint);
           canvas.restore();
     }

2.3 SVG 常用指令

  • L

  绘制直线的指令是“L”,代表从当前点绘制直线到给定点。“L"之后的参数是一个点坐标,如“L 200 400" 绘制直线。 同时, 还可以使用 “H” 和 “V” 指令来绘制水平, 竖直线,后面的参数是x 坐标 (H指令) 或y坐标 (V指令).

  • M

  M指令类似Android绘图中path 类的 moveTo 方法,即代表将画笔移动到某一点,但并不发生绘制动作. 有关moveTo方法的相关知识戳这里

  • A

  A指令用来绘制一段弧线,且.允许弧线不闭合。可以把A命令绘制的弧线想象成是椭圆的某一段,A指令以下有七个参数。

1)RX,RY指所在椭园的半轴大小.

  1. XROTATION 指椭圆的 X 轴与水平方向顺时针方向夹角, 可以想象成一个水平的椭圆绕中心点顺时针旋转XROTATION的角度。

3)FLAGI只有两个值,1表示大角度弧线,0为小角度弧线.

4)FLAG2只有两个值,确定从起点至终点的方向,1为顺时针,0为逆时针。

5)X,Y轴为终点坐标。

SVG的指令参数非常复杂,但是在Android中,不需要绘制太多太复杂的 SVG图形!


2.4 SVG编辑器

SVG 参数的写法固定而且复杂,因此完全可以使用程序来实现,所以一般通过 SVG编辑器来编辑 SVG 图形。 网上有很多 SVG 的在线编辑器, 通过可视化编辑好图形后, 点击 ViewSource 就可以转换为 SVG 代码!

下载离线的 SVG 编辑器,可以获得更为强大的编辑功能,例如常用的 Inkscape, 就是一个非常优秀的离线SVG编辑器.Inkscape win7 64bit download


2.5 Android 中使用SVG

Google 在Android 5.X 中提供了下面两个新的API来帮助支持SVG:

  • VectorDrawable

  • AnimatedVectorDrawable

其中,VectorDrawable 可以用来创建基于 XML 的 SVG 图形, 并结合Animated VectorDrawable 来实现动画效果.

2.5.1 VectorDrawable

在XML中创建一个静态的SVG图形, 通常会形成如图1 所示的树形结构。

path是SVG树形结构中的最小单位, 而通过Group 可以将不同的path进行组合。

image

           图1 SVG树形结构

2.5.1.2 创建 SVG 图形

首先需要在 drawable 目录下新建一个 vectordrawable.xml 文件 在该xml文件中通过 ****<vector>标签来声明对SVG的使用,代码如下所示。

vectordrawable.xml

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:width="200dp"
        android:height="200dp"
        android:viewportWidth="100"
        android:viewportHeight="100"
        tools:ignore="ExtraText">
</vector>

  其中包含两组宽高属性,height,width 和 viewportHeight, viewportWidth.

这两组属性分别具有不同的含义,height,width表示该SVG图形的具体大小,

viewportHeight,viewportWidth表示SVG图形划分的比例.

后面在绘制 path 时所使用的参数, 就是根据这两个值来进行转换的,比如上面的代码, 将200dp 划分为100份,如果在绘制图形时使用坐标(50.50), 则意味着该坐标位于该SVG图形正中间。

因此,height,width 的比例与viewportHeight,viewportWidth的比例, 要保持一致, 不然图形就会发生压缩, 形变,

2.5.1.3 给 <vector> 标签增加显示path

还是原来的vectordrawable.xml文件,在原来的基础上增加了 <group> 和 <path> 标签,代码如下

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:width="200dp"
        android:height="200dp"
        android:viewportWidth="100"
        android:viewportHeight="100"
        tools:ignore="ExtraText">
    <group
        android:name="test"
        android:rotation="0">
        <path
            android:fillColor="#5A8DDF"
            android:pathData="M 25 50 a 25,25 0 1,0 50,0"
            android:strokeWidth="2"
            tools:ignore="VectorRaster"/>
    </group>
</vector>

其中 pathData 就是绘制 SVG图形所用到的指令。

在这个例子中, 先使用 “M” 指令, 将画笔移动到 (25,50) 这个坐标, 再通过A指令来绘制一个圆弧并填充。

由于A命令的使用非常广泛而目功能强大,所以这里需要仔细对照前文中A指令的参数,来掌握该命令.

由于这里使用了 android:fillColor 属性来绘制图形,因此绘制出来的是一个填充的图形

如果要绘制一个非填充的图形,可以使用以下属性

android:strokeColor="#5A8DDF"
android:strokeWidth="2"

这里需要注意的是,android:fillColor 属性如果使用如下方式引入颜色,则会报错(编译环境是 AndroidStudio3.2 + compileSdkVersion 28)

android:fillColor="@color/colorAccent"

绘制效果如图 2 所示

image

         图2 SVG图形——填充

image

          图2 SVG图形——非填充

2.5.2 AnimatedVectorDrawable

AnimatedVectorDrawable 的作用就是给 VectorDrawable 提供动画效果。 Google 的 工程师将AnimatedVectorDrawablc 比喻为一个胶水, 通过 AnimatedVectorDrawable 来连接静态的VectorDrawable和动态的 objecAnimator

下面来看看如何使用AnimatedVcctorDrawable实现SVG 图形的动画效果。

  • 首先, 在 drawable目录下 新建anim_vector.xml文件

  • 在该文件中通过 <animated-vector>标签来声明对 AnimatedVcctorDrawable 的使用, 并指定其作用的 path 或 group.

anim_vector.xml

<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
                 xmlns:tools="http://schemas.android.com/tools"
                 android:drawable="@drawable/vectordrawable"
                 tools:ignore="NewApi">
    <target
        android:name="test"
        android:animation="@animator/anim_svg1" />
</animated-vector>

其中 drawable属性 对应的即为静态的vectordrawable.xml

android:drawable="@drawable/vectordrawable"

target中的animaton属性对应的即为objectAnimator

android:animation="@animator/anim_svg1" />

通过** AnimatedVectorDrawable 中 target 的 animation 属性**, 将一个动画作用到了对应name
的元素上, objectAnimator 代码如 下

在 animator 目录下新建 anim_svg1.xml文件 代码如 下

<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
            android:duration="500"
            android:interpolator="@android:anim/bounce_interpolator"
            android:propertyName="pathData"
            android:valueFrom="
        M 20,80
        L 50,80 80,80"
            android:valueTo="
        M 20,80
        L 50,50 80,80"
            android:valueType="pathType"/>

最后可以看到, 对动画效果的实现, 还是通过属性动画来实现的, 只是属性稍有不同。

在<group>标签和<path>标签中添加了 rotation,fillColor, pathData 等属性,

那么在objectAnimator中,就可以通过指定android:propertyName="XXXX"属性来选择控制哪一种属性,

通过 android:valueFrom="XXX"和Iandroid:valueTo="XXX"属性,可以控制动画的起始值

唯一需要注意的是,如果指定属性为pathData, 那么需要添加一个属性——android:valueType="path Type"来告诉系统进行 pathData 变换。 类似的情况, 可以使用 rotation 进行旋转动画, 使用 fillColor实现颜色动画, 使用 pathData 进行形状, 位置变化.

有关objectAnimator的知识可以戳这里

2.5.3 使用SVG

当所有的XML文件准备好了以后,就可以在代码中控制SVG 动画,可以非常方便地将一个 Animated VectorDrawable.xml 文件设置给一个 ImageView 作为背景显示.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:tools="http://schemas.android.com/tools"
                android:layout_width="match_parent"
                android:layout_height="match_parent">

    <ImageView
        android:id="@+id/image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:src="@drawable/anim_vector" />

</RelativeLayout>

需要注意的是,AnimatedVectorDrawable 中指定的 target 的 name 属性,必须与VectorDrawable 中需要作用的 name 属性保持一致,这样系统才能找到要实现动画的元素

anim_vector.xmlvectordrawable.xml中的name属性必须保持一致


2.6 SVG动画 三球轨迹Demo

案例效果

image

2.6.1 首先创建一个 vector.xml

在drawable目录下新建一个 vector.xml 文件,它是一个 VectorDrawable 完整代码如下:

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="200dp"
    android:height="200dp"
    android:viewportHeight="100"
    android:viewportWidth="100">
    <group
        android:name="sun"
        android:pivotX="60"
        android:pivotY="50"
        android:rotation="0">

        <path
            android:name="path_sun"
            android:fillColor="#18b4ed"
            android:pathData="
                M 50,50
                a 10,10 0 1,0 20,0
                a 10,10 0 1,0 -20,0" />

        <group
            android:name="earth"
            android:pivotX="75"
            android:pivotY="50"
            android:rotation="0">

            <path
                android:name="path_earth"
                android:fillColor="#FFA500"
                android:pathData="
                    M 70,50
                    a 5,5 0 1,0 10,0
                    a 5,5 0 1,0 -10,0" />

            <group>
                <path
                    android:fillColor="#008000"
                    android:pathData="
                        M 90,50
                        m -5 0
                        a 4,4 0 1,0 8 0
                        a 4,4 0 1,0 -8,0" />
            </group>
        </group>
    </group>
</vector>

2.6.2 接着创建2个 objectAnimator

在animator目录下创建2个文件 由于anim_sun.xmlanim_earth.xml.它们是objectAnimator动画文件,具体代码如下(由于anim_sun.xml 和 anim_earth.xml的代码相同,所以只贴其中一个,也可以根据需求更改):

anim_sun.xml

<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="4000"
    android:propertyName="rotation"
    android:valueFrom="0"
    android:valueTo="360" />

2.6.3 创建1个 AnimatedVectorDrawable

在drawable目录下创建一个 sun_system.xml 文件,它是一个AnimatedVectorDrawable,作用是粘合VectorDrawable(vector.xml)和objectAnimator(anim_sun.xml、anim_earth.xml)

具体代码如下所示:

<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/earth_moon_system">
    <target
        android:name="sun"
        android:animation="@animator/anim_sun" />
    <target
        android:name="earth"
        android:animation="@animator/anim_earth" />
</animated-vector>

注意:target标签中的name属性需要与vector.xml中group标签中的name属性保持一致!


2.6.4 在 activity 中使用SVG

首先创建一个MainActivity,含布局文件activity_main.xml,布局文件中有一个ImageView控件,在控件的src属性中引用上述创建好的sun_system.xml,具体代码如下所示

activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:tools="http://schemas.android.com/tools"
                android:layout_width="match_parent"
                android:layout_height="match_parent">

    <ImageView
        android:id="@+id/image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:src="@drawable/sun_system" />

</RelativeLayout>

除了上述在ImageView控件的src属性中引用之外,还可以通过代码指定的方式引用,具体代码如下:

MainActivity.java

/**
 * author : Hashub小晖
 * email  : hashubng@163.com
 * date   : 2019/1/5 15:40
 * desc   : 三球仪轨迹动画
 */
public class SVGSunSystemActivity extends BaseActivity
{
    @Bind(R.id.image)
    ImageView image;

    @Override
    protected void initData()
    {
        // 通过代码引用
       image.setImageResource(R.drawable.sun_system);
    }

    @Override
    protected int getLayoutId()
    {
        return R.layout.activity_svg_test;
    }


    @OnClick(R.id.image)
    public void onViewClicked()
    {
        // 在ImageView控件的点击事件中开启SVG动画
        ((Animatable) image.getDrawable()).start();
    }
}

运行后就是效果图中的效果!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270