基于输入值(触摸)而非时间的iPhone动画

12
为了实现与动画组方法相适应的动画效果,如Brad Larson在这里的回答中所示,我需要根据输入来进行动画。具体而言是根据检测到的触摸的位置和轨迹移动。虽然可以很容易地处理touchesMoved:并为每次触摸设置元素的位置,但它并不像核心动画方法那样平滑。
想象一下,一个弯曲的轨道中有一个弹珠。我想将弹珠向任意位置以任意速度推动。动画必须像这样,在响应触摸的情况下沿着路径移动视觉元素。CAKeyframeAnimation恰好具有路径部分,但似乎总是希望基于经过的时间从一帧过渡到另一帧,而不是基于其他任何因素,并且只能单向移动。
更新于1月31日-感谢大家到目前为止的回复,然而没有一个真正解决问题。我有一个圆形菜单,可以拖动选择选项。所有内容都需要一起移动,并通过使用旋转变换应用于视图并将逆旋转变换应用于其子视图来解决问题,以使所有图标的旋转角度正确。但是,当图标沿稍微椭圆形的路径移动时,它看起来更好...弹珠描述是为了清楚地说明我的意图。最好想象定向排斥的磁铁,在槽中行进-移动一个,它的邻居也会移动,但不一定朝着被拖曳的磁铁移动的方向,因为路径弯曲。
现在问题是遵循由一个圆形创建的简单路径,但我真的想知道如何沿任意路径动画对象,其位置仅由触摸控制,而不涉及速度或方向计算。

嗯,这成为了一个流行的问题。Pivot的答案是正确的,并且在我编写自问以来一直运行良好。 - Adam Eberbach
5个回答

7
你可以使用层树中时间轴的分层结构特性来实现你想要的效果。实现CAMediaTiming的对象,包括CAAnimationCALayer,从其父级继承了时间空间,它们可以通过缩放、移位和重复修改(通过propagate)并传递给它们的子节点。通过将一个图层的speed属性设置为0.0,并手动调整timeOffset属性,您可以将该图层(以及所有子图层)与通常的时间概念分离。
您需要做的是为所有菜单项定义动画,使它们沿着您期望的CGPath在时间t0t1之间移动位置,并在每个动画上适当地设置timeOffset以保持项目的适当间距。请注意,动画的beginTime通常被解释为从动画添加到其图层的时间开始,因此如果您希望t0为0.0,则可能需要将其设置为微小的epsilon > 0.0。
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
animation.beginTime = 1e-100;
animation.duration = 1.0;
animation.fillMode = kCAFillModeBoth;
animation.removedOnCompletion = NO;
animation.path = path;
animation.calculationMode = kCAAnimationPaced;
animation.timeOffset = timeOffset;

您需要在包含这些菜单项的父图层上设置speed属性为0.0,并响应触摸事件更新其timeOffset值在t0t1之间。

这种方法有两个潜在的注意事项。因为您接管了该层子树的时间性质,所以可能无法同时动画其他属性。此外,如果您想要快速滑动的惯性行为,则可能需要自己向前动画时间。


好主意!我接受它,因为它将给我所需的控制水平,即使它并不完全将动画绑定到触摸位置。通过操纵timeOffset应该可以使其非常接近相同的效果。 - Adam Eberbach
如果触摸位置和动画进度之间的关系是线性的,您可以选择动画的beginTime和duration,以便将触摸位置的相关组件作为timeOffset传递,并使用核心动画的时间到进度映射应用该关系。 - Pivot

1

速度 = 距离/时间。您可以尝试根据移动的触摸和时间给出延迟。您可以计算touchesBegan和touchesEnded方法之间的时间。


1
如果您的目标是iOS 4.0或更高版本,则可以使用新的基于块的类方法来开始对view对象进行可动画化的更改。因此,从您的触摸事件中,您可以启动对视图属性的可动画更改:
[UIView animateWithDuration:1.0 animations:^{
    yourView.alpha = 0.0; // fade out yourView over 1 second
}];

注意: 这同样适用于对视图的其他属性进行更改,例如其位置。您可以通过以下方式以动画形式更改视图的以下属性:

@property frame
@property bounds
@property center
@property transform
@property alpha
@property backgroundColor
@property contentStretch

如果你的目标是早期版本的iOS,你需要使用UIView beginAnimationscommitAnimations方法来创建一个动画块:

[UIView beginAnimations:nil context:context];
[UIView setAnimationDuration:1.0]; 
yourView.alpha = 0.0;
[UIView commitAnimations];

这个东西非常好用,一旦你开始使用它,就要小心不要上瘾。 ;)

评论更新:

您可以将弹珠的位置绑定到触摸事件的位置。一旦您获得touchesEnded事件,就可以使用动画块来动画化弹珠的位置。


是的,但问题是我不想与持续时间有任何关系。想象一下弹珠。如果我什么也不做,它就什么也不做。如果我慢慢推它,它就会慢慢移动;如果我快速推它,它就会快速移动。如果我在快速移动后突然停止推它,它应该继续滚动一段时间,直到减速停止。似乎时间方法无法实现这一点。我使用CAKeyframeAnimation制作了一些漂亮的平滑动画,但除了启动和设置缓入/缓出之外,我无法控制它。 - Adam Eberbach
仍然存在一个问题。对象的位置不是触摸的位置,它们必须保持在路径中。触摸的笔画只有在正确的视图中发生触摸时才会使对象移动。对象必须遵循一条路径。此外,计算位置并以明显的方式执行这项任务并不够流畅,特别是当位置计算变得昂贵或在旧的iOS设备上时。 - Adam Eberbach
@adam-eberbach 哦!我明白你的意思了。正是触摸事件之间的插值引起了问题。我会考虑一下的。绝对不是一件简单的事 :) - RedBlueThing

0

我并不完全确定我理解你想做什么,但是...为什么不在用户手指所在的位置绘制弹珠?不需要动画。 如果你想让弹珠在用户松开手后继续移动,你需要保持动量的概念,并使用它来使弹珠减速动画。你可以使用CoreAnimation或定时器。


0

我会在触摸移动中进行速度计算,并将其添加到一个本地变量中,该变量可以通过定时器由块突变。

换句话说,在touches moved中进行速度计算时考虑先前速度的方向。在触摸结束时,触发一个块,将“大理石”随着您所需的速度下降进行转换。如果用户再次移动大理石,则修改局部变量,这将反过来加快大理石的动画或减慢/改变方向,具体取决于触摸的方向。


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