2012-04-11 8 views
14

In Cocoa/Touch, CAMediaTimingFunction rappresenta quattro punti di controllo che specificano una curva bezier cubica di una funzione di temporizzazione. Per un'applicazione che sto scrivendo mi piacerebbe essere in grado di estrarre il risultato di detta curva di bezier ad un tempo arbitrario t (0 -> 1). Quello che mi sta confondendo è che quando guardo come do this, il risultato dovrebbe essere un punto così, non è uno scalare:Utilizzo di CAMediaTimingFunction per calcolare il valore all'istante (t)

B(t) = (1 - t)^3 * P0 + 3 * (1 - t)^2 * t * P1 + 3 * (1 - t) * t^2 * P2 + t^2 * P3

Tuttavia, i risultati di implementazione di Apple in un valore scalare (è possibile vedere su questo grafico tramano x (t) vs t: http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Animation_Types_Timing/Articles/Timing.html#//apple_ref/doc/uid/TP40006670-SW1)

Così fa Apple ha semplicemente ignorare la coordinata y del risultato e trattare solo con la x? Questo sembra strano perché in questo caso non è necessario passare in punti di controllo, ma piuttosto controllare gli scalari poiché gli y non influenzano affatto il risultato.

+0

Penso che probabilmente potrei rispondere meglio alla tua domanda se potresti ampliare il contesto della domanda. Stai usando CAMediaTimingFunction per animare qualcosa? Se è così, è un'animazione di fotogrammi chiave? – jin

risposta

4

Nota: Non sono un esperto di CoreAnimation. Questa è solo la mia comprensione dal leggere i documenti che hai collegato.

Apple sta mixando i sistemi di coordinate qui, creando confusione.

x(t) nei grafici di esempio rappresenta una progressione scalare lungo un percorso, non una coordinata fisica.

I punti di controllo utilizzati in CAMediaTimingFunction vengono utilizzati per descrivere questa progressione, non un punto geometrico. Per aggiungere confusione, x nei punti di controllo esegue effettivamente la mappatura su t nei grafici e nei punti di controllo su x(t) sui grafici.

Per prendere il grafico per kCAMediaTimingFunctionEaseInEaseOut come esempio, questo grafico sarebbe descritto approssimativamente dai punti di controllo (0,0), (0,5,0), (0,5,1), (1,1).

+0

Si è effettivamente corretto che il problema era che t significa xe x (t) significa y. Rimane la domanda su come trovare y dato un po 'x comunque (in questo momento sembra implicare la risoluzione dell'equazione cubica che è un compito piuttosto difficile). –

+2

@Fransisco L'hai contrassegnato come la risposta, ma come dichiari nel commento, la vera domanda è ancora di ottenere y per una data x. Hai mai risolto questo? Saluti. – epologee

+1

@jin: In realtà, ho appena usato il metodo CAMediaTimingFunction getControlPointAtIndex per interrogare i punti di controllo di una funzione di temporizzazione kCAMediaTimingFunctionEaseInEaseOut e i punti di controllo sono (.42, 0) e (.58, 1). (Il primo e l'ultimo punto sono sempre 0,0 e 1,1) –

1

Il miglior suggerimento sarebbe probabilmente UnitBezier.h nel codice sorgente di WebKit. In effetti, credo che Apple usi lo stesso codice all'interno di Core Animation.

Più lungo, utilizzano il parametro t calcolare (che non ha nulla a che fare con il tempo) dato un valore di x che in realtà contiene il valore di tempo. Quindi usano Newton-Raphson method (additional explanation with examples). Dal momento che può fallire, itera un po 'di più sull'uso della bisezione ("divide e conquista"). Vedi questo nel metodo solveCurveX().

Dopo aver ottenuto il parametro t (che è necessario dato che i Bezier cubici sono definiti parametricamente), lo usano semplicemente per calcolare . Vedi questo nel metodo solve().

Tra l'altro, un grande fan di cappuccino, e ancora sperando in un rilascio di Atlas :-)

8

Core Animation di CAMediaTimingFunction fa quello che si vuole, ma non espone ottenere 'y' per una data 'x' per un uso versatile (animazione), ma piuttosto nutre semplicemente i valori risolti in modo opaco per il sistema di animazione sotto il cofano.

Ne avevo bisogno anch'io quindi built a class with the interface and capabilities exactly like CAMediaTimingFunction but with the needed -valueForX: method; esempio di utilizzo:

RSTimingFunction *heavyEaseInTimingFunction = [RSTimingFunction timingFunctionWithControlPoint1:CGPointMake(0.8, 0.0) controlPoint2:CGPointMake(1.0, 1.0)]; 
CGFloat visualProgress = [heavyEaseInTimingFunction valueForX:progress]; 

È possibile creare la facilità-in, la facilità-out, facilità in-ease-out o realmente qualsiasi curve che possono essere descritti con a cubic Bézier curve. La matematica dell'implementazione è basata su WebCore (WebKit), che presumibilmente è anche utilizzato da CoreAnimation.

Godetevi, Raphael

+0

+1 Bel lavoro :) - hai idea di come le animazioni basate su fisica di iOS 7 funzionino sotto il cofano? ('animate ... usingSpringWithDamping: ..') – Robert

