圆到矩形变换动画

我是iOS新手,我需要做以下动画:

在此处输入图像描述

将圆转换为矩形应该是平滑的,但在上面的动画中它不是很平滑。

我所做的是使用本教程中的以下代码创建一个圆和一个矩形:

Circle : class OvalLayer: CAShapeLayer { let animationDuration: CFTimeInterval = 0.3 override init() { super.init() fillColor = Colors.red.CGColor path = ovalPathSmall.CGPath } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } var ovalPathSmall: UIBezierPath { return UIBezierPath(ovalInRect: CGRect(x: 50.0, y: 50.0, width: 0.0, height: 0.0)) } var ovalPathLarge: UIBezierPath { return UIBezierPath(ovalInRect: CGRect(x: 2.5, y: 17.5, width: 95.0, height: 95.0)) } var ovalPathSquishVertical: UIBezierPath { return UIBezierPath(ovalInRect: CGRect(x: 2.5, y: 20.0, width: 95.0, height: 90.0)) } var ovalPathSquishHorizontal: UIBezierPath { return UIBezierPath(ovalInRect: CGRect(x: 5.0, y: 20.0, width: 90.0, height: 90.0)) } func expand() { let expandAnimation: CABasicAnimation = CABasicAnimation(keyPath: "path") expandAnimation.fromValue = ovalPathLarge.CGPath// change ovalPathLarge to ovalPathSmail for animation expandAnimation.toValue = ovalPathLarge.CGPath expandAnimation.duration = animationDuration expandAnimation.fillMode = kCAFillModeForwards expandAnimation.removedOnCompletion = false addAnimation(expandAnimation, forKey: nil) } } Rectangle : class RectangleLayer: CAShapeLayer { override init() { super.init() fillColor = Colors.clear.CGColor lineWidth = 5.0 path = rectanglePathFull.CGPath } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } var rectanglePathFull: UIBezierPath { let rectanglePath = UIBezierPath() rectanglePath.moveToPoint(CGPoint(x: 0.0, y: 100.0)) rectanglePath.addLineToPoint(CGPoint(x: 0.0, y: -lineWidth)) rectanglePath.addLineToPoint(CGPoint(x: 100.0, y: -lineWidth)) rectanglePath.addLineToPoint(CGPoint(x: 100.0, y: 100.0)) rectanglePath.addLineToPoint(CGPoint(x: -lineWidth / 2, y: 100.0)) rectanglePath.closePath() // fillColor = Colors.red.CGColor return rectanglePath } // var topLeft: UIBezierPath {} func animateStrokeWithColor(color: UIColor, view : UIView) { strokeColor = color.CGColor // CATransaction.setDisableActions(true) // view.layer.bounds.size.height = view.layer.bounds.width + 50 let strokeAnimation: CABasicAnimation = CABasicAnimation(keyPath: "bounds.size.width") //bounds.size.width strokeAnimation.fromValue = view.layer.bounds.width strokeAnimation.toValue = view.layer.bounds.size.width - 50 strokeAnimation.duration = 0.4 addAnimation(strokeAnimation, forKey: nil) } } my view : protocol HolderViewDelegate:class { func animateLabel() } class HolderView: UIView { let ovalLayer = OvalLayer() let redRectangleLayer = RectangleLayer() var parentFrame :CGRect = CGRectZero weak var delegate:HolderViewDelegate? override init(frame: CGRect) { super.init(frame: frame) backgroundColor = Colors.clear } required init?(coder: NSCoder) { super.init(coder: coder) } func addOval() { layer.addSublayer(ovalLayer) ovalLayer.expand() // NSTimer.scheduledTimerWithTimeInterval(0.3, target: self, selector: "wobbleOval", // userInfo: nil, repeats: false) } func expandRectangle(){ NSTimer.scheduledTimerWithTimeInterval(0.45, target: self, selector: "drawRedAnimatedRectangle", userInfo: nil, repeats: false) } func drawRedAnimatedRectangle() { layer.addSublayer(redRectangleLayer) redRectangleLayer.animateStrokeWithColor(Colors.red,view: self) } 

但是我不知道怎么做动画,请有人帮帮我吗?

如果您希望同时进行放大和缩小半径,则可以显着简化我的其他答案中的代码

您现在不再需要将动画“链接”在一起,因此您可以将它们添加到单个CAAnimationGroup并同时运行它们。

我们使用的属性将保持几乎相同,除了添加groupAnim属性和删除cornerRadiusUndoAnim

 class ViewController2: UIViewController { let animLayer = CALayer() // the layer that is going to be animated let cornerRadiusAnim = CABasicAnimation(keyPath: "cornerRadius") // the corner radius reducing animation let widthAnim = CABasicAnimation(keyPath: "bounds.size.width") // the width animation let groupAnim = CAAnimationGroup() // the combination of the corner and width animation let animDuration = NSTimeInterval(1.0) // the duration of one 'segment' of the animation let layerSize = CGFloat(100) // the width & height of the layer (when it's a square) ... 

我们现在可以添加CAAnimationGroup的设置,添加我们的角半径动画和缩放动画

 override func viewDidLoad() { super.viewDidLoad() let rect = view.frame animLayer.backgroundColor = UIColor.blueColor().CGColor // color of the layer, feel free to change animLayer.frame = CGRect(x: rect.width-layerSize*0.5, y: rect.height-layerSize*0.5, width: layerSize, height: layerSize) animLayer.cornerRadius = layerSize*0.5; animLayer.anchorPoint = CGPoint(x: 1, y: 1) // sets so that when the width is changed, it goes to the left view.layer.addSublayer(animLayer) // decreases the corner radius cornerRadiusAnim.duration = animDuration cornerRadiusAnim.fromValue = animLayer.cornerRadius cornerRadiusAnim.toValue = 0; cornerRadiusAnim.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut) // timing function to make it look nice // increases the width widthAnim.duration = animDuration widthAnim.fromValue = animLayer.frame.size.width widthAnim.toValue = rect.size.width widthAnim.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut) // timing function to make it look nice // adds both animations to a group animation groupAnim.animations = [cornerRadiusAnim, widthAnim] groupAnim.duration = animDuration; groupAnim.autoreverses = true; // auto-reverses the animation once completed } 

最后,我们可以在触摸视图时运行组动画,并且两个动画将同时运行(并在完成时自动反转)。

 override func touchesBegan(touches: Set, withEvent event: UIEvent?) { animLayer.addAnimation(groupAnim, forKey: "anims") // runs both animations concurrently } 

结果

在此处输入图像描述


完整项目: https : //github.com/hamishknight/Circle-to-Rect-Animation

为了获得平滑的动画,您需要查看为cornerRadius属性设置动画,而不是使用bezier路径进行操作。

所以动画将会是这样的:

  1. 将角点半径从当前值设置为零
  2. 动画宽度到屏幕宽度
  3. 反向宽度动画
  4. 反转角半径动画

因此,让我们开始定义一些我们将要使用的属性:

 class ViewController: UIViewController { let animLayer = CALayer() // the layer that is going to be animated let cornerRadiusAnim = CABasicAnimation(keyPath: "cornerRadius") // the corner radius reducing animation let cornerRadiusUndoAnim = CABasicAnimation(keyPath: "cornerRadius") // the corner radius increasing animation let widthAnim = CABasicAnimation(keyPath: "bounds.size.width") // the width animation let animDuration = NSTimeInterval(1.0) // the duration of one 'segment' of the animation let layerSize = CGFloat(100) // the width & height of the layer (when it's a square) ... 

在这里,我们定义了我们将要使用的图层,动画,其中一个动画“细分”的持续时间以及CALayer的大小。

接下来,让我们在viewDidLoad设置我们的动画

 override func viewDidLoad() { super.viewDidLoad() let rect = view.frame animLayer.backgroundColor = UIColor.blueColor().CGColor // color of the layer, feel free to change animLayer.frame = CGRect(x: rect.width-layerSize*0.5, y: rect.height-layerSize*0.5, width: layerSize, height: layerSize) animLayer.cornerRadius = layerSize*0.5; animLayer.anchorPoint = CGPoint(x: 1, y: 1) // sets so that when the width is changed, it goes to the left view.layer.addSublayer(animLayer) // decreases the corner radius cornerRadiusAnim.duration = animDuration cornerRadiusAnim.fromValue = animLayer.cornerRadius cornerRadiusAnim.toValue = 0; cornerRadiusAnim.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn) // timing function to make it look nice // inverse of the cornerRadiusAnim cornerRadiusUndoAnim.duration = animDuration cornerRadiusUndoAnim.fromValue = 0; cornerRadiusUndoAnim.toValue = animLayer.cornerRadius cornerRadiusUndoAnim.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut) // timing function to make it look nice // increases the width, and autoreverses on completion widthAnim.duration = animDuration widthAnim.fromValue = animLayer.frame.size.width widthAnim.toValue = rect.size.width widthAnim.autoreverses = true widthAnim.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut) // timing function to make it look nice widthAnim.delegate = self // so that we get notified when the width animation finishes } 

这里没什么难的,我们只是定义我们的图层和动画属性。 我还添加了一些计时function,以使动画看起来更漂亮和流畅,而不是线性。

接下来,让我们开始动画。 我将在touchesBegan函数中执行此操作,但您可以将它放在任何位置。

 override func touchesBegan(touches: Set, withEvent event: UIEvent?) { widthAnim.beginTime = CACurrentMediaTime()+animDuration // starts after the corner radius anim has finished animLayer.addAnimation(widthAnim, forKey: "widthAnim") animLayer.addAnimation(cornerRadiusAnim, forKey: "cornerRadius") CATransaction.begin() CATransaction.setDisableActions(true) // disables implicit animations animLayer.cornerRadius = 0 CATransaction.commit() } 

这里我们添加widthcornerRadius动画,为宽度动画指定延迟开始。

您问的CATransation什么用? 一旦cornerRadius动画结束,Core Animation会将图层捕捉回它的表示层。 我们不希望这样,所以我们将直接设置值,同时确保Core Animation在我们这样做时不添加隐式动画。 使用CATransaction避免了这种情况,因为使用removedOnCompletion = falsefillMode = kCAFillModeForwards被认为是不好的做法。

最后,我们想要在宽度动画反转后撤消角半径动画。 我们之前可以为宽度动画分配一个delegate ,因此我们可以覆盖animationDidStop函数。

 override func animationDidStop(anim: CAAnimation, finished flag: Bool) { animLayer.addAnimation(cornerRadiusUndoAnim, forKey: "cornerRadiusUndo") CATransaction.begin() CATransaction.setDisableActions(true) animLayer.cornerRadius = layerSize*0.5 CATransaction.commit() } 

同样,我们使用CATransactioncornerRadius设置回原始值。 就是这样!


最后结果

在此处输入图像描述


完整项目: https : //github.com/hamishknight/Circle-to-Rect-Animation