阻尼运动算法在界面编程中的应用

最近一直在考虑如何设计系统的启动封面,关键在于封面上众多的按钮无法很好的分类组织起来。后来在魔兽中得到了启示。
        其实最简约的设计最具持久美感,所以放弃了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();


         关于双缓冲的内容就不介绍了。
         如果各位同学还有什么更好的算法欢迎讨论。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值