+1

Utilizzano un motore fisico, condiviso con SpriteKit, che per questi particolari calcoli utilizza probabilmente il popolare RK4 Integrator. L'integratore RK4 è spiegato molto bene qui http://gafferongames.com/game-physics/integration-basics/ con un ottimo esempio di codice in C++. –

+0

Peccato che non funzioni più su iOS 7.:/ – Andy

1

E 'un peccato, ma Core Animation non espone il suo modello di calcolo interno per la sua tempistica animazione. Tuttavia, ciò che ha funzionato molto bene per me è usare Core Animation per fare il lavoro!

  1. Creare un CALayer per servire come un valutatore
  2. impostare il suo telaio per ((0.0, 0.0), (1.0, 1.0))
  3. Impostare isHidden a true
  4. Impostare speed a 0.0
  5. Aggiungi questo livello a qualche strato contenitore
  6. Quando si desidera valutare qualsiasi CAMediaTimingFunction, creare un riferimento Animazione:

    let basicAnimation = CABasicAnimation(keyPath: "bounds.origin.x") 
    basicAnimation.duration = 1.0 
    basicAnimation.timingFunction = timingFunction 
    basicAnimation.fromValue = 0.0 
    basicAnimation.toValue = containerLayer.bounds.width 
    
    referenceLayer.add(basicAnimation, forKey: "evaluatorAnimation") 
    
  7. Impostare il livello del timeOffset a qualsiasi valore di ingresso normalizzato di riferimento (vale a dire, tra il 0.0 e 1.0) si desidera valutare:

    referenceLayer.timeOffset = 0.3 // 30% into the animation 
    
  8. Richiedi livello di presentazione del livello di riferimento e ottiene il suo valore x dell'origine dell'origine corrente:

    if let presentationLayer = referenceLayer.presentation() as CALayer? { 
        let evaluatedValue = presentationLayer.bounds.origin.x/containerLayer.bounds.width 
    } 
    

In pratica, si sta utilizzando Core Animation per eseguire un'animazione per un livello invisibile. Ma il numero speed del livello è 0.0, quindi non avanza l'animazione. Usando timeOffset, possiamo regolare manualmente la posizione corrente dell'animazione e ottenere la posizione x del suo livello di presentazione. Questo rappresenta l'attuale valore percepito di tale proprietà come guidato dall'animazione.

È un po 'non convenzionale, ma non c'è nulla di hacky a riguardo. È una rappresentazione fedele del valore di output di uno CAMediaTimingFunction che puoi ottenere perché Core Animation lo sta effettivamente utilizzando.

L'unica cosa di cui essere a conoscenza è che i livelli di presentazione sono vicini approssimazioni dei valori presentati sullo schermo. Core Animation non fornisce garanzie sulla loro accuratezza, ma in tutti i miei anni di utilizzo di Core Animation, non l'ho mai visto impreciso. Tuttavia, se la tua applicazione richiede una precisione assoluta, è possibile che questa tecnica non sia la migliore.