测试已测试 | ✓ |
语语言 | Obj-CObjective C |
许可证 | MIT |
发布最新版本 | 2017年10月 |
由 Jeff Hurray,Jeff Hurray 维护。
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
CAAnimations 和 UIView 动画非常强大,但将多个动画链接在一起困难,尤其是在更改锚点时。
此外,复杂的动画难以阅读。
比如说我想将 myView 向右移动 50 像素并使用弹簧,然后在移动完成后将背景颜色改为向内减速
[UIView animateWithDuration:1.0
delay:0.0
usingSpringWithDamping:0.8
initialSpringVelocity:1.0
options:0 animations:^{
CGPoint newPosition = self.myView.frame.origin;
newPosition.x += 50;
self.myView.frame.origin = newPosition;
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.5
delay:0.0
options:UIViewAnimationOptionCurveEaseIn
animations:^{
self.myView.backgroundColor = [UIColor purpleColor];
} completion:nil];
}];
这相当糟糕……使用 JHChainableAnimations 只需一行代码。
JHChainableAnimator *animator = [[JHChainableAnimator alloc] initWithView:self.myView];
animator.moveX(50).spring.thenAfter(1.0).makeBackground([UIColor purpleColor]).easeIn.animate(0.5);
还有一些非常好的动画库,例如RBBAnimation、DCAnimationKit 和PMTween,但它们仍然缺乏强大且易于阅读/编写的链式动画语法。
您可以将此框架添加到项目的几种方式。Objective-C 框架名为 JHChainableAnimations
,Swift 框架名为 ChainableAnimations
。有关 Swift 使用的更多说明,请参阅此处
pod 'JHChainableAnimations', '~> 3.0.1'
然后添加以下内容
#import <JHChainableAnimations/JHChainableAnimations.h>
pod 'ChainableAnimations', '~> 3.0.1'
然后添加以下内容
import ChainableAnimations
将 JHChainableAnimations
框架添加到您的项目中。
将 ChainableAnimations
框架添加到您的项目中。
要么克隆代码库,然后手动将 JHChainableAnimations 中的文件添加到项目中。
要创建一个 JHChainableAnimator
实例,您必须调用 initWithView:
方法。
JHChainableAnimator *animator = [[JHChainableAnimator alloc] initWithView:self.myView];
链式属性如 moveX(x)
必须位于视图和 animate(t)
函数之间
下面是如何在 1 秒内将对象的大小加倍的一个示例。
animator.makeScale(2.0).animate(1.0);
如果您想在缩放视图的同时移动视图,请添加另一个链式属性。顺序不重要
animator.makeScale(2.0).moveXY(100, 50).animate(1.0);
// the same as animator.moveXY(100, 50).makeScale(2.0).animate(1.0);
链式属性的完整列表可以在此处找到
要链式连接动画,请使用 thenAfter(t)
函数分隔链式。
以下是一个例子,展示了如何在0.5秒内缩放一个对象,然后在完成缩放后移动它1秒。
animator.makeScale(2.0).thenAfter(0.5).moveXY(100, 50).animate(1.0);
要添加动画效果,请在要应用动画的链式属性之后调用效果方法。
以下是一个使用弹簧效果缩放视图的例子。
animator.makeScale(2.0).spring.animate(1.0);
如果将2个相同的链式属性添加到同样的链式属性,第二个将取消第一个的效果。
animator.makeScale(2.0).bounce.spring.animate(1.0);
// The same as animator.makeScale(2.0).spring.animate(1.0);
这里可以找到完整的动画效果属性列表。
为了锚定视图,在动画链中的某个时刻调用锚定方法。就像效果一样,在同一个链中依次调用将会取消第一个的效果。
以下是一个围绕不同锚定点旋转视图的例子。
animator.rotateZ(180).anchorTopLeft.thenAfter(1.0).rotateZ(90).anchorCenter.animate(1.0);
// animator.rotateZ(90).anchorTopLeft.anchorCenter == animator.rotateZ(90).anchorCenter
这里可以找到锚定属性的完整列表。
要延迟动画,请调用链式属性 wait(t)
或 delay(t)
。
以下是一个在0.5秒延迟后移动视图的例子。
animator.moveXY(100, 50).wait(0.5).animate(1.0);
// The same as animator.moveXY(100, 50).delay(0.5).animate(1.0);
要动画结束后执行代码,请设置您的动画器的 completionBlock
属性,或者调用 animateWithCompletion(t, completion)
函数。
animator.makeX(0).animateWithCompletion(1.0, ^{
NSLog(@"Animation Done");
});
它与以下相同:
animator.completionBlock = ^{
NSLog(@"Animation Done");
};
animator.makeX(0).animate(1.0);
您可以通过用 repeat(time, count)
方法替换 thenAfter(time)
方法来重复动画。这将重复之前定义的动画。
// The animator will double its scale 3 times for 0.5 seconds each before it calls `moveXY` and finishes the animation
animator.makeScale(2.0).repeat(0.5, 3).moveXY(100, 50).animate(1.0);
通过调用 animateWithRepeat(time, count)
,您可以重复动画的最后一部分。
// The animator will double its scale then rotate by 90 degrees 3 times for 1 second each.
animator.makeScale(2.0).thenAfter(0.5).rotate(90). animateWithRepeat(1.0, 3);
要暂停动画,请调用动画器上的 pause
方法。调用暂停时,链中的当前动画将完成,但将不会执行任何后续操作。您可以使用 isPaused
和 isAnimating
只读属性来检查状态。如果一个动画被暂停但没有停止,它仍然会被评估为 animating
。
要从中断的状态重新开始,请调用动画器上的 resume
方法。
要停止动画并清除状态,请调用动画器上的 stop
方法。
// In this case the `moveX` animation will execute but the `moveY` will not
// If `resume` is called `moveY` will be executed
// If `stop` is called, nothing will be executed and the animator will get a fresh state
animator.moveX(10).thenAfter(0.5).moveY(10).animate(0.5);
[animator pause];
您可以通过调用 preAnimationBlock(block)
、animationBlock(block)
和 postAnimationBlock(block)
方法来勾入动画过程的不同步骤。所有这些方法都接受一个简单的块 void(^)()
参数。在动画链中调用这些方法的顺序不重要。
animator.moveX(10).preAnimationBlock(^{
NSLog(@"before the first animation");
}).thenAfter(1.0).postAnimationBlock(^{
NSLog(@"After the second animation");
}).moveY(10).animate(1.0);
您还可以让视图沿着 UIBezierPath 动画。创建一个 UIBezierPath *
实例,然后向它添加点、曲线或线条,并在链式属性中使用它。
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:self.myView.center];
[path addLineToPoint:CGPointMake(25, 400)];
[path addLineToPoint:CGPointMake(300, 500)];
animator.moveOnPath(path).animate(1.0);
动画效果不作用于路径运动。
使用 transform 链式属性。这些属性更适合与 AutoLayout 约束的视图一起使用。您不应该将它们与其他链式属性混合使用
animatorForViewWithConstraints.transformX(50).transformScale(2).animate(1.0);
现在使用带有 Swift 的 JHChainableAnimations 在版本 2.x
中稍微更易读。我创建了一个独立的 Swift 框架,其中包含一个名为 ChainableAnimator
的类。这是一个 very thin wrapper over JHChainableAnimator
,拥有略为易读的语法。
let animator = ChainableAniamtor(view: myView)
animator.moveX(x: 50).thenAfter(t: 1.0).rotate(angle: 360).bounce.animate(t:1.0)
所有 Objective-C 方法都映射到了一个 Swift 方法。
属性 | 采取... | 用法 |
---|---|---|
- (JHChainableRect) makeFrame; | CGRect | animator.makeFrame(rect).animate(1.0); |
- (JHChainableRect) makeBounds; | CGRect | animator.makeBounds(rect).animate(1.0); |
- (JHChainableSize) makeSize; | (CGFloat: width, CGFloat: height) | animator.makeSize(10, 20).animate(1.0); |
- (JHChainablePoint) makeOrigin; | (CGFloat: x, CGFloat: y) | animator.makeOrigin(10, 20).animate(1.0); |
- (JHChainablePoint) makeCenter; | (CGFloat: x, CGFloat: y) | animator.makeCenter(10, 20).animate(1.0); |
- (JHChainableFloat) makeX; | (CGFloat: f) | 动画器.makeX(10).animate(1.0); |
- (JHChainableFloat) makeY; | (CGFloat: f) | 动画器.makeY(10).animate(1.0); |
- (JHChainableFloat) makeWidth; | (CGFloat: f) | 动画器.makeWidth(10).animate(1.0); |
- (JHChainableFloat) makeHeight; | (CGFloat: f) | 动画器.makeHeight(10).animate(1.0); |
- (JHChainableFloat) makeOpacity; | (CGFloat: f) | 动画器.makeOpacity(10).animate(1.0); |
- (JHChainableColor) makeBackground; | (UIColor: 颜色) | 动画器.makeBackground(颜色).animate(1.0); |
- (JHChainableColor) makeBorderColor; | (UIColor: 颜色) | 动画器.makeBorderColor(颜色).animate(1.0); |
- (JHChainableFloat) makeBorderWidth; | (CGFloat: f) | 动画器.makeBorderWidth(3.0).animate(1.0); |
- (JHChainableFloat) makeCornerRadius; | (CGFloat: f) | 动画器.makeCornerRadius(3.0).animate(1.0); |
- (JHChainableFloat) makeScale; | (CGFloat: f) | 动画器.makeScale(2.0).animate(1.0); |
- (JHChainableFloat) makeScaleX; | (CGFloat: f) | 动画器.makeScaleX(2.0).animate(1.0); |
- (JHChainableFloat) makeScaleY; | (CGFloat: f) | 动画器.makeScaleY(2.0).animate(1.0); |
- (JHChainablePoint) makeAnchor; | (CGFloat: x, CGFloat: y) | 动画器.makeAnchor(0.5, 0.5).animate(1.0); |
- (JHChainableFloat) moveX; | (CGFloat: f) | 动画器.moveX(50).animate(1.0) |
- (JHChainableFloat) moveY; | (CGFloat: f) | 动画器.moveY(50).animate(1.0) |
- (JHChainablePoint) moveXY; | (CGFloat: x, CGFloat: y) | 动画器.moveXY(100, 50).animate(1.0) |
- (JHChainableFloat) moveHeight; | (CGFloat: f) | 动画器.moveHeight(50).animate(1.0) |
- (JHChainableFloat) moveWidth; | (CGFloat: f) | 动画器.moveWidth(50).animate(1.0) |
- (JHChainableDegrees) rotateX; | (CGFloat: 角度) #非弧度! | 动画器.rotateX(360).animate(1.0); |
- (JHChainableDegrees) rotateY; | (CGFloat: 角度) #非弧度! | 动画器.rotateY(360).animate(1.0); |
- (JHChainableDegrees) rotateZ; | (CGFloat: 角度) #非弧度! | 动画器.rotateZ(360).animate(1.0); |
- (JHChainablePolarCoordinate) movePolar; | (CGFloat: 半径, CGFloat: 角度) | 动画器.movePolar(30, 90).animate(1.0); |
- (JHChainableBezierPath) moveOnPath; | (UIBezierPath *path) | 动画器.moveOnPath(path).animate(1.0); |
- (JHChainableBezierPath) moveAndRotateOnPath; | (UIBezierPath *path) | 动画器.moveAndRotateOnPath(path).animate(1.0); |
- (JHChainableBezierPath) moveAndReverseRotateOnPath; | (UIBezierPath *path) | 动画器.moveAndReverseRotateOnPath(path).animate(1.0); |
- (JHChainableFloat) transformX; | (CGFloat f) | 动画器.transformX(50).animate(1.0); |
- (JHChainableFloat) transformX; | (CGFloat f) | 动画器.transformX(50).animate(1.0); |
- (JHChainableFloat) transformY; | (CGFloat f) | 动画器.transformY(50).animate(1.0); |
- (JHChainableFloat) transformZ; | (CGFloat f) | 动画器.transformZ(50).animate(1.0); |
- (JHChainablePoint) transformXY; | ( CGFloat x, CGFloat y) | 动画器.transformXY(50, 100).animate(1.0); |
- (JHChainableFloat) transformScale; | (CGFloat f) | 动画器.transformScale(50).animate(1.0); |
- (JHChainableFloat) transformScaleX; | (CGFloat f) | 动画器.transformScaleX(50).animate(1.0); |
- (JHChainableFloat) transformScaleY; | (CGFloat f) | 动画器.transformScaleY(50).animate(1.0); |
- (JHChainableAnimator *) transformIdentity; | 无内容 | 动画器.transformIdentity.animate(1.0); |
这些函数的快速查看可以在这里找到
这些动画函数是从一个很酷的关键帧动画库中获取的,可以在这里找到
它们基于可以在这里找到的jQuery缓动函数
有关锚定的信息可以在这里找到
我收到了许多关于下一步做什么的优秀建议。如果您认为这里缺少什么,请告诉我!以下是我打算按顺序开展工作的事务。
请随时发邮件给我[email protected]。我很乐意听取您的想法,或看到使用该功能的示例。