发布于 2016-02-24 05:35:06 | 202 次阅读 | 评论: 0 | 来源: 分享
UIKIt 轻量级的JS的UI框架
UIkit是YOOtheme团队开发的一款轻量级开源的前端框架,可以帮助你快速的开发和创建前端UI界面,支持LESS、模块化、自定义主题、及响应式设计。
创作我们自己的星球大战续集,对于我们最强的两种原力:设计和开发,都是个挑战。
by Kostya Trundayev
在我开始制作动画的时候,我想对标准开关控件创作一种独一无二的交互逻辑,并扩展影响到整个屏幕。我想到了一个有趣的想法,让用户通过点击该开关,进行两种不同的外观的切换。但我该使用什么界面来让两种设定显得有意义呢?
我记得星球大战,尤其是星球大战系列游戏,用户可以加入光明或黑暗的一方。天行者与黑武士。光明与黑暗。没错!这个矛盾人格的想法是多么出色,实在无法用言语形容。
我已经花了几个小时研究界面,我为每个设定档创建了两种图片拼贴,并绘制自己的界面。我使用 Photoshop 创建设定档,然后在 Adobe After Effects 中添加动画。
在两种屏幕都准备好了后,我觉得若发布在 Dribbble 它们看起太朴素了。我瞥了一眼屏幕中的黑武士,我终于想到了!画面中黑武士试图用他的手触碰十字星座。我想,“要是屏幕不只是接近,而是碎成小块,就像被原力击中一样?”这听起来很酷!我研究了在 After Effects 中如何实现,并做出了正如我想象的效果。我使用了粉碎效果,它能让我通过运用许多不同的设置实现摇摇欲坠的效果。
所以,剩下来唯一要做的事情是循环动画,因为该组件在动画结束的后,没有可供用户着陆的屏幕,并且从那里可以通过设定档的设置回到屏幕。这就是为什么我用星球大战标志创建第三个屏幕的原因,一个“设置你的个人资料”的按钮和群星璀璨的天空。我在 CC 球状动作特效的帮助下制作了该星空。
我将视频放入设备模型中,并为 Dribbble 做了个短片。但故事还没有结束。你还是可以学到更多内容的,因为伟大的设计加上天才的开发才是真正的原力觉醒。
by Artem Sydorenko
有两件事情在星球大战动画中似乎很难实现(在 Github 上浏览):在主屏幕中满天飞的星星和碎成片片的视图。我在 CAEmitterLayer 的帮助下解决了第一个挑战,它是一个提供粒子发射系统的类。然后,我有一段相当艰苦的时间,就是试图将视图破碎的 4000 个碎片以不同的方向降落。
为了将 UIView 打破成碎片,我用了下面的方法:snapshotViewAfterScreenUpdates() 和 resizableSnapshotViewFromRect (afterScreenUpdates:withCapInsets:)。我也使用了中间快照,它能让我更快创建创建小碎片(从快照创建快照比从原来视图制作非常小的快照要快得多):
let fromViewSnapshot = fromView.snapshotViewAfterScreenUpdates(false)
for x in CGFloat(0).stride(through: size.width, by: width) {
for y in CGFloat(0).stride(through: size.height, by: height) {
let snapshotRegion = CGRect(x: x, y: y, width: width, height: height)
let snapshot = fromViewSnapshot.resizableSnapshotViewFromRect(snapshotRegion, afterScreenUpdates: false, withCapInsets: UIEdgeInsetsZero)
containerView.addSubview(snapshot)
snapshot.frame = snapshotRegion
snapshots.append(snapshot)
}
}
有 3 种方式可以实现向星球大战那样的动画:通过使用 UIDynamics、Core Animation (UIKit) 或 OpenGL。我们用了全部这些方法来制作了动画,让我们来看看结果。
扩展阅读:如何在 iOS 和 Android 上创建铡刀菜单动画。
UIDynamics 是我们第一只小白鼠。我认为这个工具可以帮我实现物体自然下落的效果。它是这样子的:
animator = UIDynamicAnimator(referenceView: containerView)
for snapshot in snapshots {
let push = UIPushBehavior(items: [snapshot], mode: .Instantaneous)
push.pushDirection = CGVector(dx: randomFloatBetween(-0.15 , and: 0.15), dy: randomFloatBetween(-0.15 , and: 0))
push.active = true
animator.addBehavior(push)
}
let gravity = UIGravityBehavior(items: snapshots)
animator.addBehavior(gravity)
但是事实证明物体物理运动计算是很耗 CPU 的(我们有大量的物体)。我们不得不同时移动 4000 个对象,UIDynamics 很难维持这样的负荷。
下面是我们的计算结果:
由于使用 UIDynamics 制作星球大战动画是低效的,我决定使用 UIView 将动画重写一遍:
UIView.animateWithDuration(duration, animations: {
for view in snapshots {
view.frame = view.frame.offsetBy(dx: randomFloatBetween(-200 , and: 200),
dy: randomFloatBetween(fromView.frame.height, and: fromView.frame.height * 1.3)
)
}
})
在这种情况下,只有当我们将动画切割成碎片 CPU 才受到影响,这样会在动画开始时有个小延迟。但是,由于在 GPU 负荷,FPS(帧每秒)不能同时显示超过 1500 个视图。FPS 会被降到 26,但不能使动画达到我想要的 60 FPS 流畅度。
由于我们仍不能得到满意的结果,我们决定使用 OpenGL 制作星球大战动画。
多数 iOS 教程都是用 Objective-C 设计的,所以我决定用它来开始,如果成功实现,我会使用 Swift 再重写一遍动画(我做到了)。
结果是惊人的,我可以在 iPhone 6 上以 60 帧每秒播放视图碎片动画,而且没有任何中断或延迟。
我想:如果我把动画切碎成 40,000 块而不是 4,000 块呢?OpenGL 能应对这种负荷吗?通过实验该数字,我得到了 4 FPS。为了跟踪代码表现不佳的部分,我用 time profiler 来运行动画。这能让我看到负载并显示花费最长时间实现的方法。
在 time profiler 下,动画运行在 23 FPS,但这很容易解释。Time profiler 执行的是发布配置,这意味着它可以优化正在编译的代码。
我重写了关键代码(我将 BlowSprite 的类改成结构体并替换了映射的周期)后,我可以在发布版本配置上达到 60 FPS。我让动画快了两倍有多!(在没有编译优化的调试配置上, 我得到 11 FPS)。
扩展阅读:如何开发 Android 上的 Instagram 视频
在实现了 3 种类型的动画后,我们可以从下面的统计图看到 UIDynamics、UIKit (Core Animation) 和 OpenGL 的实验结果。只有 OpenGL 动画可以应付负载需求,所以选择他来实现我们的组件。
我们对 CPU 的实验结果如下所示。
在帧每秒的表现上:
单实现 UIViewControllerTransitioningDelegate 类,可以返回我们的动画构成方法 animationControllerForDismissedController,然后赋值给 viewController 你想排除的 transitioningDelegate。
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let destination = segue.destinationViewController
destination.transitioningDelegate = self
}
func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return StarWarsGLAnimator()
}
在星球大战动画中有两件事情你可以自定义:持续时间和碎片尺寸。让我们来看看你可以怎么做。
持续时间的部分代码:
func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
let animator = StarWarsGLAnimator()
animator.duration = 2
return animator
}
碎片尺寸,改变各部分点的尺寸:
func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
let animator = StarWarsGLAnimator()
animator.spriteWidth = 8
return animator
}
你可以在这些地方查阅我们的星球大战组件:
愿原力与你同在!