原文鏈接
作者:C4 開源項(xiàng)目
譯者:Crystal Sun
全部章節(jié)請(qǐng)關(guān)注此文集C4教程翻譯
校對(duì)后的內(nèi)容請(qǐng)看這里
接下來港粱,想把圖標(biāo)添加到菜單里坎拐,然后實(shí)現(xiàn)動(dòng)畫效果束铭。圖標(biāo)的設(shè)計(jì)理念非常直接:一開始像是一個(gè)點(diǎn)炭分,慢慢變大蒋院,移動(dòng)到最終的位置亏钩,出現(xiàn)全部的形狀,如下圖:
1. 定位
這部分有技巧的地方不是動(dòng)畫和移動(dòng)效果欺旧,這些都簡(jiǎn)單姑丑,而是每個(gè)姓朱的位置,這樣才能一開始是個(gè)點(diǎn)辞友,慢慢轉(zhuǎn)變成全部的形狀秕磷。有一些方法可以實(shí)現(xiàn)這種效果:
- 創(chuàng)建一個(gè)點(diǎn)和一個(gè)形狀(shape)卸夕,一直隱藏形狀知道點(diǎn)移動(dòng)到自己的位置,隱藏點(diǎn),顯示形狀敌呈。
- 創(chuàng)建一個(gè)點(diǎn)诈闺,讓這個(gè)點(diǎn)成為形狀的一部分煤辨,移動(dòng)點(diǎn)到某個(gè)位置竞滓,然后把點(diǎn)轉(zhuǎn)變成完全的形狀。
- 有一個(gè)點(diǎn)疫向,但不是真的點(diǎn)咳蔚,只是看起來像是一個(gè)點(diǎn),設(shè)置形狀的
strokeEnd
在右邊搔驼,形狀的lineCap
是.Round
屹篓。
我們使用的是第三種方法 strokeEnd
來管理圖標(biāo)的動(dòng)畫。然而匙奴,問題也出現(xiàn)了堆巧,就是每個(gè)形狀的“初始”點(diǎn)各不同 —— 我們需要知道每個(gè)形狀,在點(diǎn)狀態(tài)下或者在全部出現(xiàn)狀態(tài)下的偏移量
下面是摩羯座的圖標(biāo)泼菌,開始點(diǎn)已經(jīng)高亮標(biāo)注出了:
開始點(diǎn)的位置是 {30.0,12.2}
谍肤,對(duì)應(yīng)的 frame 是 {0.750,0.387}
。
創(chuàng)建一個(gè)“點(diǎn)”的效果哗伯,我們只需要給每個(gè)圖標(biāo)設(shè)置:
shape.strokeEnd = 0.001
shape.lineCap = .Round
如果我們用圖標(biāo)的中心點(diǎn)定位荒揣,結(jié)束和開始狀態(tài)下的菜單看起來會(huì)是下圖這樣:
如果我們使用第一個(gè)點(diǎn)作為形狀的 anchorPoint
,我們會(huì)得到下面這樣的效果:
我們的想法是焊刹,在結(jié)束狀態(tài)下使用 anchorPoint
系任,另外一個(gè)狀態(tài)使用 center
恳蹲。不過,如果我們不停的轉(zhuǎn)換位置俩滥,實(shí)現(xiàn)起來會(huì)很復(fù)雜嘉蕾。所以我們必須使用 anchorPoint
計(jì)算實(shí)際的 center
,使用這個(gè) center
在不同的狀態(tài)下來?yè)Q轉(zhuǎn)換霜旧。
開始行動(dòng)吧错忱。
2. 獲取圖標(biāo)
第一步就是從符號(hào)庫(kù)里獲取圖標(biāo),更新圖標(biāo)的錨點(diǎn)挂据。
打開 MenuIcons.swift
文件以清,在類里添加下列代碼:
func taurus() -> Shape {
let shape = AstrologicalSignProvider.sharedInstance.taurus().shape
shape.anchorPoint = Point()
return shape
}
func aries() -> Shape {
let shape = AstrologicalSignProvider.sharedInstance.aries().shape
shape.anchorPoint = Point(0.0777,0.536)
return shape
}
func gemini() -> Shape {
let shape = AstrologicalSignProvider.sharedInstance.gemini().shape
shape.anchorPoint = Point(0.996,0.0)
return shape
}
func cancer() -> Shape {
let shape = AstrologicalSignProvider.sharedInstance.cancer().shape
shape.anchorPoint = Point(0.0,0.275)
return shape
}
func leo() -> Shape {
let shape = AstrologicalSignProvider.sharedInstance.leo().shape
shape.anchorPoint = Point(0.379,0.636)
return shape
}
func virgo() -> Shape {
let shape = AstrologicalSignProvider.sharedInstance.virgo().shape
shape.anchorPoint = Point(0.750,0.387)
return shape
}
func libra() -> Shape {
let shape = AstrologicalSignProvider.sharedInstance.libra().shape
shape.anchorPoint = Point(1.00,0.559)
return shape
}
func pisces() -> Shape {
let shape = AstrologicalSignProvider.sharedInstance.pisces().shape
shape.anchorPoint = Point(0.099,0.004)
return shape
}
func aquarius() -> Shape {
let shape = AstrologicalSignProvider.sharedInstance.aquarius().shape
shape.anchorPoint = Point(0.0,0.263)
return shape
}
func sagittarius() -> Shape {
let shape = AstrologicalSignProvider.sharedInstance.sagittarius().shape
shape.anchorPoint = Point(1.0,0.349)
return shape
}
func capricorn() -> Shape {
let shape = AstrologicalSignProvider.sharedInstance.capricorn().shape
shape.anchorPoint = Point(0.288,0.663)
return shape
}
func scorpio() -> Shape {
let shape = AstrologicalSignProvider.sharedInstance.scorpio().shape
shape.anchorPoint = Point(0.255,0.775)
return shape
}
每個(gè)方法都從符號(hào)庫(kù)里獲取了圖標(biāo),設(shè)置錨點(diǎn)為圖標(biāo)的開始點(diǎn)崎逃。我們不需要知道符號(hào)的其他信息了(比如:大/小 點(diǎn)和線等等)掷倔。所以我們只需返回修改過錨點(diǎn)的形狀。
接下來个绍,我們還想能存儲(chǔ)使用的符號(hào)的副本勒葱,之后我們會(huì)對(duì)這些符號(hào)進(jìn)行操作。所以障贸,創(chuàng)建一個(gè)形狀詞典變量來存儲(chǔ)符號(hào)和方法,這些方法會(huì)給符號(hào)創(chuàng)建格子的風(fēng)格:
var signIcons : [String:Shape]!
接著吟宦,把下列方法添加到類里:
func createSignIcons() {
signIcons = [String:Shape]()
signIcons["aries"] = aries()
signIcons["taurus"] = taurus()
signIcons["gemini"] = gemini()
signIcons["cancer"] = cancer()
signIcons["leo"] = leo()
signIcons["virgo"] = virgo()
signIcons["libra"] = libra()
signIcons["scorpio"] = scorpio()
signIcons["sagittarius"] = sagittarius()
signIcons["capricorn"] = capricorn()
signIcons["aquarius"] = aquarius()
signIcons["pisces"] = pisces()
for shape in [Shape](self.signIcons.values) {
shape.strokeEnd = 0.001 //in combination with the next two settings
shape.lineCap = .Round //strokeEnd 0.001 makes a round dot at
shape.lineJoin = .Round //the beginning of the shape's path
shape.transform = Transform.makeScale(0.64, 0.64, 1.0)
shape.lineWidth = 2
shape.strokeColor = white
shape.fillColor = clear
}
}
我們從庫(kù)里拿出來的符號(hào)還是原始符號(hào)篮洁,我們需要縮小一下,如果我們不進(jìn)行這行操作:hape.transform = Transform.makeScale(0.64, 0.64, 1.0)
殃姓,符號(hào)的形狀就會(huì)超級(jí)大袁波,如下圖:
3. 目標(biāo)位置
接下來,我們需要計(jì)算每個(gè)形狀的目標(biāo)位置蜗侈,存儲(chǔ)到兩個(gè)數(shù)組中篷牌,在類里添加下面的代碼:
var innerTargets : [Point]!
var outerTargets : [Point]!
接著添加下列方法:
func positionSignIcons() {
innerTargets = [Point]()
let provider = AstrologicalSignProvider.sharedInstance
let r = 10.5
let dx = canvas.center.x
let dy = canvas.center.y
for i in 0..<provider.order.count {
let ? = M_PI/6 * Double(i)
let name = provider.order[i]
if let sign = signIcons[name] {
sign.center = Point(r * cos(?) + dx, r * sin(?) + dy)
canvas.add(sign)
sign.anchorPoint = Point(0.5,0.5)
innerTargets.append(sign.center)
}
}
outerTargets = [Point]()
for i in 0..<provider.order.count {
let r = 129.0
let ? = M_PI/6 * Double(i) + M_PI/12.0
outerTargets.append(Point(r * cos(?) + dx, r * sin(?) + dy))
}
}
上述過程使用了以下概念:
- 創(chuàng)建形狀,通過
anchorPoint
方法來給每個(gè)形狀設(shè)置起始點(diǎn) - 調(diào)用
shape.center
實(shí)際上返回的是形狀的位置anchorPoint
踏幻,相對(duì)于每個(gè)形狀的superview
- 調(diào)整
anchorPoint
到形狀的中心(例如{0.5,0.5}
)枷颊,不會(huì)改變形狀的位置 - 重置
anchorPoint
后,我們能夠獲取形狀實(shí)際的中心位置该面,作為目標(biāo)
最后夭苗,在 createSignIcons
的最后,給所有的符號(hào)都設(shè)置了風(fēng)格后隔缀,添加下列代碼:
positionSignIcons()
現(xiàn)在题造,讓我們看一下如何將這些排列在一起。
3.1 檢查一下
將菜單的 backgroundColor
值改成 C4Purple
猾瘸,創(chuàng)建符號(hào)圖標(biāo)界赔,setup()
方法應(yīng)該像下面這樣:
public override func setup() {
canvas.backgroundColor = COSMOSbkgd
createSignIcons()
}
到 WorkSpace
里丢习,更新 setup()
方法,如下:
override func setup() {
canvas.add(MenuIcons().canvas)
}
運(yùn)行程序淮悼,效果如下:
4 圖標(biāo)的動(dòng)畫效果
有四個(gè)不同的動(dòng)畫需要?jiǎng)?chuàng)建:位置的出/入咐低,形狀的出現(xiàn)/隱藏。所以敛惊,創(chuàng)建四個(gè)動(dòng)畫變量:
var signIconsOut : ViewAnimation!
var signIconsIn : ViewAnimation!
var revealSignIcons : ViewAnimation!
var hideSignIcons : ViewAnimation!
增加下列方法:
func createSignIconAnimations() {
}
需要四個(gè)動(dòng)畫是由于設(shè)計(jì)原因渊鞋,的吧是線在出入時(shí)不同的方向有不同的順序。
出:移動(dòng)瞧挤,出現(xiàn)锡宋;入:隱藏,移動(dòng)特恬。
由于模式正好相反执俩,當(dāng)動(dòng)畫開始后,我們需要獲取單獨(dú)的動(dòng)畫來應(yīng)對(duì)移動(dòng)和隱藏癌刽,形成序列役首。在方法里添加如下內(nèi)容:
revealSignIcons = ViewAnimation(duration: 0.5) {
for sign in [Shape](self.signIcons.values) {
sign.strokeEnd = 1.0
}
}
revealSignIcons?.curve = .EaseOut
hideSignIcons = ViewAnimation(duration: 0.5) {
for sign in [Shape](self.signIcons.values) {
sign.strokeEnd = 0.001
}
}
hideSignIcons?.curve = .EaseOut
需要標(biāo)注一下:
- 將每個(gè)動(dòng)畫分開,更容易在每一步進(jìn)行自定義
- 能夠明確每個(gè)方向的持續(xù)時(shí)間
- 把 stroke 設(shè)置回
0.001
显拜,保存點(diǎn)
現(xiàn)在衡奥,把下列代碼添加到 createSignIconAnimations
:
signIconsOut = ViewAnimation(duration: 0.33) {
for i in 0..<AstrologicalSignProvider.sharedInstance.order.count {
let name = AstrologicalSignProvider.sharedInstance.order[i]
if let sign = self.signIcons[name] {
sign.center = self.outerTargets[i]
}
}
}
signIconsOut?.curve = .EaseOut
//把圖標(biāo)移動(dòng)到結(jié)束位置
signIconsIn = ViewAnimation(duration: 0.33) {
for i in 0..<AstrologicalSignProvider.sharedInstance.order.count {
let name = AstrologicalSignProvider.sharedInstance.order[i]
if let sign = self.signIcons[name] {
sign.center = self.innerTargets[i]
}
}
}
signIconsIn?.curve = .EaseOut
我們?cè)谶@里獲取了圖標(biāo),設(shè)置圖標(biāo)在內(nèi)環(huán)時(shí)的中心點(diǎn)和在外環(huán)時(shí)的中心點(diǎn)远荠。
4.1 查看一下效果
調(diào)用出現(xiàn)和移動(dòng)動(dòng)畫來測(cè)試一下矮固,如下:
func animOut() {
delay(1.0) {
self.signIconsOut?.animate()
}
delay(1.5) {
self.revealSignIcons?.animate()
}
delay(2.5) {
self.animIn()
}
}
func animIn() {
delay(0.25) {
self.hideSignIcons?.animate()
}
delay(1.0) {
self.signIconsIn?.animate()
}
delay(2.5) {
self.animOut()
}
}
在 setup()
里調(diào)用 animOut()
,如下:
public override func setup() {
canvas.backgroundColor = COSMOSbkgd
createSignIcons()
createSignIconAnimations()
animOut()
}
效果如下:
5. 打掃一下
刪除 animIn
和 animOut
方法譬淳,修改 setup()
方法如下:
public override func setup() {
canvas.frame = Rect(0,0,80,80)
canvas.backgroundColor = clear
createSignIcons()
createSignIconAnimations()
}
獲取 MenuIcons.swift.
干凈利索档址!
本文由 SwiftGG 翻譯組翻譯,已經(jīng)獲得作者翻譯授權(quán)邻梆。