2015-05-20 17 views
7

Mi piacerebbe animare una forma mentre transita da un cerchio ad un triangolo con angoli arrotondati.Animare un cerchio CAShapeLayer in un triangolo con angoli arrotondati

TL; DR: Come faccio ad animare una s CAShapeLayer' path tra due CGPath forme? So che devono avere lo stesso numero di punti di controllo, ma penso che lo stia facendo - cosa c'è di sbagliato in questo codice?

Gli stati iniziale e finale sarebbe simile a questa: transition http://clrk.it/4vJS+

Ecco quello che ho provato finora: sto usando un CAShapeLayer, e animando un cambiamento nella sua proprietà path.

Secondo il documentation (sottolineatura mia):

L'oggetto percorso può essere animati utilizzando una delle sottoclassi concrete di CAPropertyAnimation. I percorsi interpoleranno come una miscela lineare dei punti "on-line"; i punti "off-line" possono essere interpolati non linearmente (per esempio per preservare la continuità della derivata della curva). Se i due percorsi hanno un numero diverso di punti di controllo o segmenti, i risultati non sono definiti.

Nel tentativo di ottenere il cerchio e triangolo per avere lo stesso numero di punti di controllo, li feci entrambe le forme a quattro punte: il cerchio è un rettangolo fortemente arrotondato, e il triangolo è "nascosto" un quarto punto di controllo su un lato.

Ecco il mio codice:

self.view.backgroundColor = .blueColor() 

//CREATE A CASHAPELAYER 
let shape = CAShapeLayer() 
shape.frame = CGRect(x: 50, y: 50, width: 200, height: 200) 
shape.fillColor = UIColor.redColor().CGColor 
self.view.layer.addSublayer(shape) 

let bounds = shape.bounds 

//CREATE THE SQUIRCLE 
let squareRadius: CGFloat = CGRectGetWidth(shape.bounds)/2 

let topCorner = CGPointMake(bounds.midX, bounds.minY) 
let rightCorner = CGPointMake(bounds.maxX, bounds.midY) 
let bottomCorner = CGPointMake(bounds.midX, bounds.maxY) 
let leftCorner = CGPointMake(bounds.minX, bounds.midY) 

let squarePath = CGPathCreateMutable() 
let squareStartingPoint = midPoint(leftCorner, point2: topCorner) 
CGPathMoveToPoint(squarePath, nil, squareStartingPoint.x, squareStartingPoint.y) 
addArcToPoint(squarePath, aroundPoint: topCorner, onWayToPoint: rightCorner, radius: squareRadius) 
addArcToPoint(squarePath, aroundPoint: rightCorner, onWayToPoint: bottomCorner, radius: squareRadius) 
addArcToPoint(squarePath, aroundPoint: bottomCorner, onWayToPoint: leftCorner, radius: squareRadius) 
addArcToPoint(squarePath, aroundPoint: leftCorner, onWayToPoint: topCorner, radius: squareRadius) 
CGPathCloseSubpath(squarePath) 
let square = UIBezierPath(CGPath: squarePath) 

//CREATE THE (FAKED) TRIANGLE 
let triangleRadius: CGFloat = 25.0 

let trianglePath = CGPathCreateMutable() 
let triangleStartingPoint = midPoint(topCorner, point2: rightCorner) 

let startingPoint = midPoint(topCorner, point2: leftCorner) 
CGPathMoveToPoint(trianglePath, nil, startingPoint.x, startingPoint.y) 
let cheatPoint = midPoint(topCorner, point2:bottomCorner) 
addArcToPoint(trianglePath, aroundPoint: topCorner, onWayToPoint: cheatPoint, radius: triangleRadius) 
addArcToPoint(trianglePath, aroundPoint: cheatPoint, onWayToPoint: bottomCorner, radius: triangleRadius) 
addArcToPoint(trianglePath, aroundPoint: bottomCorner, onWayToPoint: leftCorner, radius: triangleRadius) 
addArcToPoint(trianglePath, aroundPoint: leftCorner, onWayToPoint: topCorner, radius: triangleRadius) 
CGPathCloseSubpath(trianglePath) 
let triangle = UIBezierPath(CGPath: trianglePath) 


shape.path = square.CGPath 

... e più tardi, in viewDidAppear:

let animation = CABasicAnimation(keyPath: "path") 
animation.fromValue = self.square.CGPath 
animation.toValue = self.triangle.CGPath 
animation.duration = 3 
self.shape.path = self.triangle.CGPath 
self.shape.addAnimation(animation, forKey: "animationKey") 

Ho due piccole funzioni rapide per rendere il codice più leggibile:

func addArcToPoint(path: CGMutablePath!, aroundPoint: CGPoint, onWayToPoint: CGPoint, radius: CGFloat) { 
    CGPathAddArcToPoint(path, nil, aroundPoint.x, aroundPoint.y, onWayToPoint.x, onWayToPoint.y, radius) 
} 

func midPoint(point1: CGPoint, point2: CGPoint) -> CGPoint { 
    return CGPointMake((point1.x + point2.x)/2, (point1.y + point2.y)/2) 
} 

Il mio attuale risultato è il seguente:

triangle-to-square

NOTA: Sto cercando di costruire il cerchio di una piazza molto arrotondato, nel tentativo di ottenere lo stesso numero di punti di controllo in forma vettoriale. Nel GIF sopra, il raggio dell'angolo è stato ridotto per rendere più visibile la trasformazione.

Cosa pensi che stia succedendo? In quale altro modo potrei ottenere questo effetto?

+0

provare a realizzare entrambe le forme con molti segmenti di linea retta? – nielsbot

+0

in realtà, sei sicuro di utilizzare correttamente addArcToPoint()? – nielsbot

+0

(Non ho il parametro * path * per aggiungereArcToPoint() per essere facoltativo) – nielsbot

risposta

6

Non si finisce con lo stesso numero di punti di controllo perché addArcToPoint() salta creando un segmento di linea se il punto corrente del percorso e il suo primo argomento sono uguali.

Dalla sua documentazione (sottolineatura mia):

Se il punto corrente e il primo punto di tangenza dell'arco (punto di partenza) non sono uguali, Quarzo accoda un segmento di retta dagli attuali puntare al primo punto tangente.

Queste due chiamate nel codice triangolo-making non produrranno segmenti di linea, mentre le chiamate corrispondenti rendendo lo squircle sarà:

addArcToPoint(trianglePath, aroundPoint: cheatPoint, onWayToPoint: bottomCorner, radius: triangleRadius) 
addArcToPoint(trianglePath, aroundPoint: bottomCorner, onWayToPoint: leftCorner, radius: triangleRadius) 

È possibile creare manualmente le righe, però, chiamando CGPathAddLineToPoint . E puoi controllare il tuo lavoro usando CGPathApplierFunction, che ti permetterà di enumerare i punti di controllo.


~~~~~V~~3rd anche Potrei suggerire che girare un link di visualizzazione e la scrittura di una funzione che consente di specificare la forma desiderata per ogni t ∈ [0, 1] è destinata probabilmente ad essere una soluzione più chiara, più gestibile.

+0

Grazie mille! Sì, questo sarà un cerchio interattivo: lo trascini a sinistra, il cerchio diventa una freccia, lo trascini a destra, fa l'altro modo, quindi dovrò sicuramente scrivere quella funzione! – bryanjclark

+1

Il framework Pop di Facebook è un modo molto carino per creare animazioni con curve, concatenazioni, ecc. Al di fuori di tali funzioni (consulta https://github.com/facebook/pop/blob/master/pop/POPAnimatableProperty.h). –