如何用Framer一步步实现加载动画

为Apple Watch设计APP的时候,首要的准则是让它保持简单。Uber 为 Apple Watch设计的APP是简单的最好的例子,原因在于它只有3个页面:

1.给Uber发送需求

2.等待回复

3.让你知道司机的信息和状态

说到动画,这个 APP每个界面都有不同的加载动画,这些动画原型都非常有趣,下面将用 framer实现这些动画。

你自己可以跟着我一步步用 framer实现这个原型,下图是将要实现的效果:

43505768cd360000018c1b1b372c.jpg


要开始,先下载 Sketch文件(在附件中),在开始前我已经用 Sketch自带的 Apple Watch GUI 在不同画板之间创建了我们需要的界面,当我们把 Sketch文件导入 framer的时候,只有被编组的界面元素才会被 framer识别到,这里要特别注意图层的组织。

86105768cd510000012e7e11bb30.jpg

在这个例子中,作为第一个屏幕,需要把加载的圆形和 “Request”按钮分别编组,因为只有这两个元素需要添加交互和动画,现在,我们的Sketch文件已经准备好了,让我们导入到framer。


我们要做的第一件事是清除代码编辑器,并将您的设备切换为苹果手表。工具栏的右上角去Device  -   Apple Watch 42mm -  选择其中一个选项。

8c465768cd700000012e7ebacefe.jpg

保持Sketch处于打开状态,在 framer中点击工具栏上的导入按钮或者用快捷键 Command+I唤出导入对话框,确认对话框中的 Sketch列表后面的 import按钮处于激活状态,点击导入按钮导入。

ab7b5768cd8b0000012e7eff7d10.jpg

framer会自动为导入的原型添加注释和代码行,我们可以重命名导入文件的名称使它变简短,例如这里重命名为Uber。


# This imports all the layers for "uber-watch" into

uber= framer.importer.load "imported/uber-watch"


我们应该在右侧的预览窗口中看到request界面。我们也可以在中间的检查面板中查看导入的其他的两个界面(以淡一点的颜色显示),因为它们是不可见的。

8ecd5768cdb50000018c1bfe13e0.jpg

要创建加载脉动,我们将创建两个动画对象。第一个动画用到 loadingCircle图层scale 属性增大10%。如果要访问我们导入的任何图层对象,我们只需输入导入时的文件变量,后面加点,然后加图层组名。按照这种方式,我们将使用uber.loadingcircle指定动画的层。定义过度时间为1.5秒的脉冲动画。


pulseUp = new Animation

layer: uber.loadingCircle

properties:

scale: 1.1

time: 1.5


第二个动画是把放大的 loadingCircle图层,恢复到原来的状态,同样是 1.5秒。


pulseDown = new Animation

layer: uber.loadingCircle

properties:

scale: 1.0

time: 1.5


下面将这两种动画串联起来使得第一种状态结束时自动开始下一种状态。

为了串联动画,我们会监听 animationend事件,这个事件的意思当一种状态结束时会被调用。要监听事件,我们将制定动画对象上面的 “on”关键字来监听这个事件。

当pulseUp动画结束时,我们希望 pulseDown动画开始,用动画对象的 start()函数,因为这是一个函数,所以要在后面加括号。


pulseUp.on Events.AnimationEnd, ->

pulseDown.start()


反过来,当 pulseDown动画结束时,我们希望 pulseUp开始。

pulseDown.on Events.AnimationEnd, ->

pulseUp.start()


需要做的只有开始动画了。

pulseUp.start()


我们现在应该可以看到脉冲动画了。

d9245768ce2f0000018c1b1341f2.jpg


为了切换到下一个界面,要给 requestButton图层创建一个点击事件。

requestButton.on Events.Click, ->


当导入Sketch文件,仅仅第一个画板界面能看到,为了显示出 Requesting界面,需要设置它的 visible属性为 true,另外为了隐藏 Request界面,需要设置它的 visible属性为 false。


