SVG矢量动画

一、概述

相较于png、jpg等位图通过存储像素点来记录图像,svg (Scalable Vector Graphics)拥有一套自己的语法,通过描述的形式来记录图形。Android并不直接使用原始的svg格式图片,而是将其转化为VectorDrawable。VectorDrawable是一个xml格式的drawable,是矢量图在Android中的原始资源,引用方法和png、jpg等其他drawable一样。但是,VectorDrawable仍然只是一个静态图片,和png、jpg等没什么区别,如果想让VectorDrawable “动”起来,就需要用到AnimatedVectorDrawable。AnimatedVectorDrawable才是真正的矢量动效图,其原理就是把VectorDrawable和ObjectAnimator结合起来,通过ObjectAnimator属性动画控制VectorDrawable,利用矢量图形的特性,从而达到各种炫酷的效果。

svg动效开发流程


二、开发流程

我们下面以一个解锁动效为例,来讲解矢量图动效开发的整个流程。

(1)svg到VectorDrawable

svg源文件由美工制作(Sketch、AI等工具均可输出svg),VectorDrawable也可以通过svg2android工具直接由svg转化而来。

网址:http://inloop.github.io/svg2android/

工具:svg2android-gh-pages.zip

本示例我们需要一个锁的矢量图ic_lock_32dp

1  <?xml version="1.0" encoding="utf-8"?> 2 <vector xmlns:android="http://schemas.android.com/apk/res/android" 3 android:width="32dp" 4 android:height="32dp" 5 android:viewportHeight="110" 6 android:viewportWidth="110"> 7 8 <group 9 android:name="g_lock_arc" 10 android:pivotX="38" 11 android:scaleX="1.0"> 12 <path 13 android:name="lock_arc" 14 android:pathData="M71.6,29v-5.4c0-8.9-7.6-16-17-16s-17,7.2-17,16V29" 15 android:strokeColor="#000000" 16 android:strokeLineCap="round" 17 android:strokeLineJoin="round" 18 android:strokeWidth="4" /> 19 </group> 20 21 <path 22 android:name="lock_rect" 23 android:pathData="M39,29h32c5.5,0,10,4.5,10,10v32c0,5.5-4.5,10-10,10H39c-5.5,0-10-4.5-10-10V39C29,33.5,33.5,29,39,29z" 24 android:strokeColor="#000000" 25 android:strokeWidth="4" /> 26 </vector>

矢量图主要是由path和group组成,矢量动画实际上就是通过控制path和group的属性变化来实现动画效果的。

(2)VectorDrawable到AnimatedVectorDrawable

有了矢量图,就可以设计矢量动画了。分析上面的解锁动效,整个解锁过程可拆分为两部分

  1. 上曲线(name="lock_arc")线条收缩
  2. 上曲线组(name="g_lock_arc")整体水平翻转

我们设计矢量动画图avd_lock_to_open如下:

1  <?xml version="1.0" encoding="utf-8"?> 2 <animated-vector 3 xmlns:android="http://schemas.android.com/apk/res/android" 4 android:drawable="@drawable/ic_lock_32dp" ><!--引用的矢量图--> 5 <!--曲线收缩--> 6 <target 7 android:name="lock_arc" 8 android:animation="@anim/anim_lock_to_open_p1" /> 9 <!--曲线组翻转--> 10 <target 11 android:name="g_lock_arc" 12 android:animation="@anim/anim_lock_to_open_p2" /> 13 </animated-vector>

分析上面代码

  • android:drawable指定的是我们将对哪个矢量图做动效
  • target标签的android:name指定要对矢量图的哪个部分做动效
  • target标签的android:animation引用一个objectAnimator,指定要对该部分做什么动画

anim_lock_to_open_p1通过控制trimPathStart属性对path(name="lock_arc")做了收缩:

1  <?xml version="1.0" encoding="utf-8"?> 2 <set xmlns:android="http://schemas.android.com/apk/res/android"> 3 <objectAnimator 4 android:duration="300" 5 android:interpolator="@android:anim/accelerate_interpolator" 6 android:propertyName="trimPathStart" 7 android:startOffset="100" 8 android:valueFrom="0" 9 android:valueTo="0.24" 10 android:valueType="floatType"/> 11 </set>

anim_lock_to_open_p2通过控制scaleX属性对group(name="g_lock_arc")做了水平翻转:

1  <?xml version="1.0" encoding="utf-8"?> 2 <set xmlns:android="http://schemas.android.com/apk/res/android"> 3 <objectAnimator 4 android:duration="400" 5 android:interpolator="@android:anim/linear_interpolator" 6 android:propertyName="scaleX" 7 android:valueFrom="1" 8 android:valueTo="-1" 9 android:startOffset="450" 10 android:valueType="floatType"/> 11 </set>

通过startOffset可以精确控制两个动画的执行顺序。至此,我们已经写好了AnimatedVectorDrawable。

(3)AnimatedVectorDrawable的使用

AnimatedVectorDrawable本身还是一个drawable,和其他图片的引用方式是一样的。这里我们把它设置给ImageView

1  <?xml version="1.0" encoding="utf-8"?> 2 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent"> 5 <ImageView 6 android:id="@+id/img" 7 android:layout_gravity="center" 8 android:layout_width="64dp" 9 android:layout_height="64dp" 10 android:src="@drawable/avd_lock_to_open"/> 11 </FrameLayout>

矢量动态图不会自动播放,在需要播放的时候执行如下代码

