动画

第 17 章:圆环菜单的动画效果

2016-05-15  本文已影响73人  sing_crystal

原文链接
作者:C4 开源项目
译者:Crystal Sun
全部章节请关注此文集C4教程翻译
校对后的内容请看这里

尽管我已经把这章从上一章节里分出来了,我们还是要在 MenuRings.swift 文件里进行开发。运行应用,确保你看到的应用效果如下图:

如果不是这个效果,回到前一章节里,确保所有的变量设置正确,特别是菜单里的每条线、每个内环的变量值。

厚指环的动画效果

对每个组件我们都会设置自己的进出效果和转换效果。对于厚指环的动画效果来说,我们先在类里添加下面两个变量:

var thickRingOut : ViewAnimation?
var thickRingIn : ViewAnimation?

现在,创建一个方法,写代码:

func createThickRingAnimations() {
    thickRingOut = ViewAnimation(duration: 0.5) {
        self.thickRing?.frame = self.thickRingFrames[1]
    }
    thickRingOut?.curve = .EaseOut
    
    thickRingIn = ViewAnimation(duration: 0.5) {
        self.thickRing?.frame = self.thickRingFrames[0]
    }
    thickRingIn?.curve = .EaseOut
}

这个方法设置了两个动画,第一个 out 设置的是厚指环的新 frame,告诉 shape 更新自己的路径,强迫 shape 重画路径直到符合 frame。第二个则与 out 相反。

让我们测试一下。

创建下面这两个方法:

func animOut() {
    delay(1.0) {
        self.thickRingOut?.animate()
    }
    delay(2.0) {
        self.animIn()
    }
}

func animIn() {
    delay(1.0) {
        self.thickRingIn?.animate()
    }
    delay(2.0) {
        self.animOut()
    }
}

接着在 setup 里输入下列代码:

self.createThickRingAnimations()
animOut()

得到了这样的效果:

薄指环的动画效果(出)

先给我们的薄指环创建动画效果,和之前的步骤有些不同,因为我们需要协调五条线,之前只是一条线,所以我们使用循环体来创建,存储到数组中。

在类里增加下面两个变量:

var thinRingsOut : ViewAnimationSequence?

接着,增加下列方法:

func createThinRingsOutAnimations() {
    var animationArray = [ViewAnimation]()
    for i in 0..<self.thinRings.count-1 {
        let anim = ViewAnimation(duration: 0.075 + Double(i) * 0.01) {
            let circle = self.thinRings[i]
            //每个动画都大于第一个
            if (i > 0) {
                //把指环的透明度调整到 1.0
                ViewAnimation(duration: 0.0375) {
                    circle.opacity = 1.0
                    }.animate()
            }
            
            circle.frame = self.thinRingFrames[i+1]
        }
        anim.curve = .EaseOut
        animationArray.append(anim)
    }
    thinRingsOut = ViewAnimationSequence(animations: animationArray)
}

这个创建了一系列动画变量,基在很多章节之前设置的目标 frame 的值来填充动画。

这个方法创建了一个动画数组,然后运行循环:

  1. 抓住每个瘦指环,从小到大
  2. 创建动画,持续时间基于当前的 index(0.075 + Double(i) + 0.01)
  3. 检查每一行的 index 是否大于零,比如,不是内圆
  4. 对于任何一个非内圆,创建一个不透明的动画,执行后会立即消失
  5. 抓住当前目标的 frame,也就是当前 index + 1
  6. 设置当前圆环的 frame,调用 updatePath
  7. 设置动画的曲线为 .EaseOut
  8. 把动画添加到数组里

循环结束后,创建完成 thinRingsOut 序列。

薄指环动画效果(入)

回来的动画和出的动画非常相似,仅仅有几处地方不同。

在类里增加下列变量:

var thinRingsIn : ViewAnimationSequence?

增加下列方法:

func createThinRingsInAnimations() {
    var animationArray = [ViewAnimation]()
    for i in 1...self.thinRings.count {
        let anim = ViewAnimation(duration: 0.075 + Double(i) * 0.01, animations: { () -> Void in
            let circle = self.thinRings[self.thinRings.count - i]
            if self.thinRings.count - i > 0 {
                ViewAnimation(duration: 0.0375) {
                    circle.opacity = 0.0
                    }.animate()
            }
            circle.frame = self.thinRingFrames[self.thinRings.count - i]
        })
        anim.curve = .EaseOut
        animationArray.append(anim)
    }
    thinRingsIn = ViewAnimationSequence(animations: animationArray)
}

