简体   繁体   English

动画Bezier曲线,就像在iPhone屏幕上绘制一样

[英]Animating Bezier curve as though drawing proceeds on iPhone screen

I want to draw a bezier path of the outline of Taj Mahal's tomb. 我想绘制一条泰姬陵墓葬轮廓的bezier路径。

Q1. Q1。 How could I draw it in XCode? 我怎么能在XCode中绘制它? I could easily do that in PS but am finding it difficult to do in XCode programmatically especially getting the coordinates of the control points. 我可以轻松地在PS中做到这一点,但我发现很难在编程中以XCode方式进行,特别是获取控制点的坐标。

Q2. Q2。 Moreover, how can the coordinates of the points be fixed when the screen sizes vary? 此外,当屏幕尺寸变化时,如何固定点的坐标?

Q3. Q3。 How could I animate it as though an invisible pencil is drawing that on screen? 我怎么能像看不见的铅笔画在屏幕上那样动画呢?

How do I draw a bezier path in UIKit ? 如何在UIKit绘制bezier路径?

Create a UIBezierPath object and then use the various methods on it to construct your path . 创建一个UIBezierPath对象,然后使用它上面各种方法构建您的路径

I suspect the methods you're going to be most interested in are: 我怀疑你最感兴趣的方法是:

For example, something like this will create a simple bezier path with a quadratic curve: 例如,像这样的东西将创建一个带有二次曲线的简单贝塞尔曲线路径:

UIBezierPath* yourPath = [UIBezierPath bezierPath]; // create the bezier path
[yourPath moveToPoint:CGPointMake(100, 100)]; // move to your starting point
[yourPath addQuadCurveToPoint:CGPointMake(300, 500) controlPoint:CGPointMake(500, 350)]; // add a new quad curve to the point

However, if you're used to using something like Photoshop to create your paths, you may be interested in PaintCode , that can let you create bezier paths using a 'what you see is what you get' approach. 但是,如果您习惯使用Photoshop之类的东西来创建路径,那么您可能会对PaintCode感兴趣 ,它可以让您使用“ 所见即所得 ”的方法创建贝塞尔路径。

You can add this UIBezierPath to the path property of a CAShapeLayer in order to display it on screen. 您可以将它添加UIBezierPathpath一个财产CAShapeLayer ,以显示它在屏幕上。

CAShapeLayer* yourShapeLayer = [CAShapeLayer layer]; // create your shape layer
yourShapeLayer.frame = CGRectMake(0, 0, 300, 500); // assign it's frame
yourShapeLayer.path = yourPath.CGPath; // add your path to the shape layer
yourShapeLayer.fillColor = [UIColor clearColor].CGColor; // prevent the shape layer from filling

[self.view.layer addSublayer:yourShapeLayer]; // add the shape layer to the view

You can then easily set the strokeColor and lineWidth properties on the shape layer in order to customise how your path gets stroked. 然后,您可以在形状图层上轻松设置strokeColorlineWidth属性,以便自定义路径的描边方式。

yourShapeLayer.strokeColor = [UIColor redColor].CGColor; // red stroke color
yourShapeLayer.lineWidth = 5.0; // 5 point stroke width

You should end up with something like this: 你应该得到这样的东西:

在此输入图像描述


How could I animate it as though an invisible pencil is drawing that on screen? 我怎么能像看不见的铅笔画在屏幕上那样动画呢?

You can easily create a CABasicAnimation that can animate the strokeStart or strokeEnd property of the CAShapeLayer in order to achieve your desired animation. 您可以轻松创建一个CABasicAnimation可以动画strokeStartstrokeEnd的财产CAShapeLayer ,以达到您想要的动画。

This can result in the animation effect of the line 'getting drawn' onto the screen. 这可能会导致线条“被绘制”到屏幕上的动画效果。

For example, this code will result in the strokeEnd property animating from 0.0 to 1.0 , creating your desired effect: 例如,此代码将导致strokeEnd属性设置为0.01.0动画,从而创建所需的效果:

yourShapeLayer.strokeStart = 0.0; // reset stroke start before animating

CABasicAnimation* strokeAnim = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; // create animation that changes the strokeEnd property
strokeAnim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; // give it a nice timing function
strokeAnim.duration = 2.0; // duration of the animation
strokeAnim.fromValue = @(0.0); // provide the start value for the animation, wrapped in an NSNumber literal.
strokeAnim.toValue = @(1.0); // provide the end value for the animation, wrapped in an NSNumber literal.

[yourShapeLayer addAnimation:strokeAnim forKey:@"strokeAnim"]; // add animation to layer

在此输入图像描述


How can I scale this path to work with different sized screens? 如何缩放此路径以使用不同大小的屏幕?

The way I usually like to solve this problem is to define the path's points in a given fixed frame (let's say 500 x 500 points), and then generate a scale factor based on the size of the screen (in your case, you want to scale to the width of the screen). 我通常喜欢解决这个问题的方法是在给定的固定帧中定义路径点 (比如说500 x 500点),然后根据屏幕大小生成比例因子 (在你的情况下,你想要缩放到屏幕的宽度)。

So all we have to do is generate our scale factor. 所以我们要做的就是生成比例因子。

CGFloat sf = self.view.frame.size.width/500.0; // The factor to scale the path by

