如何将值之间的进度映射到S曲线?

时间:2017-12-28 08:53:11

标签: ios swift math

我创建了一个实用程序,用于使用以下函数将进度映射到线性函数:

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

我确定这是一个相当基本的数学公式,所以任何指针都会非常感激!

1 个答案:

答案 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,因此我不确定这是否有效,可能存在某些类型不匹配。但我认为逻辑应该没问题。