什么是 SVG?本项目简单分享动画入门的相关知识,并附有相关代码演示,同时文档对SVG等相关内容有比较详细的叙述,如vector标签中属性所代表的意义解释、path标签所支持的指令解释等等。

简介: 什么是 SVG?本项目简单分享动画入门的相关知识,并附有相关代码演示,同时文档对SVG等相关内容有比较详细的叙述,如vector标签中属性所代表的意义解释、path标签所支持的指令解释等等。

Google 在 Android5.X 中增加了对 SVG 矢量图形的支持,可以用来创建高效率的动画, 所以我们先来了解一下 SVG 的定义


  • 可伸缩矢量图形(Scalable Vector Graphics)
  • 使用 XML 格式定义图形
  • 图像在放大或改变尺寸的情况下图片质量不会有所损失
  • Android 中使用 vector 标签表示 SVG


bitmap 相比,SVG 最大的优点是放大不会失真,而 bitmap 需要为不同的分辨率准备很多套图标,而 SVG 则不需要,前面说了 SVG 要用 vector 表示,我们先来看看 vector 标签中属性的含义。


vector 的各个属性是什么意义?

<vector xmlns:android="http://schemas.android.com/apk/res/android"  //命名空间
    android:height="200dp"  //这个是图片的intrinsic高度
    android:width="200dp"   //这个是图片的intrinsic宽度
    android:viewportHeight="100"    //这个是为这个图片设置的纵坐标,表示将图片分为100等份,主要下面的pathData需要依赖这个坐标的划分
    android:viewportWidth="100"     //同上,只不过这个是横坐标, heigh,width的比例和viewportHeight,viewportWidth的比例必须保持一致,不然图形就会发生形变
    android:alpha="0.2"     //这个是整个图像的透明度,取值范围0到1
    >
    <group      //这个标签中可以放入若干个<pathData/>标签,并给它们设置一些共同的属性
        android:name="group_name"   //这个name很有用,在设置objectAnimator的时候用来区分给那个部分施加动画
        android:pivotY="50"     //这个设置这个group的中心点的X坐标,取值范围为0到100,在做rotation时有用
        android:pivotX="50"     //这个设置这个group的中心点的Y坐标,取值范围为0到100,在做rotation时有用
        android:translateX="20" //将整个group在X轴方向平移多少像素
        android:translateY="30" //将整个group在Y轴方向平移多少像素
        android:rotation="90"   //将整个group以中心点左边旋转的角度,360为一圈
        android:scaleX="0.5"    //横坐标的缩放比例 , 取值1表示100%
        android:scaleY="0.3">   //纵坐标的缩放比例,取值0.5表示50%,取值1.5表示150%
        <pathData   //这个标签是重头戏,矢量图绘制的路径
            android:name="path_name"    //为这个path标记的名字,在使用objectAnimator的时候用来区分给哪个部分施加动画
            android:pathData="m 0,0 L50,0 L100,100 L0,100 z"    //这个是SVG的语法,下面讲
            android:fillColor="@color/red"  //定义填充图形的颜色,如果没有定义则不填充路径
            android:fillAlpha="1"       //定义填充图形的透明度,取值范围0到1
            android:strokeAlpha="0.5"   //定义路径的透明度,取值范围0到1
            android:strokeColor="#ff0000ff" //定义如何绘制路径,如果没有定义则不显示路径
            android:strokeWidth="20"    //线段的宽度
            android:strokeLineCap="butt|round|square"   //线的末端形状,butt严格到指定的坐标就截至,round是圆角,square是方形,到指定的坐标后还会再冒出一点来
            android:strokeLineJoin="round|bevel|miter"  //线的连接处形状,round是圆角的,bevel和miter貌似看不出来有什么区别....
            android:trimPathStart="0.5"    //顾名思义,从path开始的地方(0%)去除path,去除到指定的百分比位置,取值范围0到1
            android:trimPathEnd="0.5"      //顾名思义,从path结束的地方(100%的地方)去除path,去除到指定的百分比位置,取值范围0到1
            android:trimPathOffset="0.5"   //这个属性是和上面两个属性共同使用的,单独使用没有用,这个属性的意思是,在去除path的时候设置path原点的位置,按百分比设置,取值范围0到1
            />
    </group>