uber.requestButton.on Events.Click, ->

uber.request.visible = false

uber.requesting.visible = true


如果仔细观察原型,你会发现过度不平滑,因为仅仅是界面瞬间的隐藏和显示。我们可以做的更好,在 requesting完全显示之前, 通过设置 Request界面的透明度的过度动画为 0.3,然后设置 requesting的透明度过度到 1。与其创建另一个动画对象,并告诉它开始,倒不如我们可以直接在事件里面通过图层名称.animate启动的图层的动画,然后指定过度动画的属性。


uber.requestButton.on Events.Click, ->

uber.requesting.opacity = .3

uber.request.visible = false

uber.requesting.visible = true

uber.requesting.animate

properties:

opacity: 1


现在原型的效果如下:

715a5768ce650000012e7e38dd2c.jpg

在真实的Apple Watch中,点击 request按钮后就是展示加载界面,最后才是显示下一个界面。

在Requesting界面中一致播放脉动加载条的动画直到有司机确认,但是这里有一个回到 request界面的取消按钮。这里我们不处理取消按钮的事件,但是你可以自己练习着完成它。

为了创建这个脉动加载条,先设置加载条的开始位置,这个加载条就是在 Sketch文件中命名为 requestingLoad的图层组,这里设置开始位置通过 scaleX属性为 0.1且它的不透明度为 0。


uber.requestingLoad.scaleX = .1

uber.requestingLoad.opacity = 0


为加载条创建动画对象,我们想要它从初始状态过度到原始的状态,且伴随不透明度的增加。这里要通过 repeat选项,重复动画几次。


requestingPulse= new Animation

layer: uber.requestingLoad

properties:

opacity: .8

scaleX: 1

repeat: 5


现在回到第一屏的 request按钮的点击事件,切换到过度界面


uber.requestButton.on Events.Click, ->

uber.requesting.opacity = .3

uber.request.visible = false

uber.requesting.visible = true

uber.requesting.animate

properties:

opacity: 1

requestingPulse.start()


现在来添加一个延迟直到想我们希望看到的一样。


requestingPulse= new Animation

layer: uber.requestingLoad

properties:

opacity: .8

scaleX: 1

delay: .4

time: .9

repeat: 5


这里Uber的 requesting界面的效果如下:

d5bb5768ceee0000018c1b3caccd.jpg

为了过度到下一个界面,需要设定脉冲加载动画的重复次数。每一次 requestingpulse动画结束的时候触发的animationend 事件,是添加逻辑最好的地方。

首先创建一个 requestingCount变量,它的作用是记录脉冲次数,设置初始值为0。我们把它放在animationend事件代码块外面,而不是在里面,因为如果把它里面将每次重置为0,结果是一个无限循环。


requestingCount = 0


在AnimationEnd事件中,它被调用一次增加计数 1次,这里我们设置变量等于它本身加 1即可。


requestingPulse.on Events.AnimationEnd, ->

requestingCount = requestingCount + 1


一种简单的写法是在变量后面带两个加号,效果和上面是一样的。


requestingPulse.on Events.AnimationEnd, ->

requestingCount++


在计算次数等于希望的动画重复播放次数,添加切换到下一个界面的逻辑,这里我们设定希望动画重复的次数为 3。


requestingPulse.on Events.AnimationEnd, ->

requestingCount++

if requestingCount is 3


当计算次数为 3的时候,我们要做和 Request界面切换到 Requesting界面一样的切换效果来切换到下一个界面。首先设置下一个界面( enroute)的不透明度为 0.3, requesting图层的 visible的属性为 false, enroute界面的 visible的属性为 true,在让透明度的过度更平滑一些。


requestingPulse.on Events.AnimationEnd, ->

requestingCount++

if requestingCount is 3

uber.enroute.opacity = .3

uber.requesting.visible = false

uber.enroute.visible = true

uber.enroute.animate

