Как заставить CATransform3dMakeRotation вращаться в другую сторону? И цепь вместе - PullRequest
4 голосов
/ 26 сентября 2010

Я впервые работаю с Core Core Animation и в процессе реализации игральной карты, которую я могу перевернуть, я решил использовать CALayer для отображения содержимого (не знаю, как я Я собираюсь получить две стороны, но это другой вопрос) и я должен быть в состоянии перевернуть его, переместить его и т.д ...

Я использую CATransaction с некоторым успехом - в приведенном ниже фрагменте карта перемещается из левого нижнего угла в верхний левый и переворачивается. Проблема в том, что я не хочу перевернуть противоположный путь, но не знаю, как сказать: «Эй, ты идешь не в ту сторону!»


[CATransaction begin]; 
[CATransaction setValue:[NSNumber numberWithFloat:2.0f] forKey:kCATransactionAnimationDuration];
myCard.position = CGPointMake(CGRectGetMidX(self.bounds)/2,CGRectGetMidY(self.bounds)/2);
myCard.transform = CATransform3DMakeRotation(M_PI, 1, 0, 0);
[CATransaction commit]; 

Второй вопрос: как я могу заставить его выполнять два преобразования одновременно? Я пытался вложить два CATransactions, но второй просто перекрывает первый. Я также попытался изменить вектор для поворота на 2D, как сказать, что он вращается вокруг осей x и y, но это не эквивалентно просто перевернуть его пи на две отдельные оси. Вот вложенный код.


[CATransaction begin]; 
[CATransaction setValue:[NSNumber numberWithFloat:2.0f] forKey:kCATransactionAnimationDuration];
myCard.position = CGPointMake(CGRectGetMidX(self.bounds)/2,CGRectGetMidY(self.bounds)/2);
myCard.transform = CATransform3DMakeRotation(M_PI, 1, 0, 0); // rotate about x

  [CATransaction begin]; 
  [CATransaction setValue:[NSNumber numberWithFloat:1.0f] forKey:kCATransactionAnimationDuration];
  myCard.transform = CATransform3DMakeRotation(M_PI, 0, 1, 0); // rotate about y
  [CATransaction commit]; 

[CATransaction commit]; 

А вот и UIView блоков анимации внутри ... Я добавил ползунки для угла, x, y, z для вектора вращения и t для времени. Перевод происходит в течение времени = 2t, и каждое из вращений должно занимать только t каждого.

[CATransaction begin]; 
[CATransaction setValue:[NSNumber numberWithFloat:t * 2] forKey:kCATransactionAnimationDuration];
myCard.position = CGPointMake(CGRectGetMidX(self.view.bounds)/2,CGRectGetMidY(self.view.bounds)/2);

  [UIView beginAnimations:nil context:nil];
  [UIView setAnimationDuration:t];
  myCard.transform = CATransform3DMakeRotation(angle, x, y, z);
  [UIView commitAnimations]; 

  [UIView beginAnimations:nil context:nil];
  [UIView setAnimationDuration:t];
  [UIView setAnimationDelay:t];
  myCard.transform = CATransform3DMakeRotation(angle * 2, x, y, z);
  [UIView commitAnimations];

[CATransaction commit]; 

И вот где я сейчас: все это работает с одним исключением. Вращение y меняется на противоположное (начинает вращаться в противоположном направлении), когда карта достигает пи / 2 и 3 * пи / 2. Он также переворачивается вокруг оси X в этих точках. Но X и Z работают отлично. Так близко!

CGMutablePathRef thePath = CGPathCreateMutable();
CGPathMoveToPoint(thePath,NULL,CGRectGetMidX(self.view.bounds)/2,CGRectGetMidY(self.view.bounds)/2*3);
CGPathAddCurveToPoint(thePath,NULL,
                      CGRectGetMidX(self.view.bounds)/2,CGRectGetMidY(self.view.bounds)/2*2,
                      CGRectGetMidX(self.view.bounds)/2,CGRectGetMidY(self.view.bounds)/2*1.5,
                      CGRectGetMidX(self.view.bounds)/2,CGRectGetMidY(self.view.bounds)/2);
CAKeyframeAnimation *moveAnimation=[CAKeyframeAnimation animationWithKeyPath:@"position"];
moveAnimation.path=thePath;
CFRelease(thePath);

CABasicAnimation *xRotation;
xRotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.x"];
xRotation.fromValue = [NSNumber numberWithFloat:0.0];
xRotation.toValue = [NSNumber numberWithFloat:x * angle * M_PI];

CABasicAnimation *yRotation;
yRotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];
yRotation.fromValue = [NSNumber numberWithFloat:0.0];
yRotation.toValue = [NSNumber numberWithFloat:y * angle * M_PI];