</vector>


下面就来讲解 path 标签,path 标签是用来创建 SVG 的,就像用指令控制一只画笔,path 标签所支持的指令有以下几种。

标签的绘图指令

M = moveto(M X, Y): 将画笔移动到指定的位置,但未发生绘制
L = lineto(L X, Y): 画直线到指定位置
H = horizontal(H X): 画水平线到指定X坐标
V = vertical lineto(V Y): 画垂直线到指定Y坐标
C = curveto(C X1,Y1,X1,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 = elliptical Arc(A RX,RY,XROTATION,FLAG1,FLAG2,X,Y): 画弧线
Z = closepath(): 关闭路径,把前面的路径连起来


在使用以上指令时,需要注意:


  1. 坐标轴以(0,0)为中心,X 轴水平向右,Y 轴水平向下
  2. 所有指令大小写均可。大写绝对定位,参考全局坐标系;小写相对定位,参考父容器坐标系
  1. 指令和数据间的空格可以省略,可以用逗号隔开,也可以用空格
  2. 同一指令出现多次可以只用一个


SVG 的指令参数非常复杂,但是在 Android 中,不需要太多太复杂的 SVG 图形,所以我们先来掌握几个常用的指令,在以后的学习中,读者将会慢慢掌握更多的 SVG 绘制技巧和方法。


常用指令

  • M :类似 Android 绘图中 path 类的 moveTo 方法,即将画笔移动到某一点但并没有发生绘制动作,下面配合 L 进行讲解


  • L :画一条直线
1.  <pathData
2.        ...省略一些代码
3.        android:pathData="M 20 50 L 80 50"/>


9ebd83aa720e8f05e5cf5137cd6ac6aa.png


上面表示把画笔放在(20,50)位置,连直线到 80,50 点。 同时 L 后面还可以跟 H 或 V 指令来绘制水平、竖直线,后面的参数是 x 坐标(H 指令)或 y 坐标(V 指令),如下:

<pathData
        ...省略一些代码
        android:pathData="M 20 50 L 80 50 V 80 H 20"/>



4bc3a66adf5a5d8cd49fd0a6c8e4eb21.png

  • A :绘制一段弧线,且弧线不允许闭合,可以把弧线想象成椭圆的某一段,A 指令有以下 7 个参数:
  1. RX,RY 指所在椭圆的半轴大小
  2. XROTATION 指椭圆的 X 轴与水平方向的顺时针方向夹角,可以想象成一个水平的椭圆绕中心点顺时针旋转 XRORATION 的额角度
  3. FLAG1 只有俩个值,1 表示大角度弧线,0 表示小角度弧线
  4. FLAG2 只有俩个值,1 为顺时针,0 反之
  1. X,Y 为终点坐标

看代码:

<pathData
        ...省略一些代码
        android:pathData="
        M 50 50
        a 30 15 0 1 0 1 0"/>


再看图:


c5c7a048ae81d59dc84ff69177615600.png

图一

上面表示把画笔放在(50,50)位置;30, 15 分别表示椭圆的 x,y 半轴大小;0 表示 x 轴不旋转;1 表示用大角度弧线绘制;0 表示顺时针:1,0 表示相对与以(50,50)为起始点的坐标轴的坐标,因为 a 是小写。

再看一段代码:

 <pathData
      ...省略一些代码
      android:pathData="
      M 25 50
      a 25 25 0 1 0 50 0" />

再看图:


b11a489b41a8c59090f6ef538779a0cf.png


图二


可以看到这里显示了一个半圆,因为这里的 X,Y 轴大小相等。

再看一段代码:

 <pathData
       ...省略一些代码
       android:pathData="M 25 50
       a 25 25 0 1 0 40 0" />

再看图:

695b57c31227e287960ce3d07724497a.png


图三

这里把终点 x 轴坐标改为 40,图中显示了圆的大部分。

看一段代码:

 <pathData
        ...省略一些代码
        android:pathData="M 25 50
        a 25 25 0 0 0 40 0" />


再看图:


90cdaf736e27614a4677d83da776cbab.png


图四

这里把 FLAG1 改为 0,与图三相比,发现弧度变小了,因为用小弧度画。

看一段代码:

  <pathData
        ...省略一些代码  
        android:pathData="M 25 50
        a 25 25 0 0 1 40 0" />


再看图:


48aea4fe612c2749892eb0c6b992d476.png


图五

这里把 FLAG2 改为 1,与图四相比,图形翻转了,因为画的方向不一样了,把 A 指令的几个图结合看一下,就能弄懂 A 这个指令了。


关于贝塞尔指令的,这里就不过多介绍了,放出几个链接供大家学习: 贝塞尔曲线初探SVG 讲解


VectorDrawable 和AnimatedVectorDrawable


Coogle 在 Android5.0X 中提供了俩个 API 来帮助支持 SVG:


  • VectorDrawable
  • AnimatedVectorDrawable 其中 VectorDrawable 用于创建 XML 文件的 SVG 图形即前面的 vector 标签,并结合 AnimatedVectorDrawable 来完成动画效果。


1、VectorDrawable


在 XML 中创建一个静态的 XMLSVG 图形,通常会形成如下的树形结构:


5798d7082ae6adebff80d65b3976ce2b.png

树形结构

path 是树形结构中最小的单位,而通过 Group 可以将不同的 path 进行组合,接下来我们使用 vector 标签创建 SVG 图形,代码如下:

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="200dp"
    android:height="200dp"
    android:viewportWidth="100"
    android:viewportHeight="100">
    <group
        android:name="line">
        <pathData
            android:name="path1"
            android:strokeColor="@android:color/holo_green_dark"
            android:strokeWidth="5"
            android:strokeLineCap="round"
            android:pathData="
            M 20 20
            L 50 20 80 20"/>
        <pathData
            android:name="path2"
            android:strokeLineCap="round"
            android:strokeWidth="5"
            android:strokeColor="@android:color/holo_green_dark"
            android:pathData="
            M 20 80
            L 50 80 80 80"/>
    </group>
</vector>



上面的代码画了俩条线,每条线由三个点控制,形成初始状态,下面立马通过 AnimatedVectorDrawable 来实现动画效果。


2、AnimatedVectorDrawable


AnimatedVectorDrawable 就是通过连接静态的 VectorDrawable 和动态的 objectAninmator 来为 VectorDrawable 提供动画效果,分几个步骤来使用:


在 XML 中通过 animated-vector 标签来声明对 AnimatedVectorDrawable 的使用,并指定它的 drawable 属性,target 标签中的 name 属性和 animation 属性 代码如下:


<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/svg_path">
    <target
        android:animation="@animator/anim_path1"
        android:name="path1"/>
    <target
        android:animation="@animator/anim_path2"
        android:name="path2"/>
</animated-vector>

android:drawable="@drawable/svg_path"指定了上面创建的 VectorDrawable 即画的俩条线;target 标签中的 name 指定了要作用动画的 path 或 Group 的 name, 即俩者的 name 要保持一致,这样系统才能找到要实现动画的元素;taret 标签中的 animation 指定了要作用的都动画。 在本例中,path1 的动画代码如下:

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

path2 的动画代码如下;


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

以上的俩个动画代码中都定义了一个 pathType 的属性动画,并指定了变换的初始值分别为:

//path1
 android:valueFrom=
    "M 20 20
     L 50 20 80 20"
//path2
 android:valueFrom=
    "M 20 80
     L 50 80 80 80"


结束值为:

//path1
 android:valueTo=
    "M 20 20
     L 50 50 80 20"
//path2
  android:valueTo=
    "M 20 80
     L 50 50 80 80"

这里要注意的是,SVG 的路径变换属性动画中,变换前后的节点数必须相同,这也是为什么前面需要使用三个点来绘制一条直线,因为后面需要中点进行动画变换。

  • 把 AnimatedVectorDrawable 的 XML 文件设置给 ImageView
 <ImageView
        android:id="@+id/iv_path"
        android:src="@drawable/svg_path_anim"
        .../>


  • 代码中启动 AnimatedVectorDrawable 动画
 ImageView ivPath;
 ...
 ivPath = findViewById(R.id.iv_path);
        ivPath.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Drawable drawable = ivPath.getDrawable();
                if(drawable instanceof Animatable){
                    ((Animatable)drawable).start();
                }
            }
        });



