细说如何完美实现macOS中的神奇效果
细说如何完美实现macOS中的神奇效果
前言
神奇效果运行时,窗口的底部先逐渐收窄,收窄到一定程度后,窗口开始向下吸收。
底部收窄
在这个过程中,左右两侧会出现曲线,随着时间的推移,曲线的形变程度越来越大,直到最终停止形变。窗口始终被限制在两侧曲线之间。
向下吸收
在这个过程中,窗口向下进行运动,逐渐消失。和底部收窄一样,窗口也是始终被限制在两侧曲线之间。
剖析
底部收窄的过程,是两侧曲线的演变过程。两侧的曲线可以用正弦曲线或余弦曲线表示。这里只拿正弦曲线进行说明。
周期函数回顾
先回顾一下在中学时期所学的周期函数,对于A * sin(B * (x + C)) + D
来说,可以得出以下信息:
- 振幅为
A
- 周期为
2π / B
- 相移为
−C
- 垂直移位为
D
- 值域为
[D - A, D + A]
根据上面的内容,对于值域为[MIN, MAX]
的周期函数,我们很容易得到以下公式:
D - A = MIN
D + A = MAX
sin(x)
的曲线:
推导所需周期函数
为了方便理解,先把神奇效果放到坐标系中,这里使用纹理坐标。
很明显,p1
和p2
这两个点的x
坐标值就是曲线在x
轴上的最小值与最大值。为了让曲线不断演变,就需要将p1
点的位置固定在(0, 1)
,而让p2
点的x
坐标一直向右移动,其y
坐标保持为0
,也就是说,我们需要通过这两个点的x
坐标值(对应周期函数值域的最小值与最大值),得到对应的曲线。
可以看出:
x
从最小值变成最大值的过程中,y
只改变了1
y
为0
时,曲线的x
值最大y
为1
时,曲线的x
值最小
因此,我们所需要的是一个周期为2
(最小值变成最大值,需要半个周期)、相移为-0.5
(周期除以4,然后减去对称轴的x,最后取负)、值域最小值为0
的周期函数(此时的A
和D
相同):
A * sin(π * x + π * 0.5) + A
或D * sin(π * x + π * 0.5) + D
比如:
当然,上面得到的函数,其y
值是根据x
值的变化而变化的,而神奇效果中使用的函数,其x
值是根据y
值的变化而变化的(当然,也可以将程序坞
放置到左侧或右侧,这样就是y
值根据x
值的变化而变化)。
修改一下周期函数,将x
修改为y
:
A * sin(π * y + π * 0.5) + A
或D * sin(π * y + π * 0.5) + D
然后,可以得到类似这样的曲线:
当使用不同的值域最大值(也就是D + A
,因为此时的A
和D
相同,最大值也是2 * D
或2 * A
)时,可以得到不同的曲线:
同理,我们可以得到右侧曲线所对应的周期函数:
A * sin(π * y - π * 0.5) + D
此时,D + A = 1
。当使用不同的值域最小值(也就是D - A
)时,也可以得到不同的曲线:
得到左右两侧曲线对应的函数后,就能很方便地实现神奇效果中的底部收缩了。
底部收缩
假如底部收缩的持续时间为curve_animation_duration
,左侧曲线最终的值域最大值为left_max_end
,右侧曲线最终的值域最小值为right_min_end
,那么,根据时间(u_time
)的不断增加,可以得到曲线演变的进度:
1 |
|
根据曲线演变进度,可以得到当前左侧曲线的值域最大值和当前右侧曲线的值域最小值:
1 |
|
进而计算出左右曲线各自的A
、D
:
1 |
|
使用这样的纹理图片:
对其进行采样:
1 |
|
就可以得到这样的效果:
向下吸收
假如向下吸收的持续时间为translation_animation_duration
,那么向下吸收的进度就是:
1 |
|
向下移动:
1 |
|
最终效果:
结束语
神奇效果还可以通过修改顶点来实现,不过,这种方式需要较多的顶点,才能让曲线足够光滑。
而通过修改采样时所使用的纹理坐标实现的神奇效果,并不需要那么多顶点。