最近一直在考虑如何设计系统的启动封面,关键在于封面上众多的按钮无法很好的分类组织起来。后来在魔兽中得到了启示。
其实最简约的设计最具持久美感,所以放弃了Panel、Tab容器的设计,而是参考War3的界面,按功能分成大类,当点击每个大类按钮时,对应的子功能按钮会像War3的侧边栏一样弹出,效果还是比较炫的。
具体效果见War3的侧边栏吧,简单描述就是左边的两排按钮会从上面弹下来,就像皮球落地一样。下面讲一下具体实现。
从初中物理知识中我们可以了解到这种运动属于阻尼运动,而皮球落地可以用取绝对值的阻尼运动公式来描述,程序中为了运动美观而没有取绝对值。
阻尼运动又分为欠阻尼、过阻尼及临界阻尼,概念就不多说了,有兴趣自己看,在后面的代码分析中将会看到按钮运动是欠阻尼。
阻尼运动公式:x(t) = A * exp(-1 * β * t) * cos(sprt(ω^2 - β^2 * t) + φ) ,
β是阻尼因子,ω是固有角频率。通过调整这两个参数的值即可获得我们预想的运动形式。
下面列举出代码部分,语言为C#。
int
fixY_1
=
224
, fixY_2
=
280
, fixY_3
=
336
;
double
A_1
=
170
, A_2
=
170
, A_3
=
170
;
double
LocalY_1, LocalY_2, LocalY_3;
double
w_1
=
5
, w_2
=
3
, w_3
=
1
;
double
B_1
=
0.4
, B_2
=
0.3
, B_3
=
0.5
;
double
t_1
=
0
, t_2
=
0
, t_3
=
0
;
double
LocalY_temp
=
0
;
while
(
true
)
{
LocalY_1
=
A_1
*
Math.Exp(
-
1
*
B_1
*
t_1)
*
Math.Cos(Math.Sqrt(w_1
*
t_1));
LocalY_2
=
A_2
*
Math.Exp(
-
1
*
B_2
*
t_2)
*
Math.Cos(Math.Sqrt(w_2
*
t_2));
LocalY_3
=
A_3
*
Math.Exp(
-
1
*
B_3
*
t_3)
*
Math.Cos(Math.Sqrt(w_3
*
t_3));
t_1
=
t_1
+
0.5
;
t_2
=
t_1;
t_3
=
t_1;
bttn_LIV.Location
=
new
Point(
160
, fixY_1
-
Convert.ToInt16(LocalY_1));
bttn_VBR.Location
=
new
Point(
320
, fixY_1
-
Convert.ToInt16(LocalY_1));
bttn_OSA.Location
=
new
Point(
160
, fixY_2
-
Convert.ToInt16(LocalY_2));
bttn_Sens.Location
=
new
Point(
320
, fixY_2
-
Convert.ToInt16(LocalY_2));
bttn_BR.Location
=
new
Point(
160
, fixY_3
-
Convert.ToInt16(LocalY_3));
bttn_Respon.Location
=
new
Point(
320
, fixY_3
-
Convert.ToInt16(LocalY_3));
Application.DoEvents();
if
(LocalY_temp
==
LocalY_1)
break
;
else
LocalY_temp
=
LocalY_1;
}
So easy,呵呵,上述程序中的大循环即完成了预期的效果。fix_Y
1-3定义了三行按钮停止后的Location.Y值,A_1 ~ A-3定义了三行按钮开始运动时的Location.Y值,w_1
~ w_3,B_1 ~
B_3分别是固有角频率和阻尼因子,这六个值是关键值,分别决定了三行按钮的下落运动轨迹。这六个值需要根据情况进行调整,是整个程序比较耗时的部分。
程序的整体思路是每循环一次,时间参数t_1~t_3增加,然后计算按钮的Location.Y值,改变按钮位置,直到两次循环按钮位置不再发生变化位
置。时间参数决定了运动的细腻程度,这个可以参考奈奎斯特定理,但由于CPU的运算周期一定,所以过小的时间步长会导致按钮运动过慢,这样又需要调整w和
β参数来改善运动速度,这三个变量互相影响,需要按实际情况进行调整。
另外在程序运行时更改控件位置需要开启双缓冲,在.NET中这步操作非常简单,在窗体构造函数中加入:
this
.SetStyle(ControlStyles.UserPaint
|
ControlStyles.DoubleBuffer
|
ControlStyles.AllPaintingInWmPaint
|
ControlStyles.ResizeRedraw
|
ControlStyles.SupportsTransparentBackColor,
true
);
this
.UpdateStyles();
关于双缓冲的内容就不介绍了。
如果各位同学还有什么更好的算法欢迎讨论。