Search code examples
objective-cioscocoa-touchmathcore-animation

Using a CGPoint and CATransform3D to find a new point on a line?


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?


Solution

  • 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)