1  Drawable drawable = mImageView.getDrawable(); 2 if (drawable instanceof Animatable) { 3 ((Animatable) drawable).start(); 4 }

至此,一个矢量动效的开发和引用已经全部完成了。但有时候我们不想每次都主动调用start方法来播放动效,而是希望根据控件的状态(如state_pressed)改变自动播放相应的过渡动效,这时就需要用到animated-selector了。

(4)Animated-Selector用法

在讲解animated-selector之前,我们先看一下selector的用法,它可以根据控件的不同状态显示不同的drawable,如

1  <?xml version="1.0" encoding="utf-8" ?> 2 <selector xmlns:android="http://schemas.android.com/apk/res/android"> 3 <!-- 默认时的背景图片 --> 4 <item android:drawable="@drawable/pic1" /> 5 <!-- 没有焦点时的背景图片 --> 6 <item android:state_window_focused="false" 7 android:drawable="@drawable/pic2" /> 8 <!--获得焦点时的图片背景 --> 9 <item android:state_focused="true" 10 android:drawable="@drawable/pic3" /> 11 <!--选中时的图片背景 --> 12 <item android:state_selected="true" 13 android:drawable="@drawable/pic4" /> 14 </selector>

但使用selector时,状态切换是瞬间完成的,没有过渡。如果希望在状态切换时增加动效,那就需要用到animated-selector。为了讲解,我们仿照矢量动态图avd_lock_to_open再写一个闭锁的动态图avd_open_to_lock。其中会用到一个矢量图ic_open_32dp和两个objectAnimator:anim_open_to_lock_p1、anim_open_to_lock_p2,代码类似,不再展示。我们直接看animated_selector的写法:

1  <?xml version="1.0" encoding="utf-8"?> 2 <animated-selector xmlns:android="http://schemas.android.com/apk/res/android"> 3 <item 4 android:id="@+id/state_on" 5 android:drawable="@drawable/ic_lock_32dp" 6 android:state_checked="true" /> 7 <item 8 android:id="@+id/state_off" 9 android:drawable="@drawable/ic_open_32dp" /> 10 <transition 11 android:drawable="@drawable/avd_lock_to_open" 12 android:fromId="@id/state_on" 13 android:toId="@id/state_off" /> 14 <transition 15 android:drawable="@drawable/avd_open_to_lock" 16 android:fromId="@id/state_off" 17 android:toId="@id/state_on" /> 18 </animated-selector>

分析上面代码:

  • item标签指定了确定状态下的drawable
  • transition标签指定了状态切换时执行的矢量动效

通常情况下,我们会手动设置控件的状态,以便在适当的时候显示我们想要的动效。比如,我们希望点击ImageView时在两个矢量动效之间切换:

1  boolean isChecked = false; 2 mImageView.setOnClickListener(new View.OnClickListener() { 3 @Override 4 public void onClick(View v) { 5 isChecked = !isChecked; 6 inal int[] stateSet = {isChecked ? android.R.attr.state_checked : 0}; 7 mImageView.setImageState(stateSet, true);//设置state_checked状态 8 } 9 });

使用animated-selector后,我们不再需要主动去调start( )方法来播放动效,只需要设置控件状态即可。

最终效果:

至此,我们已经完成了一套完整的矢量动效开发流程,目录结构如下:

svg开发目录

Github源码:https://github.com/JiaxtHome/AnimDemo


 三、总结

我们已经知道svg动效实际上就是通过控制path或group的属性变化来达到动画效果,下面列出常用的可控属性

svg可控属性

Name

Tags

Comment

strokeAlpha

path

定义路径边框的透明度

strokeWidth

path

定义路径边框的粗细尺寸 

strokeColor

path

定义路径边框的颜色 

 fillColor

path

定义填充路径的颜色 

 fillAlpha

path

定义填充路径的颜色透明度 

 pathData

path

定义路径形状,可用来做morphing动效 

trimPathStart 

path

从路径起始位置截断路径的比率,取值范围从 0 到1 

trimPathEnd 

path

从路径结束位置截断路径的比率,取值范围从 0 到1 

 trimPathOffset

path

设置路径截取的范围,取值范围从0到1 

rotation 

group

定义旋转角度 

pivotX 

group

定义缩放和旋转时的 X 参考点,相对于 viewport 值来指定 

pivotY 

group

定义缩放和旋转时的 Y 参考点,相对于 viewport 值来指定 

 scaleX

group

定义 X 轴的缩放倍数 

 scaleY

group

定义 Y 轴的缩放倍数 

translateX 

group

定义 X 轴方向位移,相对于viewport 值来指定 

translateY 

group

定义 Y 轴方向位移,相对于viewport 值来指定 

上面属性基本可以分为三类,同时也决定了我们能做的动效类型

(1)改变路径的颜色、尺寸、截断

trimPath

(2)路径的形状变化

morphing

(3)旋转、缩放、平移

translation、scale

其中morphing动画是svg动画的亮点,但并不是任意两个图形之间都能变换,需要图形路径的绘制命令相同。我们可以借助vectalign工具让几乎任意两个svg图形之间都能够进行morphing变换。

网址:https://github.com/bonnyfone/vectalign

工具:vectalign-0.2-jar-with-dependencies.jar.zip

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

AndroidStudio封装SDK的那些事

<divclass"markdown\_views"<!flowchart箭头图标勿删<svgxmlns"http://www.w3.org/2000/svg"style"display:none;"<pathstrokelinecap"round"d"M5,00,2.55,5z"id"raphael

mysql设置时区

mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0