properties:

opacity: 1


代替计算次数的做法,你也可以选用在 requesting界面经过几秒之后自动切换到下一个界面(提示:使用 Utils.delay 来做)。记住这是一个原型,所以它不必是完美的,所有选用那种方式更快或更好才是关键。

效果如下:

ea685768cf520000012e7e72879f.jpg

对于Uber的erneute界面,在Sketch文件中命名为 enRouteProgress进度条是有旋转动画的。创建旋转动画设置旋转角度为360度, 给旋转动画添加AnimationEnd事件监听,使旋转结束继续开始下一次旋转,重复次数可以多一些,也可以设定旋转一圈所用的时间。


enRouteRotate = new Animation

layer: uber.enRouteProgress

properties:

rotation: 360

repeat: 30

time: 2


可以在前面提到的切换到第三个屏幕的的时候开始播放这个动画。


requestingPulse.on Events.AnimationEnd, ->

requestingCount++

if requestingCount is 3

uber.enroute.opacity = .3

uber.requesting.visible = false

uber.enroute.visible = true

uber.enroute.animate

properties:

opacity: 1

enRouteRotate.start()


如果你观察原型,会发现不是很平滑。


这是因为动画曲线默认是 “ease-in-out”曲线,我们设置动画的曲线为 linear。

enRouteRotate= new Animation

layer: uber.enRouteProgress

properties:

rotation: 360

repeat: 30

curve: "linear"

time: 2


现在看起来已经平滑多了:

eb585768cf850000012e7e6667f0.jpg


这个屏幕如果可以滑动应该还有更多内容。如果你观察 Sketch中的这个画板,你会看到这样的结构,可以滚动的内容被加上了蒙板。

dfd05768cfd10000012e7edf4fb9.jpg

添加滚动事件是通过在 Sketch中命名为 content的组上创建 ScrollComponent 的wrap 函数来完成的。


scroll= ScrollComponent.wrap uber.content


这里禁用水平方向滚动


scroll.scrollHorizontal = false


现在原型中的第三个界面应该能滚动了。

39125768cfec0000018c1b20c83c.jpg


为了让这个 framer原型更具真实,现在来做汽车的移动。


创建一个小汽车在一条路径上通过转弯到另一条路径。可以通过改变 x, y的坐标实现移动,通过转动角度来变化路径,让它看起来像沿着路径运动。


moveCarDown= new Animation

layer: uber.delorean

properties:

x: 130

y: 95

time: 20


moveCarLeft= new Animation

layer: uber.delorean

properties:

rotation: -100

x: 125

y: 125

time: 10


通过监听moveCarDown动画的结束事件来开始 moveCarLeft动画建立一个动画序列。


moveCarDown.on Events.AnimationEnd, ->

moveCarLeft.start()


这里需要在开始 moveCarDown 动画开始的时候,开始 enRouteRotate动画


requestingPulse.on Events.AnimationEnd, ->

requestingCount++

if requestingCount is 3

uber.enroute.opacity = .3

uber.requesting.visible = false

uber.enroute.visible = true

uber.enroute.animate

properties:

opacity: 1

enRouteRotate.start()

moveCarDown.start()


小汽车已经沿着路径移动了,为了展示,只制作了简短的 gif动画避免gif过大,效果如下:

1bff5768d02d0000018c1b0690de.jpg

最后,在5秒之后更新标签 ““En Route”为“Arriving Now” ,这里用延时函数 delay 函数实现,它的意思是在操作执行之前等待几秒。


moveCarDown.start()

Utils.delay 5, ->


在5秒之后,隐藏当前的标签显示需要展示的标签,这里都用到了 visible属性。


Utils.delay 5, ->

uber.enRouteTitle.visible = false

uber.arrivingNowTitle.visible = true


恭喜您,我们在 framer中已经实现he Uber Apple Watch app的效果了。

0ec25768d0530000012e7e860fea.jpg