Kinieta 0.5.1

Kinieta 0.5.1

测试测试
语言语言 SwiftSwift
许可证 MIT
发布最后发布2017年11月
SwiftSwift 版本4.0
SPM支持 SPM

Michael Michailidis维护。



Kinieta 0.5.1

  • 作者
  • Michael Michailidis

Kinieta

一个带有直观 API 和可读代码的 iOS 动画引擎!(用 Swift 4.0 编写。)

为什么还要有另一个?

我决定从头开始构建一个动画引擎,理由是:没有其他工具能按我想要的方式完成任务!虽然有些库非常出色,但我的需求非常严格,因为我需要的

  • 是一个用 Swift 4.0 编写的库
  • 采用 时间轴 方法,动画可以在不同的起始和结束点并行运行
  • 能够通过单个完整块来 组合 来自不同视图的动画
  • 有一个简单的 API,我可以将一些变量扔进去,其他的事情就由库来解决
  • 采用 约定优先于配置 的方法,在不传递的情况下,将假定许多变量
  • 使用基于自定义贝塞尔曲线的无限 缓动函数 进行高效插值
  • 使用先进的 HCL 色彩空间提供 真正的颜色插值 而不是简单的 RGB
  • 代码非常易于阅读,社区的新开发者可以迅速加入其中!

安装

Cocoa Pods

pod 'Kinieta', '~> 0.5'

手动

目前,只需复制Kinieta(虚拟)文件夹中的文件即可。

如何使用

square.move(to: ["x": 374], during: 1.0).easeInOut(.Back).wait(for: 1.0).complete {
    square.move(to: ["x": 74])
}

Basic Move with Ease

基本用法

代码中包含的一个扩展在 UIView 上,将提供动画的入口点。接口对象是 Kinieta,每个视图都有一个。

// This will snap the view to the given coordinates
aView.move(to: ["x": 250, "y": 500])

// This will animate the same view to the coordinates in 0.5 seconds
aView.move(to: ["x": 250, "y": 500], during: 0.5)

// This will delay the start of the animation by 0.5 seconds
aView.move(to: ["x": 250, "y": 500], during: 0.5).delay(for: 0.5)

// And this will ease the whole thing
aView.move(to: ["x": 250, "y": 500], during: 0.5).delay(for: 0.5).easeInOut()

// While this will ease it with a bounce-back
aView.move(to: ["x": 250, "y": 500], during: 0.5).delay(for: 0.5).easeInOut(.Back)

// And call the complete block when the animation is finished
aView.move(to: ["x": 250, "y": 500], during: 0.5).delay(for: 0.5).easeInOut(.Back).complete { print("") }

可以动画的UIView属性及其密钥如下所示

密钥 值类型 计量 属性动画
"x" 任意数字 屏幕点 frame.origin.x
"y" 任意数字 屏幕点 frame.origin.y
"w""width" 任意数字 屏幕点 frame.size.width
"h""height" 任意数字 屏幕点 frame.size.height
"a""alpha" 任意数字 0到1不透明度 alpha
"r""rotation" 任意数字 transform
"frame" CGRect 复合 frame
"bg""background" UIColor 颜色 backgroundColor
"brc""borderColor" UIColor 颜色 layer.borderColor
"brw""borderWidth" UIColor 屏幕点 layer.borderWidth
"crd""cornerRadius" UIColor 切边半径 layer.cornerRadius

注意:当同一列表中传入两个同义词键(如 "bg" 和 "background")时,较详细的键(即 "background")将获胜,而另一个将被默默忽略。

缓动动画

每个移动都可以通过调用3个缓动函数之一来平滑,并将

// When no curve is passed `.Quad` is used
aView.move(to: ["x": 250, "y": 500], during: 0.5).easeIn()

// Ease at start, end and both ends respectively
aView.move(to: ["x": 250, "y": 500], during: 0.5).easeIn(.Cubic)
aView.move(to: ["x": 250, "y": 500], during: 0.5).easeOut(.Cubic)
aView.move(to: ["x": 250, "y": 500], during: 0.5).easeInOut(.Cubic)

可以传递默认参数以提供要使用的缓动函数,默认的为Quad。所有缓动动画都是基于贝塞尔曲线,大多数已在 Easing.Types 枚举中默认提供。

