Adr*_*ian 5 2d core-animation core-graphics objective-c cashapelayer
有没有一种很好的方法来计算给定时间(从0到1)的路径(CGPath或UIBezierPath)的位置?
例如,使用CAShapeLayer可以创建动画笔画结束.我想在任意时间知道那个笔画结束的位置.
先谢谢,阿德里安
您绝对可以将方法基于CADisplayLink和跟踪层。但是,如果您不介意自己做一点数学,那么解决方案就不会太复杂。另外,您不必依赖设置显示链接和额外的图层。实际上,您甚至不必依赖QuartzCore。
以下内容适用于任何CGPathRef。如果是UIBezierPath,则获取相同的CGPath属性:
CGPathApply在要自检的路径上使用CGPathApplierFunction。CGPathApplierFunction将为该路径的每个组件调用。CGPathElement(应用程序的参数)将告诉您路径元素的种类以及构成该元素的点(控制点或端点)。kCGPathElementMoveToPoint,kCGPathElementAddLineToPoint,kCGPathElementAddQuadCurveToPoint并kCGPathElementAddCurveToPoint分别。CGPathApply每个路径使用一次,此步骤将非常快。现在,进入数学:
t,获取元素(稍后会详细介绍)及其组成点。kCGPathElementMoveToPoint,则为线性插值p0 + t * (p1 - p0)(用于x和y)kCGPathElementAddQuadCurveToPoint,则其二次((1 - t) * (1 - t)) * p0 + 2 * (1 - t) * t * p1 + t * t * p2kCGPathElementAddCurveToPoint,则其立方贝塞尔曲线((1 - t) * (1 - t) * (1 - t)) * p0 + 3 * (1 - t) * (1 - t) * t * p1 + 3 * (1 - t) * t * t * p2 + t * t * t * p3现在的问题仍然存在,您如何及时确定路径元素t。您可以假定每个路径元素都具有相等的时间片,或者可以计算每个元素的距离并考虑小数时间(前一种方法对我来说效果很好)。同样,不要忘记为所有先前的路径元素添加时间(您不必为这些元素添加插值)。
就像我说的那样,这仅是出于完整性(可能是Apple如何自行解决此问题),并且仅当您愿意进行数学计算时。
在马特的显示链接答案的基础上,您可以通过创建第二个“不可见”关键帧动画来跟踪终点的位置。
笔记:
我们从 3 个属性开始:
@interface ViewController ()
@property (nonatomic, strong) CADisplayLink *displayLink;
@property (nonatomic, strong) CAShapeLayer *pathLayer;
@property (nonatomic, strong) CALayer *trackingLayer;
@end
Run Code Online (Sandbox Code Playgroud)
这displayLink将允许我们在每次屏幕更新时运行代码。它pathLayer提供了我们将制作动画的视觉效果。它trackingLayer提供了一个不可见的层,我们将使用它来跟踪strokeEnd动画在 上的位置pathLayer。
我们像这样打开视图控制器:
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[self createDisplayLink];
[self createPathLayer];
[self createTrackingLayer];
[self startAnimating];
}
...
Run Code Online (Sandbox Code Playgroud)
通过以下方法...
我们首先创建显示链接并将其添加到运行循环中(根据 Matt 的代码):
-(void)createDisplayLink {
_displayLink = [CADisplayLink
displayLinkWithTarget:self
selector:
@selector(displayLinkDidUpdate:)];
[_displayLink
addToRunLoop:[NSRunLoop mainRunLoop]
forMode:NSDefaultRunLoopMode];
}
Run Code Online (Sandbox Code Playgroud)
然后我们创建可见层:
-(void)createPathLayer {
//create and style the path layer
//add it to the root layer of the view controller's view
_pathLayer = [CAShapeLayer layer];
_pathLayer.bounds = CGRectMake(0,0,100,100);
_pathLayer.path = CGPathCreateWithEllipseInRect(_pathLayer.bounds, nil);
_pathLayer.fillColor = [UIColor clearColor].CGColor;
_pathLayer.lineWidth = 5;
_pathLayer.strokeColor = [UIColor blackColor].CGColor;
_pathLayer.position = self.view.center;
[self.view.layer addSublayer:_pathLayer];
}
Run Code Online (Sandbox Code Playgroud)
然后我们创建一个“不可见”(即通过没有尺寸的框架)层来跟踪:
-(void)createTrackingLayer {
_trackingLayer = [CALayer layer];
//set the frame (NOT bounds) so that we can see the layer
//uncomment the following two lines to see the tracking layer
//_trackingLayer.frame = CGRectMake(0,0,5,5);
//_trackingLayer.backgroundColor = [UIColor redColor].CGColor;
//we add the blank layer to the PATH LAYER
//so that its coordinates are always in the path layer's coordinate system
[_pathLayer addSublayer:_trackingLayer];
}
Run Code Online (Sandbox Code Playgroud)
然后我们创建一个获取跟踪层位置的方法:
- (void)displayLinkDidUpdate:(CADisplayLink *)sender {
//grab the presentation layer of the blank layer
CALayer *presentationLayer = [_trackingLayer presentationLayer];
//grab the position of the blank layer
//convert it to the main view's layer coordinate system
CGPoint position = [self.view.layer convertPoint:presentationLayer.position
fromLayer:_trackingLayer];
//print it out, or do something else with it
NSLog(@"%4.2f,%4.2f",position.x,position.y);
}
Run Code Online (Sandbox Code Playgroud)
...以及startAnimating方法:
-(void)startAnimating {
//begin the animation transaction
[CATransaction begin];
//create the stroke animation
CABasicAnimation *strokeEndAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
//from 0
strokeEndAnimation.fromValue = @(0);
//to 1
strokeEndAnimation.toValue = @(1);
//1s animation
strokeEndAnimation.duration = 10.0f;
//repeat forever
strokeEndAnimation.repeatCount = HUGE_VAL;
//ease in / out
strokeEndAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
//apply to the pathLayer
[_pathLayer addAnimation:strokeEndAnimation forKey:@"strokeEndAnimation"];
//NOTE: we don't actually TRACK above animation, its there only for visual effect
//begin the follow path animation
CAKeyframeAnimation *followPathAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
//set the path for the keyframe animation
followPathAnimation.path = _pathLayer.path;
//add an array of times that match the NUMBER of points in the path
//for custom paths, you'll need to know the number of points and calc this yourself
//for an ellipse there are 5 points exactly
followPathAnimation.keyTimes = @[@(0),@(0.25),@(0.5),@(0.75),@(1)];
//copy the timing function
followPathAnimation.timingFunction = strokeEndAnimation.timingFunction;
//copy the duration
followPathAnimation.duration = strokeEndAnimation.duration;
//copy the repeat count
followPathAnimation.repeatCount = strokeEndAnimation.repeatCount;
//add the animation to the layer
[_trackingLayer addAnimation:followPathAnimation forKey:@"postionAnimation"];
[CATransaction commit];
}
Run Code Online (Sandbox Code Playgroud)
如果您有想要遵循的路径,但又不想自己进行数学计算,那么这种技术非常有用。
一些有价值的原因是:
编辑 这是 github 存储库的链接:https ://github.com/C4Code/layerTrackPosition
这是我的模拟器的图像:
