Layer Animations 综述
前两篇文章已经介绍了 UIView 层面的动画内容已经足够应对大多数情况,但是面对更高的性能和更大的复杂度的场景则远远不够。 所以接下来我们将视角伸向更底层的 Core Animation,看一看 iOS 动画世界的基石。 Core Animation 的主体内容大致可分为两类:Layer、Animation。为了与前文更好的衔接,我们先来看 Animation 部分。下图是 Animation 的结构图:
CAAnimation 是整个动画的基础,他派生出了 CAAnimationGroup、CATransition、CAPropertyAnimation。而 CAPropertyAnimation 进一步派生了三个最常用的动画类:CABasicAnimation、CAKeyframeAnimation、CASpringAnimation。CAMediaTiming 协议中则包含了动画时间线相关的所有重要属性。CAMediaTimingFunction 负责时间线上的进出场效果处理。CAAnimationDelegate 则是作为所有动画的代理。 准备好了吗?一起接受动画洗礼吧。 CABasicAnimationCABasicAnimation 作为基础动画使用频次最高,它通过更改 CALayer 中各种基础属性来进行动画实现。不过这里可用于动画的属性远比 UIView 层面要多得多。除了位置、大小、旋转角度等,还可以对边框、阴影、寄宿图进行设置。 例如最简单的位置改变动画,我们都不需要在 viewWillAppear 中提前设置状态,直接在 viewDidAppear 中添加代码: override func viewDidAppear(_ animated: Bool) { ... let flyRight = CABasicAnimation(keyPath: "position.x" ) flyRight.fromValue = -view.bounds.size.width / 2 flyRight.toValue = view.bounds.size.width / 2 flyRight.duration = 0.5 userAvator?.layer.add(flyRight,forKey: nil) } 效果如下: 当然我们也可以像之前一样设置状态然后再动画: override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) userAvator?.center.x = -view.bounds.size.width / 2 } override func viewDidAppear(_ animated: Bool) { ... let flyRight = CABasicAnimation(keyPath: "position.x" ) flyRight.toValue = view.bounds.size.width / 2 flyRight.duration = 0.5 userAvator?.layer.add(flyRight,forKey: nil) }
但是此时你就会发现一个问题,userAvator 在动画介绍后并没有出现在屏幕中。 在之前我们提过,动画过程中我们所见的并不是 Layer Tree 而是 Presentation Tree 所代表的瞬时值,而 Presentation Tree 又是由私有的 Render Tree 进行渲染的。所以我们能看见不过是 Presentation Tree 呈现的动画过程,而对象本身的状态并没有事实上的改变, userAvator 还是处于预设状态的视图之外。为了解决这个问题,需要更细粒度的动画控制。 CAMediaTiming、CAMediaTimingFunction 进行更精确的控制。CAMediaTiming 作为协议主要负责对动画的时间线进行刻画,主要包含以下属性:
其中 fillMode 必须在动画的 isRemovedOnCompletion = false 的情况下才会生效。它共有四个可选值:
所以,上一个问题我们可以如此修复: override func viewDidAppear(_ animated: Bool) { ... flyRight.isRemovedOnCompletion = false flyRight.fillMode = kCAFillModeForwards // 另外可以进行延迟设置 flyRight.beginTime = CACurrentMediaTime() + 0.3 } 除了 CAMediaTiming ,我们也可以通过 CAMediaTimingFunction 对动画的 timingFunction 属性进出场效果进行设置:
CASpringAnimationCASpringAnimation 作为 CABasicAnimation 的子类,为了更好的模拟阻尼简谐运动的效果该类提供了很多与之相关的属性:
override func viewDidAppear(_ animated: Bool) { let flyRight = CASpringAnimation(keyPath: "transform.scale" ) flyRight.fromValue = 1 flyRight.toValue = 1.25 flyRight.damping = 10 flyRight.mass = 5 flyRight.stiffness = 5 flyRight.initialVelocity = 10 flyRight.duration = flyRight.settlingDuration userAvator?.layer.add(flyRight,forKey: nil) }
CAKeyframeAnimation与之前 UIView 中的关键帧动画一样,CAKeyframeAnimation 负责提取 Layer 中的动画关键帧。该动画中的属性值包括:
简单的示例: override func viewDidAppear(_ animated: Bool) { ... let flyRight = CAKeyframeAnimation(keyPath: "transform.rotation" ) flyRight.duration = 1.0 flyRight.values = [0,-M_PI_4,-M_PI_2,-M_PI_4 * 3,-M_PI,-M_PI_4 * 5,-M_PI_2 * 3,-M_PI * 2] flyRight.keyTimes = [0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8] userAvator?.layer.add(flyRight,forKey: nil) }
CATransition顾名思义 CATransition 就是为了实现 Layer 的转场过渡效果而设计的动画类。该动画让视图的添加过程显得更加自然,具体的动画属性:
type 提供以下取值:
subType 包含以下取值:
示例代码,与效果图: override func viewDidAppear(_ animated: Bool) { ... let flyRight = CATransition() flyRight.duration = 1.0 flyRight.type = "rippleEffect" userAvator?.layer.add(flyRight,forKey: nil) }
CAAnimationGroup如果你足够敏锐那么应该注意到上面所有的示例代码中,动画都是针对某一个特定属性的修改或者动作。那么对于同时进行多个动画怎么办呢?当然你也可以通过 add(_ anim: CAAnimation,forKey key: String?) 函数反复在 Layer 上添加动画来实现,但是理想的方式是使用 CAAnimationGroup 将多个动画组合起来再添加到 Layer 中。 我们只需要将那些单独的动画添加到动画数组 animations 中,其余的事情 CAAnimationGroup 会很好的完成。 示例代码与效果图如下: override func viewDidAppear(_ animated: Bool) { ... let spring = CASpringAnimation(keyPath: "transform.scale" ) spring.fromValue = 1 spring.toValue = 1.25 spring.damping = 5 spring.mass = 1 spring.stiffness = 2 spring.initialVelocity = 10 spring.duration = spring.settlingDuration let keyframe = CAKeyframeAnimation(keyPath: "transform.rotation" ) keyframe.duration = spring.settlingDuration keyframe.values = [0,-M_PI * 2] keyframe.keyTimes = [0,0.8] let flyRight = CAAnimationGroup() flyRight.animations = [spring,keyframe] flyRight.duration = spring.settlingDuration userAvator?.layer.add(flyRight,forKey: nil) }
总结文章到此,相信大家对 Core Animation 框架中的几种动画应该有了大致的了解。当然这些动画还有很懂的细节等待发掘,例如:关键帧动画中的几种插值模拟,读者可以自己去探索。Core Animation 特殊 Layer 部分的内容我们留着下篇再讲。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- ajax 简单实例
- Cocos 2d-x 3.6 touch事件只有began 坑~
- react-native:如何更新构建工具23.0.2?
- ruby-on-rails – 如何使用Draper 0.14.0访问装饰器规范文件
- 什么是DOJO?
- 安装Oracle11g_win32,以及遇到的问题和解决方案
- c# – TreeView.ItemContainerGenerator.ContainerFromItem
- 哪个命令会从Oracle中的SQLServer替换IDENTITY INSERT ON /
- ruby-on-rails-3 – Rails未定义方法’model_name’
- 从C#中的ConcurrentQueue中取出队列对象