我创建了一个实用程序,用于使用以下函数将进度映射到线性函数:
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
我确定这是一个相当基本的数学公式,所以任何指针都会非常感激!
答案 0 :(得分:1)
将您的进度视为单个参数数学函数。在您当前的情况下,它是线性函数,看起来像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
,因此我不确定这是否有效,可能存在某些类型不匹配。但我认为逻辑应该没问题。