我已经使用以下函数创建了一个用于将进度映射到线性函数的实用程序:
public static func map<T: FloatingPoint>(progress: T, min: T, max: T) -> T {
assert(progress >= 0)
assert(progress <= 1)
return min + ((max - min) * progress)
}
这将返回一组线性值,其中,如果您具有最小值0和最大值100,则为每个进度返回以下值:
0.1 -> 10
0.2 -> 20
0.5 -> 50
0.8 -> 80
0.9 -> 90
我想创建一个类似的函数,将返回值映射到S曲线,您越接近进度的起点和终点,结果就越不受结果的影响。例如,这对于使用CADisplayLink平滑动画非常有用。上述示例的预期结果如下所示:
0.1 -> 01
0.2 -> 10
0.5 -> 50
0.8 -> 90
0.9 -> 99
我相信这是一个相当基本的数学公式,所以任何指针都将非常感激!
将您的进度视为单个参数数学函数。在你目前的情况下,它是一个线性函数,看起来像y = mx + n
,其中y
是返回值,n
是(max - min)
,m
是1,x
是你的进度值。
为了达到你想要的效果,你需要使用sigmoid function的替代版本而不是线性版本。你需要x = 0.5的中间值,并且只对0和1之间的值感兴趣。另外,正如维基百科的文章所暗示的那样,x = -6和6之前和之后的y值分别非常接近,所以你只需要将范围[0, 1]
的x值缩放到[-6, 6]
。以下内容应该给你一个想法
public static func map<T: FloatingPoint>(progress: T, min: T, max: T) -> T {
assert(progress >= 0)
assert(progress <= 1)
return min + ((max - min) * sigmoid(progress))
}
private static func sigmoid(_ input: FloatingPoint) -> FloatingPoint {
let x = (input - 0.5) * 12.0 // scale the input value to be between -6 and 6
let ex = pow(M_E, x) // M_E is the Euler number and is a Double constant
return ex / (ex + 1) // return the result of the sigmoid function
}
我之前从未使用过FloatingPoint
,所以我不确定这是否可行,可能存在一些类型不匹配。但我认为逻辑应该没问题。