折叠动画与滑动动画不匹配

4
我正在尝试创建一个类似于http://vimeo.com/41495357的折叠菜单动画。目前,在屏幕上拖动手指时,动画可以正常工作。但是,当我尝试直接从打开到关闭或者从关闭到打开动画菜单时,向右滑动的主视图与展开视图的右侧不对齐。它们在动画结束时对齐,但在动画过程中不对齐。此外,在动画期间,展开视图的边缘似乎会消失在视图的边界之外。
实际的展开层是在layoutSublayers中完成的,因此当层的宽度改变时,其子层上的变换就会被计算。我在这个项目中找到了计算变换的数学方法: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);

这是负责启动展开动画的代码。(目前菜单会展开至整个屏幕宽度)

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

这些动画为什么不对齐呢?也许数学计算有误?我查看了一些项目的示例代码,如https://github.com/honcheng/PaperFold-for-iOShttps://github.com/xyfeng/XYOrigami,但我无法找出问题所在。
更新: 根据Till的回答,我已经为滑动层创建了这个关键帧动画。然而,这些动画仍然不匹配,因此我的关键帧计算可能有误?
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"];
2个回答

3
我认为你的思路有误。你想从距离计算角度,而不是反过来。如果你想计算每个折叠动画边的角度,可以使用基本的勾股定理。显然,我们知道打开距离的大小,也知道斜边的大小(完全打开时视图大小的一半)。
代码如下:
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;

您可以使用这些角度来设置折叠视图左右侧的旋转。
请注意,将左侧的锚点设置为(0.0,0.5),将右侧设置为(1.0,0.5),并同时对右侧进行平移动画!
希望能帮到您!祝好!

我不确定在所有情况下,从角度计算距离是否都是错误的。对于手动滑动,它肯定应该是这样的,因此+1。对于其他类型的动画,例如用于制作动态效果(例如屏幕构建),从角度计算距离也可能很好 - 至少这是我过去的做法。 - Till

0

这确实是您的数学问题。该视图的距离取决于折叠视图的投影宽度,而该宽度并非严格线性取决于角度;

projectionWidth = cos(angle) * foldViewWidth

换句话说,40度的结果与20度完全不同,并且该关系不是线性的,而是基于该角度的余弦。

因此,线性动画行不通。您需要创建一个基于该折叠视图的投影宽度的帧动画来匹配翻译和旋转。


我正在尝试基于这个计算创建关键帧动画,但似乎我漏掉了什么。foldViewWidth 是否是折叠视图在展开状态下的总宽度? - Niels
foldViewWidth 是该视图完全展开时的宽度(或者换句话说,它的原始宽度)。 - Till
这可能是一个愚蠢的问题,但我如何知道动画中某个点的角度。我仍然无法弄清楚。您能给我一些指针来创建这个关键帧动画吗? - Niels

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接