第四个案例,加载效果
最终效果首先我们需要梳理一下加载的过程,用静态界面标识出来,所谓磨刀不误砍柴工。
静态效果思路整理:
- 点击按钮
- 按钮变色
- 文字放大并缩小
- 按钮变化样式缩小
- 按钮开始加载
- 加载完成,按钮变化样式还原
- 结束图标出现
思路清楚后,接下来就是用 Framer 逐一的实现了。
点击按钮
我们先把需要的元素用 Framer 画出来,有按钮,按钮上的文字,最后出现符号。把所有动画写完以后,点击按钮触发动画。
# Define variables
width = 240 * 2
height = 48 * 2
green = '#3BE0A5'
grey = '#CECBD4'
# 设置背景色
Screen.backgroundColor = "#fafafa"
# 设置默认动画参数
Framer.Defaults.Animation =
curve: Bezier.easeIn
time: .3
# 画按钮
button = new Layer
x: Align.center
y: Align.center
height: 48 * 2
width: 240 * 2
borderColor: green
borderWidth: 2
borderRadius: 100
backgroundColor: null
# 记录初始 x 值
buttonOriginX = button.x
# 画文字
text = new TextLayer
parent: button
text: 'SUBMIT'
fontSize: 32
fontFamily: 'Roboto'
color: green
x: Align.center
y: Align.center
originY: 1
# 画符号
symbol = new TextLayer
parent: button
text: '✓'
fontSize: 50
fontFamily: 'Roboto'
color: '#fff'
x: Align.center
y: Align.center
opacity: 0
注意:
- 这里记录一下按钮的初始 x 位置,因为变换长宽是以(0,0)为固定点的。按钮缩小时,需要同时移动 x 坐标让按钮视觉上看上出是以 (center, center) 为固定点。
按钮变色
按钮变色给用户点击提供反馈,同时边框消失。
# 按钮变色
buttonChangeColor = new Animation button,
borderWidth: 0
backgroundColor: green
options:
time: .2
文字放大并缩小
按钮变色的同时文字放大,提供 Anticipation, 放大后文字再缩小消失。
# 文字放大并缩小
textScaleUp = new Animation text,
scale: 1.1
options:
time: .2
delay: .1
textScaleDown = new Animation text,
scale: 0
opacity: 0
options:
time: .2
按钮变换样式缩小
按钮变成原型,背景色消失,边框变粗且变成灰色。
# 按钮变换样式缩小
buttonChangeStyle = new Animation button,
borderWidth: 8
borderColor: grey
backgroundColor: 'rgba(59,224,165,0)'
width: button.height
borderRadius: '50%'
x: buttonOriginX + (width - button.height) / 2
option:
time:.4
delay: .2
注意:
-
x: (width - button.height) / 2
记录的是中心偏移的距离,即 x 需要增加的距离
按钮开始加载
这是最复杂的一步,这也是我喜欢 Framer 的原因。当你用 AE 实现一个看似很简单的功能,开发可能需要很长的时间才能实现。这时作为设计师,你就需要权衡项目进度能否允许复杂动效的开发。
使用了 Svg 的 stroke-dashoffset, stroke-dasharray 来模拟实现圆的加载。同时使用 Utils.modulate() 映射一个特定的 Bezire 函数效果。推荐一个调节 Bizire 曲线的好网站,cubic-bezire.
# 按钮开始加载
# svg 中即将用到的属性
radius = (96-8) / 2
border = 8
viewBox = (radius * 2) + border
# buttonBorder 用 svg 绘制
buttonBorder = new Layer
x: Align.center
y: Align.center
width: viewBox
height: viewBox
rotation: -90
backgroundColor: null
buttonBorder.pathLength = 2 * Math.PI * radius
buttonBorder.html = "
<svg viewBox='#{-border/2} #{-border/2} #{viewBox} #{viewBox}'>
<circle fill='none'
stroke='#{green}'
stroke-width = '#{border}'
stroke-dasharray = '#{buttonBorder.pathLength}'
stroke-dashoffset = '#{buttonBorder.pathLength}'
cx = '#{radius}'
cy = '#{radius}'
r = '#{radius}'>
</svg>"
buttonBorder.path = document.querySelector('svg circle')
# buttonBorder 的运动
proxy = new Layer
x: 1
visible: false
proxy.on 'change:x', ->
offset = Utils.modulate(@.x, [0, 200], [buttonBorder.pathLength, 0])
buttonBorder.path.setAttribute 'stroke-dashoffset', offset
buttonBorderAnimation = new Animation proxy,
x: 200
options:
time: 1.5
# 自定义 Bezire 函数
curve: Bezier(.9,.1,.83,.67)
注意:
- 实际做的时候,我花了很长事件把两个圆环对齐,一开始不理解为什么对不齐。后来发现,Framer 在添加 border 时,border 是包含在总 width 内的,即 border 宽度不影响该元素的总宽度。但是 svg 矢量画圆的时候,是以 border 的中点开始画的。所以相比原图层,会偏移 border / 2 的宽度。所以 Svg 图像的原点需要偏移 (- borderWidth/2, borderWidth/2)进行对齐。
加载完成,按钮变化样式还原
按钮边框消失,宽度和圆角还原,背景色出现。
# 加载完成,按钮变化样式还原
buttonChangeBig = new Animation button,
borderWidth: 0
width: width
x: buttonOriginX
borderRadius: 100
backgroundColor: green
options:
delay: .1
time: .3
curve: Bezier.easeInOut
注意:
- 同理,x 坐标还原到初始状态
结束图标出现
结束图标出现,opacity 和 scale 从 0 到 1,同时加一个 Spring 曲线增加趣味性。
# 结束图标出现
symbolAppear = new Animation symbol,
opacity: 1
scale: 1
options:
delay: .1
time: .2
curve: Spring(damping: .6)
动画执行
将之前的动画思路,用代码按顺序执行。
# 点击按钮事件,动画开始
button.on Events.Click, ->
# 背景色变化
buttonChangeColor.start()
# 文字变颜色,并放大
text.color = '#fff'
textScaleUp.start()
# 文字放大结束后,文字变小,按钮缩小
textScaleUp.on Events.AnimationEnd, ->
textScaleDown.start()
buttonChangeStyle.start()
# 按钮缩小结束后,边框开始加载
buttonChangeStyle.on Events.AnimationEnd, ->
buttonBorderAnimation.start()
# 边框加载结束后,按钮开始变大
buttonBorderAnimation.on Events.AnimationEnd, ->
buttonBorder.destroy()
buttonChangeBig.start()
# 按钮变大结束后,符号出现
buttonChangeBig.on Events.AnimationEnd, ->
symbolAppear.start()
这是一个不断调试的过程,每一个动画的 time, delay, curve 三个参数都需要反复调整直到产生一个满意的视觉效果。我会先凭经验输入一个大概的值,在根据实际情况进行微调。你可以调的时候画一个 timeline 作为草稿。
Timeline小结
四个案例后,笔者对一般的效果已经有了一定了解。但是深感自己在 Web Animation 底层知识上的薄弱。比如:动画创意,动效节奏,Svg, Canvas 的 JS 实现。所以笔者接下来会多了解 Web Animation 方面的理论知识,为做更好的动效打下基础。我们下期见。
Reference
Framer 线上演示:https://framer.cloud/jvcnt
完整代码
# Coded by Joey in April, 2017
# 导入 Roboto 字体
Utils.insertCSS("@import 'https://fonts.googleapis.com/css?family=Roboto:300,400,700';")
# Define variables
width = 240 * 2
height = 48 * 2
green = '#3BE0A5'
grey = '#CECBD4'
# 设置背景色
Screen.backgroundColor = "#fafafa"
# 设置默认动画参数
Framer.Defaults.Animation =
curve: Bezier.easeIn
time: .3
# 画按钮
button = new Layer
x: Align.center
y: Align.center
height: 48 * 2
width: 240 * 2
borderColor: green
borderWidth: 2
borderRadius: 100
backgroundColor: null
# 记录初始 x 值
buttonOriginX = button.x
# 画文字
text = new TextLayer
parent: button
text: 'SUBMIT'
fontSize: 32
fontFamily: 'Roboto'
color: green
x: Align.center
y: Align.center
originY: 1
# 画符号
symbol = new TextLayer
parent: button
text: '✓'
fontSize: 50
fontFamily: 'Roboto'
color: '#fff'
x: Align.center
y: Align.center
opacity: 0
scale: 0
# 按钮变色
buttonChangeColor = new Animation button,
borderWidth: 0
backgroundColor: green
options:
time: .2
# 文字放大并缩小
textScaleUp = new Animation text,
scale: 1.1
options:
time: .2
delay: .1
textScaleDown = new Animation text,
scale: 0
opacity: 0
options:
time: .2
# 按钮变换样式缩小
buttonChangeStyle = new Animation button,
borderWidth: 8
borderColor: grey
backgroundColor: 'rgba(59,224,165,0)'
width: button.height
borderRadius: '50%'
x: buttonOriginX + (width - button.height) / 2
option:
time:.4
delay: .2
# 按钮开始加载
# svg 中即将用到的属性
radius = (96-8) / 2
border = 8
viewBox = (radius * 2) + border
# buttonBorder 用 svg 绘制
buttonBorder = new Layer
x: Align.center
y: Align.center
width: viewBox
height: viewBox
rotation: -90
backgroundColor: null
buttonBorder.pathLength = 2 * Math.PI * radius
buttonBorder.html = "
<svg viewBox='#{-border/2} #{-border/2} #{viewBox} #{viewBox}'>
<circle fill='none'
stroke='#{green}'
stroke-width = '#{border}'
stroke-dasharray = '#{buttonBorder.pathLength}'
stroke-dashoffset = '#{buttonBorder.pathLength}'
cx = '#{radius}'
cy = '#{radius}'
r = '#{radius}'>
</svg>"
buttonBorder.path = document.querySelector('svg circle')
# buttonBorder 的运动
proxy = new Layer
x: 1
visible: false
proxy.on 'change:x', ->
offset = Utils.modulate(@.x, [0, 200], [buttonBorder.pathLength, 0])
buttonBorder.path.setAttribute 'stroke-dashoffset', offset
buttonBorderAnimation = new Animation proxy,
x: 200
options:
time: 1.5
# 自定义 Bezire 函数
curve: Bezier(.9,.1,.83,.67)
# 加载完成,按钮变化样式还原
buttonChangeBig = new Animation button,
borderWidth: 0
width: width
x: buttonOriginX
borderRadius: 100
backgroundColor: green
options:
delay: .1
time: .3
curve: Bezier.easeInOut
# 结束图标出现
symbolAppear = new Animation symbol,
opacity: 1
scale: 1
options:
delay: .1
time: .2
curve: Spring(damping: .6)
# 点击按钮事件,动画开始
button.on Events.Click, ->
# 背景色变化
buttonChangeColor.start()
# 文字变颜色,并放大
text.color = '#fff'
textScaleUp.start()
# 文字放大结束后,文字变小,按钮缩小
textScaleUp.on Events.AnimationEnd, ->
textScaleDown.start()
buttonChangeStyle.start()
# 按钮缩小结束后,边框开始加载
buttonChangeStyle.on Events.AnimationEnd, ->
buttonBorderAnimation.start()
# 边框加载结束后,按钮开始变大
buttonBorderAnimation.on Events.AnimationEnd, ->
buttonBorder.destroy()
buttonChangeBig.start()
# 按钮变大结束后,符号出现
buttonChangeBig.on Events.AnimationEnd, ->
symbolAppear.start()
网友评论