圆形到矩形变换动画
Circle to rectangle transformation animation
我是 iOS 的新手,我需要制作以下动画:
圆形到矩形的转换应该是平滑的,但在上面的动画中它不是很平滑。
我所做的是使用以下代码创建一个圆形和一个矩形 in this tutorial:
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)
}
但是我不知道怎么做我的动画,请问谁能帮帮我吗?
为了获得流畅的动画效果,您想查看 cornerRadius
属性 的动画,而不是乱用贝塞尔曲线路径。
所以动画将是这样的:
- 将角半径从当前值设置为零
- 动画宽度达到屏幕宽度
- 反转宽度动画
- 反转角半径动画
那么,让我们开始定义一些我们将要使用的属性:
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)
...
在这里我们定义了我们将要使用的图层、动画、其中一个动画的持续时间 'segments' 和 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
}
这里没什么难的,我们只需定义图层和动画属性。我还添加了一些计时功能,以使动画看起来漂亮流畅,而不是线性的。
接下来,让我们开始我们的动画吧。我将在 touchesBegan
函数中执行此操作,但您可以将其放在任何地方。
override func touchesBegan(touches: Set<UITouch>, 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()
}
这里我们添加 width
和 cornerRadius
动画,为宽度动画指定延迟开始。
你问的 CATransation
是怎么回事?好吧,一旦 cornerRadius
动画结束,Core Animation 就会将该层捕捉回它的表示层。我们不希望这样,所以我们将直接设置该值,同时还要确保 Core Animation 在我们这样做时不会添加隐式动画。使用 CATransaction
可以避免这种情况,因为 使用 removedOnCompletion = false
和 fillMode = 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()
}
同样,我们使用 CATransaction
将 cornerRadius
设置回其原始值。就是这样!
最终结果
完整项目:https://github.com/hamishknight/Circle-to-Rect-Animation
如果您希望同时同时进行放大和缩小角半径,您可以显着简化 .
您现在不再需要将动画 'chain' 放在一起,因此您可以将它们同时添加到一个 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
}
最后,我们可以 运行 触摸视图时的组动画,两个动画将同时 运行 在一起(完成后 auto-reverse)。
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
animLayer.addAnimation(groupAnim, forKey: "anims") // runs both animations concurrently
}
结果
完整项目:https://github.com/hamishknight/Circle-to-Rect-Animation
我是 iOS 的新手,我需要制作以下动画:
圆形到矩形的转换应该是平滑的,但在上面的动画中它不是很平滑。
我所做的是使用以下代码创建一个圆形和一个矩形 in this tutorial:
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)
}
但是我不知道怎么做我的动画,请问谁能帮帮我吗?
为了获得流畅的动画效果,您想查看 cornerRadius
属性 的动画,而不是乱用贝塞尔曲线路径。
所以动画将是这样的:
- 将角半径从当前值设置为零
- 动画宽度达到屏幕宽度
- 反转宽度动画
- 反转角半径动画
那么,让我们开始定义一些我们将要使用的属性:
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)
...
在这里我们定义了我们将要使用的图层、动画、其中一个动画的持续时间 'segments' 和 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
}
这里没什么难的,我们只需定义图层和动画属性。我还添加了一些计时功能,以使动画看起来漂亮流畅,而不是线性的。
接下来,让我们开始我们的动画吧。我将在 touchesBegan
函数中执行此操作,但您可以将其放在任何地方。
override func touchesBegan(touches: Set<UITouch>, 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()
}
这里我们添加 width
和 cornerRadius
动画,为宽度动画指定延迟开始。
你问的 CATransation
是怎么回事?好吧,一旦 cornerRadius
动画结束,Core Animation 就会将该层捕捉回它的表示层。我们不希望这样,所以我们将直接设置该值,同时还要确保 Core Animation 在我们这样做时不会添加隐式动画。使用 CATransaction
可以避免这种情况,因为 使用 removedOnCompletion = false
和 fillMode = 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()
}
同样,我们使用 CATransaction
将 cornerRadius
设置回其原始值。就是这样!
最终结果
完整项目:https://github.com/hamishknight/Circle-to-Rect-Animation
如果您希望同时同时进行放大和缩小角半径,您可以显着简化
您现在不再需要将动画 'chain' 放在一起,因此您可以将它们同时添加到一个 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
}
最后,我们可以 运行 触摸视图时的组动画,两个动画将同时 运行 在一起(完成后 auto-reverse)。
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
animLayer.addAnimation(groupAnim, forKey: "anims") // runs both animations concurrently
}
结果
完整项目:https://github.com/hamishknight/Circle-to-Rect-Animation