enum Types {
    case Sine
    case Quad
    case Cubic
    case Quart
    case Quint
    case Expo
    case Back 
    case Custom(Bezier)
}

最后一种类型 .Custom 将捕获自定义贝塞尔曲线并将其用作缓动函数。三次方程(或立方)贝塞尔曲线由4个点组成,称为控制点。前两个和最后两个是约定(0.0, 0.0)和(1.0, 1.0),而另外两个定义曲线的曲率。当然,您不必手动计算这些数字,因为有一些有用的工具可以帮助您完成这项工作,cubic-bezier 就是其中之一。

例如,对于开始非常快然后突然减慢的动画,我使用了从网站上复制并插入Bezier实例的 这个曲线

let myBezier = Bezier(0.16, 0.73, 0.89, 0.24)
aView.move(to: ["x": 250, "y": 500], during: 1.0).easeInOut(.Custom(myBezier))

Move with Custom Ease

传递的所有曲线都已被 预先烘焙到表格中,以提供快速解析!

序列化

您可以将几个动画非常容易地组合在一起

let start = ["x": aView.x, "y": aView.y]
aView.move(to: ["x": 250, "y": 500], during: 0.5).easeInOut(.Cubic)
     .move(to: ["x": 300, "y": 200], during: 0.5).easeInOut(.Cubic)
     .move(to: start, during: 0.5).easeInOut(.Cubic)

可以保存包含动画的字典并将其稍后通过,如上述示例所示。您还可以通过调用具有 wait(for time: TimeInterval) 函数添加动画之间的暂停。

aView.move(to: ["x": 250, "y": 500], during: 0.5).easeInOut(.Cubic)
     .wait(for: 0.5)
     .move(to: ["x": 300, "y": 200], during: 0.5).easeInOut(.Cubic)

最后,您可以使用 again(times: Int = 1) 函数重复动画序列。

aView.move(to: ["x": 250, "y": 500], during: 0.5).easeInOut(.Cubic)
     .move(to: ["x": 300, "y": 200], during: 0.5).easeInOut(.Cubic)
     .again()

并行化

您可以将各种动画同时运行以实现更复杂的效果。例如,我们可以在移动的末尾添加一个短暂的淡入,并在所有操作完成后调用单个回调

aView.move(to: ["x": 200, "y": 500], during: 1.0).easeInOut(.Cubic)
     .move(to: ["a": 0], during: 0.2).delay(for: 0.8).easeOut()
     .parallel()
     .complete { print("Finished All") }

Move with Custom Ease

组合组的潜在陷阱

.parallel() 干的事情是创建一个包含所有 先于调用 的动作的内部组。这可能会在需要顺序运行两个或多个并发组时引起问题。例如

aView.move(to: ["x": 300], during: 1.0).easeInOut() // this needs to run first,
    .move(to: ["x": 200], during: 1.0).easeInOut() // then this...
    .move(to: ["a": 0], during: 0.2).easeOut()     // ...parallel with this!
    .parallel()

上面的代码将取 所有三个移动 并并行运行,实际上忽略了第一个。但是,我们想要的却是第一个移动单独运行,接着是其他两个在并行执行。为此,我们以如下方式调用 then 属性

aView.move(to: ["x": 300], during: 1.0).easeInOut() 
    .then        
    .move(to: ["x": 200], during: 1.0).easeInOut() 
    .move(to: ["a": 0], during: 0.2).easeOut()     
    .parallel()

分组

您可以将多个不同视图的动画组合在一起,并在它们全部完成后获取一个共同的事件处理程序。

Engine.shared.group([
    aView.move(to: ["x": 374], during: 1.0).easeInOut(.Cubic)
         .move(to: ["a": 0], during: 0.2).delay(for: 0.8).easeOut().parallel(),
    otherView.move(to: ["x": 100, "r": 30], during: 1.0).easeInOut(.Cubic)
]) { print("Both Finished") }

请记住,调用Kinieta API的返回值是一个对象,因此也可以这样做

let move1 = aView.move(to: ["x": 374], during: 1.0).easeInOut(.Cubic)
let move2 = otherView.move(to: ["x": 100, "r": 30], during: 1.0).easeInOut(.Cubic)
Engine.shared.group([move1, move2]) { print("Both Finished") }