0

I have a layer with a CAKeyframeAnimation that is animating it along a path. The layer also has a changing transform property as the animation's rotationMode is set to kCAAnimationRotateAuto.

When I tap the layer (which I'm detecting with a tap gesture recognizer and hit-testing the presentationLayer of the animated layer like in this question, if there's a better way to do this let me know), I want to do the following:

  1. Grab the position and transform from the presentation layer
  2. Remove the current animation.
  3. Using the info grabbed in step 1, add a new animation to the layer that maintains the transform and start point and slows the layer's transit to a new endpoint ~40 pixels from where the previous animation had just ended.

The end result should be that, when tapped, an animating layer will slow to a halt along a straight line that lies tangent to the path at the point where the original keyframe animation was removed.

How would I go about doing this, math-wise? I already have the start point and transform, so it seems to me that I just need to figure out the endpoint, perhaps by using the startpoint, transform, and distance (say 40 for fun). Any ideas?

Community
  • 1
  • 1
kevboh
  • 5,207
  • 5
  • 38
  • 54
  • What exactly are you trying to do in step 3? Continue moving along the path but slow down and then stop (also on the path)? – David Rönnqvist Jun 21 '12 at 13:20
  • No, continue in a straight line along the tangent to the path (as reported by the transform of the layer) for a bit. – kevboh Jun 21 '12 at 13:22
  • Ok, I was confused when you said "the end result **is** ... layer **will** ..." it made it sound like you already had that part working and wanted something else – David Rönnqvist Jun 21 '12 at 13:25
  • **DISCLAIMER: I haven't tried this yet (therefor comment instead of answer)** If you have the transform you should be able to calculate the angle. You could also apply a translation transform on the rotation transform to have it move 40px to one side, something like `layer.transform = CATransform3DTranslate(layer.transform, 40, 0, 0);` (it should be able to give you the visual effect that you are after but will probably mess up hit testing since the actual layer position is not changed) – David Rönnqvist Jun 21 '12 at 13:34
  • Do you know how I could calculate the angle from the transform? – kevboh Jun 21 '12 at 13:36
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/12859/discussion-between-david-ronnqvist-and-kevboh) – David Rönnqvist Jun 21 '12 at 13:39

1 Answers1

2

If I understand correctly what you basically want is for the animated layer to decelerate and stop along the same vector as it's moving when you tap it. If you have the position when you tap it and the transform when you tap it I think you can find out the endpoint by doing the following:

CATransform3D transform = <Your layer's transform goes here>;
CGPoint startPoint = <Your layers's current position goes here>;
CGFloat distance = 40.f;

CGPoint v = CGPointMake(0, distance);

CGAffineTransform affineTransform = CATransform3DGetAffineTransform(transform);

CGPoint offset = CGPointApplyAffineTransform(v, affineTransform);
CGPoint endPoint = CGPointMake(startPoint.x + offset.x, startPoint.y + offset.y);

P.S. - This only works if you don't apply any scaling, skewing etc. as part of your animation (i.e. the transform should only represent rotations and translations)

Mattia
  • 2,251
  • 1
  • 22
  • 27
  • This is pretty sweet. I won't be able to try this until later tonight or tomorrow, but I'll let you know how it turns out. Thanks! – kevboh Jun 21 '12 at 15:08
  • That's a great trick. When I read the OPs post, I was trying to figure out how to calculate a tangent to a curved CGPath at the current point. That would require some seriously nasty math. I forgot that the transform would be rotated due to the rotation mode being set to kCAAnimationRotateAuto. I like it. – Duncan C Jun 23 '12 at 12:17