CABasicAnimation *zRotation;
zRotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
zRotation.fromValue = [NSNumber numberWithFloat:0.0];
zRotation.toValue = [NSNumber numberWithFloat:z * angle * M_PI];

CAAnimationGroup *groupAnimation = [CAAnimationGroup animation];
groupAnimation.duration = t;
groupAnimation.removedOnCompletion = NO;
groupAnimation.fillMode = kCAFillModeForwards;
groupAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
groupAnimation.animations = [NSArray arrayWithObjects:moveAnimation, xRotation, yRotation, zRotation, nil];

[myCard addAnimation:groupAnimation forKey:@"animateCard"];

Ответы [ 3 ]

10 голосов
/ 27 сентября 2010

Я считаю, что в данном случае вы хотите использовать вспомогательный путь, как описано в этом ответе :

CABasicAnimation *rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.x"];
rotationAnimation.toValue = [NSNumber numberWithFloat: M_PI];
rotationAnimation.duration = duration;
[myCard.layer addAnimation:rotationAnimation forKey:@"rotationAnimation1"];

, который должен вращаться вокруг оси X для первой стадии вашей анимации. Во-вторых, я считаю, что если вы используете следующее

CABasicAnimation *rotationAnimation2 = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];
rotationAnimation2.toValue = [NSNumber numberWithFloat: M_PI];
rotationAnimation2.duration = duration2;
rotationAnimation2.cumulative = YES;
[myCard.layer addAnimation:rotationAnimation2 forKey:@"rotationAnimation2"];

Это должно сделать анимацию кумулятивной и может дать желаемый эффект. Я не пробовал это сам, поэтому может потребоваться некоторая обработка, чтобы получить именно тот результат, который вам нужен.

Когда вы работаете с преобразованием напрямую, Core Animation будет интерполировать преобразование из текущего значения в указанное преобразование. Он найдет кратчайший путь к этому преобразованию, что ограничит направление анимации. Если вы попытаетесь анимировать одно и то же свойство преобразования дважды, второе значение просто переопределит первое, а не объединит два преобразования вместе.

Однако при использовании вспомогательной траектории, подобной этой, Core Animation будет интерполировать между вашим начальным и конечным углом, поэтому вы можете изменить направление, изменив знак конечного угла. Он оптимизирует изменение значения угла, а не изменение базового преобразования. Вы также должны иметь возможность комбинировать анимации на траектории клавиш, потому что преобразование генерируется для вас под капотом для каждой комбинации манипуляций с траекторией.

3 голосов
/ 22 ноября 2010

Я предполагаю, что переворот на оси вызван блокировкой карданного подвеса внутри библиотеки Apple. Простое исправление, которое работает в 99,999% случаев, состоит в том, чтобы просто не использовать точные повороты на 0, 90, 180, 270 и 360 градусов. Я видел подобные вещи в моем коде, но я просто использую что-то вроде (M_PI * 0.99) вместо M_PI. Это всегда проблема, если вы используете матричные множители видеокарты с углами Эйлера.

Чтобы применить несколько преобразований одновременно, вы можете вложить их. Это работает точно так же, как умножение матриц в OpenGL.

// translate and scale at the same time
float scale = 1.6;
CATransform3D scaleMatrix = CATransform3DMakeScale(scale, scale, 1);
CATransform3D finalMatrix = CATransform3DTranslate(scaleMatrix, frame.size.width/scale, frame.size.height/scale, 0);
CABasicAnimation* animBottomTrans = [CABasicAnimation animationWithKeyPath:@"transform"];
[animBottomTrans setFromValue:[NSValue valueWithCATransform3D:finalMatrix]];
[animBottomTrans setToValue:[NSValue valueWithCATransform3D:CATransform3DIdentity]];
[animBottomTrans setDuration:duration];
[myLayer addAnimation:animBottomTrans forKey:@"transform"];

Помните, что поскольку они вложенные, вы должны иметь дело с предыдущим преобразованием в любых преобразованиях, следующих за ним. В этом коде я должен изменить сумму, которую я перевел, потому что я уже масштабировал. Если вы поворачиваете на 90 градусов по оси z, а затем поворачиваете на 90 градусов по оси y, вы фактически только что повернули по осям z и x (вращение z меняет оси x и y).

2 голосов
/ 27 сентября 2010

Полученное преобразование такое же: (CGAffineTransform){-1,0,0,-1,0,0}. CoreAnimation выбирает самое короткое вращение, которое будет работать, и по умолчанию вращается по часовой стрелке (я думаю).

Самый простой способ повернуть другим способом - это повернуть на почти -M_PI (я не уверен, насколько точно вы можете приблизиться, прежде чем он решит повернуть «неправильным» способом).

Более сложный способ - разбить его на два поворота: одно от 0 до -M_PI / 2 и одно от -M_PI / 2 до -M-PI. Я думаю, что вы можете установить задержку анимации, чтобы это произошло ...

...