ios - profesionales - Comprender la pausa y reanudar la animación en una capa
presets para after effects gratis (3)
De los docs :
hora de inicio
beginTime Especifica la hora de inicio del receptor en relación con su objeto principal, si corresponde. (necesario)
Aquí hay una gran explicación de esto tomado de here :
Si una animación está en un grupo de animación, beginTime es el desplazamiento desde el principio de su objeto principal: el grupo de animación. Entonces, si el tiempo de inicio de la animación es 5, comienza 5 segundos después de que comience el grupo de animación.
Si una animación se agrega directamente a una capa, beginTime sigue siendo el desplazamiento desde el principio de su objeto principal: la capa. Pero como el comienzo de una capa está en el pasado 1, no puedo simplemente establecer el tiempo de inicio en 5 para retrasar la animación 5 segundos, ya que 5 segundos después del comienzo de una capa es probablemente un tiempo pasado. Lo que generalmente quiero es un retraso relativo a cuando la animación se agrega a la capa, indicada por addTime.
Este es el código de la pregunta con una adición de registros:
- (IBAction)start:(UIButton *)sender
{
[UIView animateWithDuration:10 animations:^() {//Move square to x=300
}completion:^(BOOL finished){}];
}
- (IBAction)pause:(UIButton *)sender
{
CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
layer.speed = 0.0;
layer.timeOffset = pausedTime;
NSLog(@"pausedTime: %f",pausedTime);
}
- (IBAction)resume:(UIButton *)sender
{
CFTimeInterval pausedTime = [layer timeOffset];
layer.speed = 1.0;
layer.timeOffset = 0.0;
layer.beginTime = 0.0;
NSLog(@"CACurrentMediaTime: %f",[layer convertTime:CACurrentMediaTime() fromLayer:nil]);
CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
NSLog(@"timeSincePause: %f",timeSincePause);
layer.beginTime = timeSincePause;
}
Salida:
pausedTime: 20000
CACurrentMediaTime: 20005
timeSincePause: 5 // <- that''s your begin time. When you hit resume you want to begin the animation from that relative time.
Para resumir todo esto,
La duración de la animación es un total de 10, detuve la animación en 5 y también quiero que sea mi tiempo de inicio cuando reanudo la animación.
Por lo tanto, guardo el tiempo de pausa y lo resto de mi tiempo actual para obtener el tiempo de animación relativo que ha pasado.
Estoy estudiando Animación en la Guía de programación de animaciones básicas y me quedo atascado al comprender la pausa y reanudar la animación en una capa.
El documento me dice cómo pausar y reanudar la animación sin una explicación clara. Creo que la clave es entender qué es el método timeOffset
y beginTime
de CAlayer
.
Este código es pausar y reanudar la animación. En el método resumeLayer
, layer.beginTime = timeSincePause;
Esta línea realmente me confunde.
-(void)pauseLayer:(CALayer*)layer {
CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
layer.speed = 0.0;
layer.timeOffset = pausedTime;
}
-(void)resumeLayer:(CALayer*)layer {
CFTimeInterval pausedTime = [layer timeOffset];
layer.speed = 1.0;
layer.timeOffset = 0.0;
layer.beginTime = 0.0;
CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
layer.beginTime = timeSincePause;
}
Cualquier ayuda será apreciada.
En el archivo de cabecera de CAMediaTiming, podemos ver estos códigos:
/* The begin time of the object, in relation to its parent object, if
* applicable. Defaults to 0. */
@property CFTimeInterval beginTime;
/* The basic duration of the object. Defaults to 0. */
@property CFTimeInterval duration;
/* The rate of the layer. Used to scale parent time to local time, e.g.
* if rate is 2, local time progresses twice as fast as parent time.
* Defaults to 1. */
@property float speed;
/* Additional offset in active local time. i.e. to convert from parent
* time tp to active local time t: t = (tp - begin) * speed + offset.
* One use of this is to "pause" a layer by setting `speed'' to zero and
* `offset'' to a suitable value. Defaults to 0. */
@property CFTimeInterval timeOffset;
Lo importante es la fórmula:
t = (tp - comenzar) * velocidad + desplazamiento
Esta fórmula define cómo la hora global (o la hora principal, tp) se asigna a la hora local de la capa. Y esta fórmula puede explicar todo de los códigos listados:
- En el momento A, la animación está en pausa. Después de establecer la velocidad = 0, y timeOffset = pauseTime, la hora local de la capa es igual a pauseTime. Y la hora local no aumentará más porque la velocidad = 0;
- En el momento B, se reanuda la animación. Después de establecer la velocidad = 1.0, timeOffset = 0, beginTime = 0, la hora local de la capa es igual a la hora global (o tp) que es (timePause + timeSinacePause). Pero necesitamos que la animación comience desde el punto de tiempo #A, así que configuramos beginTime = timeSincePaused, y luego la hora local de la capa es igual a timePause. De causa, esto incurre en que la animación continúe desde el punto en pausa.
Hagamos una prueba en las dos propiedades de una capa: beginTime
y timeOffset
.
Requisito previo
Obtenemos el espacio de tiempo de CALayer utilizando [layer convertTime:CACurrentMediaTime() fromLayer:nil]
1 、 Asignar 5.0 al tiempo de beginTime
la capa (el tiempo de beginTime
es 0 antes):
NSLog(@"CACurrentMediaTime:%f", [t1.layer convertTime:CACurrentMediaTime() fromLayer:nil]) ;
t1.layer.beginTime = 5.0 ;
NSLog(@"CACurrentMediaTime:%f",[t1.layer convertTime:CACurrentMediaTime() fromLayer:nil]) ;
el registro de resultados es:
2014-01-15 11:00:33.811 newUserInterface[1404:70b] CACurrentMediaTime:7206.884498
2014-01-15 11:00:33.811 newUserInterface[1404:70b] CACurrentMediaTime:7201.885088
El resultado muestra que si agrego 5.0 en beginTime
, el tiempo de la capa será menos 5.0. Si una animación está en vuelo, agregar 5.0 en beginTime
provocará que la animación vuelva a hacer la animación hace 5.0 segundos.
2 、 Asigne 5.0 a timeOffset
la capa ( timeOffset
es 0 antes):
NSLog(@"CACurrentMediaTime:%f", [t1.layer convertTime:CACurrentMediaTime() fromLayer:nil]) ;
t1.layer.timeOffset = 5.0 ;
NSLog(@"CACurrentMediaTime:%f",[t1.layer convertTime:CACurrentMediaTime() fromLayer:nil]) ;
el resultado es:
2014-01-15 11:09:07.757 newUserInterface[1449:70b] CACurrentMediaTime:7720.851464
2014-01-15 11:09:07.758 newUserInterface[1449:70b] CACurrentMediaTime:7725.852011
El resultado muestra que si agrego 5.0 en timeOffset
, el tiempo de la capa agregará 5.0. Si una animación está en vuelo, agregar 5.0 en timeOffset causará que la animación salte a la animación que haría 5.0 segundos más tarde.
Comprender la pausa y reanudar la animación en una capa
Aquí hay un ejemplo, t1
es una subvista de la vista raíz de UIViewController. Hago una animación en t1 que anima la posición de t1.layer
.
Si se agrega una animación a una capa, la capa calculará cuándo animar la animación de acuerdo con el tiempo de beginTime
la animación, si el tiempo de beginTime
es 0, lo animará de inmediato.
CABasicAnimation * b1 = [CABasicAnimation animationWithKeyPath:@"position"] ;
b1.toValue = [NSValue valueWithCGPoint:CGPointMake(160.0, 320.0)] ;
b1.duration = 10.0f ;
[t1.layer addAnimation:b1 forKey:@"pos"] ;
NSLog(@"CACurrentMediaTime:%f", [t1.layer convertTime:CACurrentMediaTime() fromLayer:nil]) ;
muestra el registro 2014-01-15 11:25:53.975 newUserInterface[1530:70b] CACurrentMediaTime:8727.108740
, lo que significa que la animación comenzará en 8727 y se detendrá en 8727 + 10 en el espacio de tiempo de t1.layer.
Cuando la animación está en vuelo, y la - (void)pauseLayer:(CALayer*)layer
método de - (void)pauseLayer:(CALayer*)layer
.
layer.speed = 0.0;
provocará que la capa se detenga y el tiempo de la capa se establecerá en 0. (Lo sé porque al establecer layer.speed en 0, inmediatamente busco la hora de la capa y la registro)
layer.timeOffset = pausedTime;
agregará pauseTime al tiempo de la capa (asumiendo que layer.timeOffset sea 0), ahora el tiempo de la capa es pausedTime.
- (void)pauseLayer:(CALayer *)layer
{
CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil]; // pauseTime is the time with respect to layer''s time space
layer.speed = 0.0; // layer''s local time is 0
layer.timeOffset = pausedTime; // layer''s local time is pausedTime, so animation stop here
}
Luego reanudaré la animación utilizando el método de - (void)resumeLayer:(CALayer*)layer
.
-(void)resumeLayer:(CALayer*)layer {
CFTimeInterval pausedTime = [layer timeOffset];
layer.speed = 1.0;
layer.timeOffset = 0.0;
layer.beginTime = 0.0;
CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
layer.beginTime = timeSincePause;
}
Si detengo la animación en 8727 + 1 (significa la animación animada durante 1 segundo), en el método layer.speed = 0
, layer.speed = 0
establecerá el tiempo de la capa en 0 y layer.timeOffset = pausedTime;
agregará tiempo de pausa en el tiempo de la capa, por lo que el tiempo de la capa es tiempo de pausa.
Espera un momento, vamos a tener un resumen ahora. layer.speed
es 0.0, ''layer.timeOffset'' es igual a pausedTime
que es 8727 + 1 y el tiempo de layer es pausedTime también. Por favor ten en cuenta que pronto los usaremos.
Continuemos, layer.speed = 1.0;
la animación en 8727 + 11 con el método layer.speed = 1.0;
, layer.speed = 1.0;
agregará 8727 + 11 en el tiempo de la capa, por lo que el tiempo de la capa es 8727 + 1 + 8727 + 11, layer.timeOffset = 0.0;
causa el tiempo de la capa menos 8727 + 1 porque layer.timeOffset es 8727 + 1 antes, la hora local de la capa es 8727 + 11 ahora. timeSincePause
es (8727 + 11-8727-1) = 10.
layer.beginTime = timeSincePause;
hace que el tiempo de la capa sea menos 10. Ahora, el tiempo local de la capa es 8727 + 1, que es el momento en que hice una pausa en la animación.
Te mostraré el código y el registro:
- (void)pauseLayer:(CALayer *)layer
{
NSLog(@"%f", CACurrentMediaTime()) ;
NSLog(@"pauseLayer begin:%f", [t1.layer convertTime:CACurrentMediaTime() fromLayer:nil]) ;
CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil] ;
layer.speed = 0.0 ;
NSLog(@"pauseLayer after set speed to 0:%f",[t1.layer convertTime:CACurrentMediaTime() fromLayer:nil]) ;
layer.timeOffset = pausedTime ;
NSLog(@"pauseLayer after set timeOffset:%f",[t1.layer convertTime:CACurrentMediaTime() fromLayer:nil]) ;
}
- (void)resumeLayer:(CALayer *)layer
{
NSLog(@"%f", CACurrentMediaTime()) ;
NSLog(@"resumeLayer begin:%f",[t1.layer convertTime:CACurrentMediaTime() fromLayer:nil]) ;
CFTimeInterval pausedTime = layer.timeOffset ;
layer.speed = 1.0 ;
NSLog(@"resumeLayer after set speed to 1:%f",[t1.layer convertTime:CACurrentMediaTime() fromLayer:nil]) ;
layer.timeOffset = 0.0;
NSLog(@"resumeLayer after set timeOffset to 0:%f",[t1.layer convertTime:CACurrentMediaTime() fromLayer:nil]) ;
layer.beginTime = 0.0 ;
NSLog(@"resumeLayer after set beginTime to 0:%f",[t1.layer convertTime:CACurrentMediaTime() fromLayer:nil]) ;
CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime ;
layer.beginTime = timeSincePause ;
NSLog(@"resumeLayer after set beginTime to timeSincePause:%f",[t1.layer convertTime:CACurrentMediaTime() fromLayer:nil]) ;
}
Iniciar sesión:
2014-01-15 13:14:34.157 newUserInterface[1762:70b] 15247.550325
2014-01-15 13:14:34.158 newUserInterface[1762:70b] pauseLayer begin:15247.550826
2014-01-15 13:14:34.158 newUserInterface[1762:70b] pauseLayer after set speed to 0:0.000000
2014-01-15 13:14:34.159 newUserInterface[1762:70b] pauseLayer after set timeOffset:15247.551284
2014-01-15 13:14:40.557 newUserInterface[1762:70b] 15253.950505
2014-01-15 13:14:40.558 newUserInterface[1762:70b] resumeLayer begin:15247.551284
2014-01-15 13:14:40.558 newUserInterface[1762:70b] resumeLayer after set speed to 1:30501.502810
2014-01-15 13:14:40.559 newUserInterface[1762:70b] resumeLayer after set timeOffset to 0:15253.952031
2014-01-15 13:14:40.559 newUserInterface[1762:70b] resumeLayer after set beginTime to 0:15253.952523
2014-01-15 13:14:40.560 newUserInterface[1762:70b] resumeLayer after set beginTime to timeSincePause:15247.551294
Otra pregunta está en el método resumeLayer
: ¿por qué no combinar las dos líneas de asignación en una layer.beginTime = timeSincePause;
, la razón está en [layer convertTime:CACurrentMediaTime() fromLayer:nil]
, el valor del resultado está relacionado con layer.beginTime.
layer.beginTime = 0.0;
CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
layer.beginTime = timeSincePause;
A decir verdad, todavía no sé cómo funciona la animación, lo que estoy haciendo es analizar el resultado, no es una buena solución. Estoy muy contento de que cualquiera que tenga ideas sobre esto pueda compartir. ¡Gracias!