这样俩个 path 就实现了动画效果,如图:


e8640b019d3670e5041742e866e5b16e.gif


在本文源码中还有俩个动画,就不贴代码了



a333d5e9c412899d3d65a35fa5b78123.gif



相关文章
|
JavaScript 前端开发 数据安全/隐私保护
VUE3实现全局水印功能
watermark-js-plus 是一个用于给图片添加水印的 JavaScript 库。它提供了一个简单的方式来在图片上添加文字水印、图片水印或自定义水印。
1013 0
|
7月前
|
人工智能 测试技术 算法框架/工具
《鸿蒙AI开发:第三方库管理与更新全攻略》
在鸿蒙系统人工智能应用开发中,第三方库的管理与更新至关重要。首先需根据项目需求精准选择适配的库,如OpenCV、TensorFlow Lite等,并关注兼容性与社区支持。引入库时可借助ohpm工具,通过指令或配置文件实现便捷管理。版本管理要求明确指定依赖版本,定期更新并充分测试以确保稳定性。更新过程中需谨慎操作,解决依赖冲突,保障应用功能正常运行。整个流程从选择到更新环环相扣,助力开发者构建高效稳定的AI应用。
375 0
|
6月前
|
前端开发 JavaScript 关系型数据库
基于python的租房网站-房屋出租租赁系统(python+django+vue)源码+运行
该项目是基于python/django/vue开发的房屋租赁系统/租房平台,作为本学期的课程作业作品。欢迎大家提出宝贵建议。
193 6
|
6月前
|
JavaScript 前端开发 关系型数据库
基于Python+Vue开发的体育场馆预约管理系统源码+运行
本项目为大学生课程设计作业,采用Python和Vue技术构建了一个体育场馆预约管理系统(实现前后端分离)。系统的主要目标在于帮助学生理解和掌握Python编程知识,同时培养其项目规划和开发能力。参与该项目的学习过程,学生能够在实际操作中锻炼技能,为未来的职业发展奠定良好的基础。
152 3
|
6月前
|
前端开发 JavaScript 关系型数据库
基于Python+Vue开发的新闻管理系统源码+运行
基于Python+Vue开发的新闻管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Python编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Python的新闻管理系统项目,大学生可以在实践中学习和提升自己的能力,为以后的职业发展打下坚实基础。
143 5
|
分布式计算 资源调度 大数据
【决战大数据之巅】:Spark Standalone VS YARN —— 揭秘两大部署模式的恩怨情仇与终极对决!
【8月更文挑战第7天】随着大数据需求的增长,Apache Spark 成为关键框架。本文对比了常见的 Spark Standalone 与 YARN 部署模式。Standalone 作为自带的轻量级集群管理服务,易于设置,适用于小规模或独立部署;而 YARN 作为 Hadoop 的资源管理系统,支持资源的统一管理和调度,更适合大规模生产环境及多框架集成。我们将通过示例代码展示如何在这两种模式下运行 Spark 应用程序。
642 3
|
7月前
|
前端开发 JavaScript
什么是深拷贝;深拷贝和浅拷贝有什么区别;深拷贝和浅拷贝有哪些方法(详解)
浅拷贝适用于只复制对象的第一层属性,且这些属性不是引用类型。深拷贝适用于需要完全独立的副本,包括对象和数组的嵌套结构。选择哪种拷贝方式取决于你的具体需求和场景。 博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
JavaScript 容器
vue element plus Infinite Scroll 无限滚动
vue element plus Infinite Scroll 无限滚动
433 0
|
数据采集 Python
Python正则表达式提取车牌号
Python正则表达式提取车牌号
348 0
|
JavaScript
一文搞懂Vue3中如何使用ref获取元素节点?
一文搞懂Vue3中如何使用ref获取元素节点?
968 1