原文地址:http://www.androiddesignpatterns.com/2016/11/introduction-to-icon-animation-techniques.html#drawing-paths
以下为简单翻译,不足之处别见怪,有些关键词没有改动以免误解。
文中带复选框的图片请到原文中查看效果,本文中只是静态图。
创意定制是Material Design的一个原则;添加一些小的图标动画可以给用户带来惊喜的体验,让你的应用更加自然和活泼。但是,从头创建一个图标动画很有挑战性。不仅会有很大的工作量,还需要对最后效果的想像力。如果你不熟悉创建图标动画的常用技术,会遇到相当大的困难。
这篇文章涉及了图标动画的一些技术手段。学习的最好途径就是通过实例,在文章中你会遇到一些交互实例,它们会帮你强化每个用到的技术点。我希望这篇文章能让你窥探到一点图标动画的原理,因为我坚信了解了原理才能创造属于自己的东西。
这篇文章分为下面几节:
1.Drawing Paths
2.Transforming groups of paths
3.Trimming stroked paths
4.Morphing paths
5.Clipping paths
6.Conclusion:putting it all together
这篇文章中的所有图标动画都适用于Github中的AnimatedVectorDrawable对象。
Drawing paths(绘制路径)
在开始创建图标前,首先需要理解他们是如何绘制的。在Android里,我们可以使用VectorDrawable创建新图标。VectorDrawables类似于SVGs:允许我们使用一种叫做Path的线(line)和图形(shape)创建一种可伸缩且与密度无关的对象。每一个path的图形(shape)都是由一系列的绘制指令(drawing commands)组成的。这些指令是SVG path data spec的子集,由一个以空格或逗号分隔的字符串表示。这个说明文档(SVG path data spec)定义了很多不同类型的指令,例如下表:
列表解释:M指令,参数x,y。将画笔移动到指定的坐标位置。
L指令, 参数x,y。绘制一条到(x,y)的直线。
C指令,参数x1,y1;x2,y2;x,y。使用控制点(x1,y1)和(x2,y2)绘制到(x,y)的三次贝塞尔曲线。
Z指令,没有参数。由当前的自路径绘制一条到起点坐标的直线,形成一个闭环。
Path有两种模式:填充(filled)和描边(stroked),如果设置为填充模式,图形的内部都会被画满。如果是描边模式,那画笔就会沿着图形的轮廓绘制。这两种模式都有自己的一套属性,用来进一步进行修改。
接下来我们来看一个应用实例。假设我们想要为一个音乐应用创建一个图标,图标具有播放,暂停,录音三种状态。我们可以为每一个状态设置一个单独的path:
三角形的播放图标和圆形的录音图标是填充(filled)模式,分别用黄色和红色填充。而暂停图标是描边(stroked)模式,描边宽度为2。图形1表明每一个path都是在一个12x12的网格中绘制的:
就像我们前面所说的,VectorDrawables的优点之一就是他的密度无关性,意思就是他可以在不损失视觉质量的情况下在一个设备中随意缩放。这非常实用和高效,开发者不用再去为各种不同密度的屏幕生成各种尺寸的PGN图片了,这样也可以降低apk的包大小。但对我们来说,使用VectorDrawables也是为了要使用AnimatedVectorDrawable来给path做动画。
AnimatedVectorDrawables连接了VectorDrawables和ObjectAnimators:VectorDrawable为每一个path(或path组)分配一个唯一的名字,AnimatedVectorDrawable再把这些名字和相应的ObjectAnimators联系起来。下面我们会发现,使用了VectorDrawable的元素具有很强的动画能力。
Transforming groups of paths(路径的组转换)
上一节中我们学习了如何通过改变path的属性来修改path的显示,例如改变path的透明性和颜色。除此之外,VectorDrawable也支持使用<group>来进行路径组转换,我们可以使用下面这些属性一次性改变多个path:
理解group嵌套组(nested groups)的内部执行顺序非常重要。有两条规则需要记住:(1)子group会继承父group的转换效果,(2)同一个group中的转换会按照缩放,旋转,平移的顺序执行。下面的例子是关于运行,暂停,录制图标的组转换:
图2展示了图标的不同转换状态,点击复选框,可以看到不同组合的转换效果!
这个串联各种group的能力可以实现很多很酷的效果。图3展示了3个例子:
1、这个展开收起图标由两个矩形path组成。点击后,这两个path会同时执行旋转90度和竖直平移(组动画)。
2、这个闹钟图标由两个矩形path组成。点击后,这两个path会沿着圆心来回晃动形成一个响铃的效果。
3、这个广播按钮图标是我最喜欢的,因为它既简单又绚酷。图标由两个path组成,一个填充(filled)的内圆和一个描边(stroked)的外环,当按钮选中的时候,有三个属性同时变换:
注意前三分之一的动画:当外圆的边宽和缩放比例同时增加并分别减小的时候,使他看起来好像外圆坍缩到圆心一样——非常酷的一个效果。
最后说一个‘横向不确定进度条’的组动画。他由三个path组成:一个透明背景和两个内部的矩形path。动画进行时,两个内部path在同时进行着不同频率的横向平移和缩放。点击图4中的复选框,可以看到每个变换的单独效果!
Trimming stroked paths(剪切描边路径)
描边(stroked)path有一个比较少见的功能就是剪切。意思是说给你一个描边path,你可以选定path的部分可见。在Android里,使用下面的属性实现:
trimPathStart 表示path可见部分的起点,trimPathEnd表示可见部分的终点。另外trimPathOffset的值会同时附加到trimPathStart和trimPathEnd上。图5展示了他们是如何工作的——拖动滚动条可以看到不同的效果!注意trimPathStart大于trimPathEnd时的效果;如果这样做的话,可见部分会在末端回折,从开端一直画到起始点。
这三个属性开发出了很多的可能性。图6展示了4个这样的例子:
1、这个指纹图标包括五条描边(stroked)path,path剪切的起点值分别设置为0和1.图标隐藏时,动画快速变到0,此时图标变为不可见,之后需要显示图标时再快速变化到1即可。下面的手写签名图标过程也是类似的。
2、‘搜索返回图标’使用了一个非常聪明的path剪切组合动画,它使搜索图标和返回图标达到了无缝衔接。选中‘show trim paths’你会看到随着trimPathStart和trimPathEnd的变化,path的剪切是如何进行的。选中‘slow animation’你会看到随着时间的变化path的可见长度也随之变化:圆圈开始慢慢展开然后收缩,这样就创建了一个非常好非常自然的‘拉伸’效果。这个效果其实非常简单:只需要在剪切效果的开始设置一点点延迟,使结尾动画看起来比其他部分更快一点就行啦。
3、数字图标动画中的每一个数字都是由四个path组成,每一个path由一种描边(stroke)颜色构成,并且path剪切(trim)的起点到终点的长度都是数字总长度的四分之一。为了实现效果,每一个path的trimPathOffset都会从0慢慢变到1.
最后,图7展示了使用描边(stroked)path剪切实现‘圆圈进度条’效果。这个图标由一个单独的,圆形描边path组成,如下:
1、其中<group>指明了一个效果:圆形进度条会在4444毫秒内旋转720度。
2、这个进度条path的剪切位移在1333毫秒内从0到0.25.
3、进度条会在1333毫秒内以下面的值来剪切:
当t=0.0,进度条处于最小尺寸(只有3%可见)。当t=0.5,进度条拉伸到最大尺寸(75%可见)。当t=1.0,进度条又被缩小到最小尺寸,然后再重新开始。
Morphing paths(路径渐变)
在本文中最好的图标动画当属path渐变了。现在只支持5.0及以上系统,通过指定android:pathData属性是两个path做到无缝连接动画。使用path渐变,能够把加号变成减号,把运行图标变成暂停图标,或者把‘更多图标’变成返回图标,就像图8中展示的。
要实现一个path渐变动画第一件要考虑的事情就是你想要做渐变的path是否符合条件。为了将path A渐变到path B,需要满足下面的条件:
1、A和B需要有相同数目的绘制指令。
2、A的第N条指令要和B的第N条指令模式相同。
3、A的第N条指令要和B的第N条指令有相同数目的参数。
其中任何一条不满足(比如试图将L指令渐变成C指令,或者将一个有2个坐标的指令渐变成一个有4个坐标的指令等等),都会引发一个崩溃异常。因为path渐变动画的实现是不透明的,所以这些规则必须严格遵守。动画开始的时候,框架会取得指令的模式,再由path的android:pathData属性获取坐标值。如果上面的限制条件都符合,框架就会把两个path中的坐标值替换到指令字符串中。之后框架就会在每个新的显示帧上执行相同的绘制指令,根据当前的动画进度重新计算所使用的坐标值。图8很好的阐明了这个概念。首先禁止‘循环播放(animate rotation)’,然后选中‘显示坐标(show path coordinates)’和‘减慢动画(slow animation)’按钮。在动画过程中注意每个path的红点坐标是如何运动的:他们由path A的起点经过一条直线到达path B的终点。path渐变动画真的非常简单!
尽管概念很简单,但path渐变动画有时也会非常繁琐和耗时。举例来说,我们为了使两个path的转换过程更加流畅经常需要手动的变化path的起点和终点。其中大部分工作都花费在要根据复杂度把哪一个点放到哪个地方。下面列出几点提示和技巧:
1、操作一个简单路径和一个复杂路径的时候增加虚拟坐标通常是必要的。图8中的所有例子都添加了虚拟坐标。例如,加号变减号的动画。我们只用4个绘制指令去绘制一个减号的长方形path。然而,绘制一个加号需要12个绘制指令,所以为了使两个path相符合必须在减号path上增加8个空白指令。比较两个path的绘制指令字符串,看看你能不能找出那些空白指令!
2、通过设置path的起点和终点作为控制点,就可以用一个贝塞尔曲线绘制一条直线。也可能使用一个或多个三次贝塞尔曲线判断椭圆指令,我会在这里讨论这些。
3、有时你在做渐变的时候效果会不太好。以我的经验,添加一个180度或360度的旋转动画,这样会使动画看起来有明显改善:增加旋转可以转移眼睛对渐变的注意力并且增加一个触摸层也能对用户的触碰做反应。
4、记住path渐变动画从根本上是视每个path绘制指令坐标的相对位置而决定的。为了得到更好的效果,试着去减小动画过程中每个坐标的间距:间距越小,渐变越流畅。
Clipping paths(路径切割)
我要介绍的最后一个动画限定符是<clip-path>。path切割的约束区域会被应用到画布上——约束区域外的任何部分都不会被绘制。通过对这些区域做动画,我们会得到一些非常酷的效果,接下来就会看到。
<clip-path>也可以使用android:pathData来做渐变。通过图9中的例子来可以看到这些动画是如何运行的。选中‘show clip paths’复选框就会显示一个红色的覆盖层,用来显示当前处于活动状态的<clip-path>部分,这样就能让他的<path>部分绘制。剪切path对于‘填充’效果特别有用,下面的沙漏和‘心形填充裂开效果’就能说明。
Conclusion:putting it all together(总结)
如果你已经读完了上面的内容,那就意味着你已经有了创建图标动画的基础!为了庆祝一下,让我们用最后一个例子来完结这一大篇文章!考虑图10中的进度图标,他的动画有下面六种属性:
1、Fill alpha(最后下载箭头淡出)。
2、Stroke width(进度条运行过程中可以转换成对勾)。
3、Translation and rotation(在动画开始的时候创建一个‘箭头跳动’的效果)。
4、Trim path start/end(在结束时把进度条变为对)。
5、Path morphing(在开始时创建一个‘横线跳动’效果,在结束时把对勾转换到箭头)。
6、Clip path(竖向填充箭头的内容来指示进度)。
这些就是我所知道的全部了。。。感谢阅读!在这篇文章中所有的图标动画都可以免费使用,在Github上还有更多的AnimatedVectorDrawable格式的图标动画。
扩展阅读:Vector图像刚发布的时候,是只支持Android 5.0+的,对于Android pre-L的系统来说,并不能使用,所以,可以说那时候的Vector并没有什么卵用。不过自从AppCompat 23.2之后,Google对p-View的Android系统也进行了兼容,也就是说,Vector可以使用于Android 2.1以上的所有系统,只需要引用com.android.support:appcompat-v7:23.2.0以上的版本就可以了,这时候,Vector应该算是迎来了它的春天。(引自:http://blog.csdn.net/u010335298/article/details/51840805)。