不同之处在于:

  1. 这里的数学计算表示循环体和出的效果正好相反:self.thinRings.count - 1
  2. 透明度调整为 0.0
  3. 由于我们总是想获取数组“之前”的数字(比如 -1),所以我们的循环执行如下:for i in 1...self.thinRings.count

虚线环的动画效果

要完成这些动画,还需要创建两个新的动画变量:

var revealDashedRings : ViewAnimation?
var hideDashedRings : ViewAnimation?

增加下列方法:

func createDashedRingAnimations() {
    revealDashedRings = ViewAnimation(duration: 0.25) {
        self.dashedRings[0].lineWidth = 4
        self.dashedRings[1].lineWidth = 12
    }
    revealDashedRings?.curve = .EaseOut
    
    hideDashedRings = ViewAnimation(duration: 0.25) {
        self.dashedRings[0].lineWidth = 0
        self.dashedRings[1].lineWidth = 0
    }
    hideDashedRings?.curve = .EaseOut
}

查看一下效果

想要看出、入效果,需要修改 animOutanimIn 方法,如下:

func animOut() {
    delay(1.0) {
        self.thickRingOut?.animate()
        self.thinRingsOut?.animate()
        self.revealDashedRings?.animate()
    }
    delay(2.0) {
        self.animIn()
    }
}

func animIn() {
    delay(1.0) {
        self.thickRingIn?.animate()
        self.thinRingsIn?.animate()
        self.hideDashedRings?.animate()
    }
    delay(2.0) {
        self.animOut()
    }
}

在 setup() 里调用 animOut():

createThinRingsOutAnimations()
createThinRingsInAnimations()
createDashedRingAnimations()

运行,效果如下图:

估计现在你已经注意到了,指环和竖线的调速有些问题,不用担心,在最后把所有的组件组合到一起后,会处理这个问题的。

快要到达终点了。

分割线

分割线的动画效果比较简单,除了 Jake 的要求,它们出现的顺序是随机的。翻译一下就是:每次线出现的动画和顺序都是要随机的,要和之前的有所不同。

我是这样解决这个问题的:

func revealHideDividingLines(target: Double) {
    var indices = [0,1,2,3,4,5,6,7,8,9,10,11]
    
    for i in 0...11 {
        delay(0.05*Double(i)) {
            let randomIndex = random(below: indices.count)
            let index = indices[randomIndex]
            
            ViewAnimation(duration: 0.1) {
                self.menuDividingLines[index].strokeEnd = target
            }.animate()
            
            indices.removeAtIndex(randomIndex)
        }
    }
}

把这些添加到类里。

这个方法做了下面这些事情:

  1. 获取一个目标(应该是 0.0 或者 1.0
  2. 创建 index 数组
  3. 运行循环,执行 12 次
  4. 每次遍历都从

接着,测试一下效果:

func animOut() {
    delay(1.0) {
        self.thickRingOut?.animate()
        self.thinRingsOut?.animate()
    }

    delay(1.5) {
        self.revealHideDividingLines(1.0)
    }

    delay(2.5) {
        self.animIn()
    }

}

func animIn() {
    delay(0.25) {
        self.revealHideDividingLines(0.0)
    }

    delay(1.0) {
        self.thickRingIn?.animate()
        self.thinRingsIn?.animate()
    }
    delay(2.0) {
        self.animOut()
    }
}

效果如下:

打扫房子

现在,不在调用 self.animOUt() 方法,删掉两个动画方法。

接着创建两个新方法:

func createRingsLines() {
    createThickRing()
    createThinRings()
    createDashedRings()
    createMenuDividingLines()
}

func createRingsLinesAnimations() {
    createThickRingAnimations()
    createThinRingsOutAnimations()
    createThinRingsInAnimations()
    createDashedRingAnimations()
}

更新 setup 方法如下:

public override func setup() {
  canvas.backgroundColor = clear
  canvas.frame = Rect(0,0,80,80)
  createRingsLines()
  createRingsLinesAnimations()
}

最后,在类里再添加一个变量:

var menuIsVisible = false

这最后一个变量我们在之后的章节中会用到。

MenuRings.swift 源文件。

本文由 SwiftGG 翻译组翻译,已经获得作者翻译授权。

上一篇下一篇

猜你喜欢

热点阅读