Then, we just have to multiple all our points in the UIBezierPath by this scale factor. 然后,我们只需通过此比例因子将UIBezierPath中的所有点UIBezierPath For example, taking our previous path: 例如,采用我们之前的路径:

UIBezierPath* yourPath = [UIBezierPath bezierPath]; // create the bezier path
[yourPath moveToPoint:CGPointMake(100.0*sf, 100.0*sf)]; // move to your starting point
[yourPath addQuadCurveToPoint:CGPointMake(300.0*sf, 500.0*sf) controlPoint:CGPointMake(500.0*sf, 350.0*sf)];

Now, all we have to do is program in our bezier path points as if we were always drawing it in a 500 x 500 frame (my points already fit nicely within that). 现在,我们所要做的就是在我们的bezier路径点进行编程, 好像我们总是在500 x 500帧中绘制它(我的点已经很好地适应了它)。

You'll also want to change the size of your CAShapeLayer so that it's squared and occupies the entire width of the screen. 您还需要更改CAShapeLayer的大小,使其平方并占据整个屏幕宽度。

yourShapeLayer.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.width);

Finally, you want to position the CAShapeLayer to the center of the screen. 最后,您希望将CAShapeLayer定位到屏幕的中心。 You can do this easily by setting the position property of the layer to the center of the view. 您可以通过将图层的position属性设置为视图的center来轻松完成此操作。

yourShapeLayer.position = self.view.center;

Now your path should always scale to occupy the screen's width. 现在,您的路径应始终缩放以占据屏幕的宽度。

在此输入图像描述

(I added a gray background to the layer just to illustrate the scaling) (我在图层中添加了灰色背景,仅用于说明缩放)


Full project (for your convenience): https://github.com/hamishknight/Drawing-Scaling-Animating-UIBezierPaths 完整项目(为方便起见): https//github.com/hamishknight/Drawing-Scaling-Animating-UIBezierPaths

I blogged about this in 2010: Animating the Drawing of a CGPath With CAShapeLayer . 我在2010年写了这篇博文: 使用CAShapeLayer动画绘制CGPath

The gist of it is this: 它的要点是这样的:

  • Create a UIBezierPath or CGPath with the shape you want to draw. 使用您要绘制的形状创建UIBezierPathCGPath Use the methods moveToPoint: , addLineToPoint: , addCurveToPoint:controlPoint1:controlPoint2: etc. to create the shape. 使用方法moveToPoint:addLineToPoint: , addCurveToPoint:controlPoint1:controlPoint2: addLineToPoint: addCurveToPoint:controlPoint1:controlPoint2:等来创建形状。
  • Create a CGShapeLayer and assign the path to the layer's path property. 创建CGShapeLayer并指定图层path属性的path
  • Add the layer to your view/layer hierarchy (eg in a view controller: [self.view.layer addSublayer:shapeLayer]; ). 将图层添加到视图/图层层次结构中(例如,在视图控制器中: [self.view.layer addSublayer:shapeLayer]; )。 You also have to give the layer a valid size and position ( frame ). 您还必须为图层指定有效的大小和位置( frame )。
  • Animate the layer's strokeEnd property from 0 to 1. 将图层的strokeEnd属性设置为0到1。

The answer posted by originaluser2 solves everything. originaluser2发布的答案解决了一切。 You can also try this way, if you want to resize the bezier path for different dievice screens: 如果要为不同的设备屏幕调整贝塞尔曲线路径,也可以尝试这种方式:

You can simply subclass the UIView and customise its drawRect method: 您可以简单地继承UIView并自定义其drawRect方法:

  - (void)drawRect: (CGRect)frame
{

    //// Bezier Drawing
UIBezierPath* bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint: CGPointMake(CGRectGetMinX(frame) + 0.23370 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.65441 * CGRectGetHeight(frame))];
[bezierPath addCurveToPoint: CGPointMake(CGRectGetMinX(frame) + 0.49457 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.33088 * CGRectGetHeight(frame)) controlPoint1: CGPointMake(CGRectGetMinX(frame) + 0.23370 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.65441 * CGRectGetHeight(frame)) controlPoint2: CGPointMake(CGRectGetMinX(frame) + 0.35870 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.33088 * CGRectGetHeight(frame))];
[bezierPath addCurveToPoint: CGPointMake(CGRectGetMinX(frame) + 0.77717 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.65441 * CGRectGetHeight(frame)) controlPoint1: CGPointMake(CGRectGetMinX(frame) + 0.63043 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.33088 * CGRectGetHeight(frame)) controlPoint2: CGPointMake(CGRectGetMinX(frame) + 0.77717 * CGRectGetWidth(frame), CGRectGetMinY(frame) + 0.65441 * CGRectGetHeight(frame))];
[UIColor.blackColor setStroke];
bezierPath.lineWidth = 1;
[bezierPath stroke];

}

frame 50x50: 框架50x50:

在此输入图像描述

frame 100x100: 框架100x100:

在此输入图像描述

frame 100x50 框架100x50

在此输入图像描述

This draws a bezier curve in a frame of size 50x50 if you send frame of size 50x50. 如果您发送大小为50x50的帧,则会在大小为50x50的帧中绘制贝塞尔曲线。 Also this code get resized automatically based on the frame size it recieves. 此代码也会根据收到的帧大小自动调整大小。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM