Search code examples
objective-cioscore-animation

Folding animation not lining up with sliding animation


I am trying to create a folding menu animation like this one: http://vimeo.com/41495357

At this point the animation works when dragging your finger across the screen, but when I try to animate the menu from open to closed or closed to open directly, the main view that is sliding away does not line up with the right side of the view that is unfolding. They are aligned when the animation ends, but not during. Also, the edges of the view that is unfolding seem to disappear outside the bounds of the view during the animation.

The actual unfolding of the layer is done in layoutSublayers, so when the width of layer changes, the transforms on its sublayers are calculated. I found the math for calculating the transforms in this project: https://github.com/MassiveHealth/Opaque

// Configure the layer bounds and midpoints
CGRect halfRect = CGRectMake(0, 0, self.fullWidth / 2, self.bounds.size.height);
self.rightImageLayer.bounds = halfRect;
self.leftImageLayer.bounds = halfRect;
CGPoint midPoint = CGPointMake(0.5 * self.bounds.size.width, 0.5 * self.bounds.size.height);
self.rightImageLayer.position = midPoint;
self.leftImageLayer.position = midPoint;

// Calculate transforms
CGFloat l = 0.5 * self.fullWidth;
CGFloat y = 0.5 * self.bounds.size.width;
CGFloat theta = acosf(y/l);
CGFloat z = l * sinf(theta);
CGFloat topAngle = theta * -1;
CGFloat bottomAngle = theta;

CATransform3D transform = CATransform3DMakeTranslation(0.0, 0.0, -z);
self.rightImageLayer.transform = CATransform3DRotate(transform, topAngle, 0.0, 1.0, 0.0);
self.leftImageLayer.transform = CATransform3DRotate(transform, bottomAngle, 0.0, 1.0, 0.0);

This is the code that is responsible for starting the unfold animation. (right now the menu unfolds to the entire width of the screen)

self.foldingLayer.frame = self.view.bounds;
self.slidingLayer.frame = CGRectMake(self.view.frame.size.width, 0, self.slidingLayer.frame.size.width, self.slidingLayer.frame.size.height);

How come these animations don't line up? Perhaps the math is incorrect? I have looked at sample code from projects like https://github.com/honcheng/PaperFold-for-iOS and https://github.com/xyfeng/XYOrigami but I can't figure it out.

Update

Based on Till's answer I have created this keyframe animation for the sliding layer. The animations still don't line up, so my keyframe calculations must be wrong?

CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position.x"];

NSUInteger steps = 100;
CGFloat totalFoldViewWidth = self.view.bounds.size.width;
NSMutableArray *values = [NSMutableArray arrayWithCapacity:steps];

for(NSUInteger i = 0; i < steps; i++) {
    CGFloat fraction = (CGFloat)i / steps;
    CGFloat foldViewWidth = fraction * totalFoldViewWidth;
    CGFloat l = 0.5 * totalFoldViewWidth;
    CGFloat y = 0.5 * foldViewWidth;
    CGFloat angle = acosf(y/l);
    CGFloat projectionWidth = cosf(angle) * totalFoldViewWidth;
    [values addObject:[NSNumber numberWithFloat:projectionWidth]];
}
animation.calculationMode = kCAAnimationLinear;
[animation setValues:values];
[animation setDuration:3.0];

[self.slidingLayer addAnimation:animation forKey:@"slideAnimation"];

Solution

  • I think you are addressing the problem from the wrong direction. You want to calculate the angles from the distance and not vice versa. If you want to calculate the angle for each side of the folding animation, you use basic pythagorean arithmetic. Clearly we know the size for the opening distance and we know the size for the hypotenuse (half the size of the view when fully opened).

    In code:

    CGFloat otherSide = 0.5 * openingWidth; // halve because we have two sides
    CGFloat hypotenuse = 0.5 * unfoldedViewTotalWidth; // halve because we have to sides
    CGFloat angle = asinf(otherSide / hypotenuse);
    CGFloat rightAngle = -1 * (M_PI_2 - angle);
    CGFloat leftAngle = M_PI_2 - angle;
    

    You can use these angles to set the rotation for left and right sides of the fold view.

    Beware to set your anchorpoints for the left side to (0.0, 0,5) and for the right to (1.0, 0.5) and to animate the right side translation as well!

    Hope this helps! Cheers!