VB.NET Windows Forms 中的图像过渡
使用 GDI+ 在 VB.NET 中实现图像过渡和视觉效果。

引言
本文演示了如何在 VB.NET 中实现简单的图像过渡。
过渡是幻灯片放映、商务演示、视频、屏幕保护程序中最常用的功能之一,通过过渡,开发人员可以使用简单且极少的代码来构建简单易用的网页/Windows Forms/视频视觉效果。
背景
您可能会发现很多文章演示如何使用 Silverlight 或 Dynamic HTML 实现图像过渡,但很少有文章讨论 C# 或 VB.NET Windows Forms 中的图像过渡……显然,图像过渡是一个很好的商机。
在本文中,我将尝试解释一些最受欢迎的图像过渡方法以及它们的实现方式。
过渡和视觉效果
下表列出了最受欢迎的图像视觉效果/过渡。您可以沿用相同的过渡名称,或根据您的方便重命名这些过渡。
- 卷帘式 (Barn):运动类似于门打开或关闭的效果
- 百叶窗式 (Blinds):运动看起来像打开或关闭百叶窗
- 棋盘式 (CheckBoard):方形排列,像棋盘或国际象棋棋盘
- 虹膜式 (Iris):运动类似于相机光圈的打开
- 淡入淡出式 (Fade):淡入淡出一个或两个图像
- 滑动式 (Slide):将部分或整个图像滑动到位
- 螺旋式 (Spiral):螺旋运动
过渡计时
System.Threading.Timer 类实现了一个简单轻量级的定时器,它使用回调函数。是的,它只有少数功能,但也有一些很好的优点,例如指定回调的方式、指定第一次运行回调前等待的时间,以及指定调用间隔时间。这些对于在过渡期间掌握 GDI+ 的速度和计算过渡的步骤非常重要。
此外,有些人可能更喜欢使用 System.Timers.Timer 类而不是 System.Threading.Timer 类,并将 Timer 类的 Elapsed 事件挂接起来以加速 GDI+。
以下简单代码演示了如何使用 System.Timers 类在窗体上绘制内容。
- 声明定时器
Private m_timer As New System.Timers.Timer()
Elapsed 事件并启用定时器(StartTimer 例程)Private Sub StartTimer(ByVal m_Interval As Integer)
    AddHandler m_timer.Elapsed, AddressOf TimerTick
    m_timer.Interval = m_Interval
    m_timer.Enabled = True
End Sub
Elapsed 事件触发时要执行的操作。Private Sub TimerTick(ByVal source As Object, _
                      ByVal e As System.Timers.ElapsedEventArgs)
    Invalidate()
End Sub
Paint 事件中绘制内容。Private Sub Form2_Paint(ByVal sender As System.Object, _
                        ByVal e As PaintEventArgs) Handles MyBase.Paint
    Dim path As New Drawing2D.GraphicsPath()
    Dim stringText As String = Date.Now
    Dim family As New FontFamily("Verdana")
    Dim myfontStyle As Integer = CInt(FontStyle.Bold)
    Dim emSize As Integer = 20
    Dim origin As New Point(0, 0)
    Dim format As StringFormat = StringFormat.GenericDefault
    path.AddString(stringText, family, myfontStyle, emSize, origin, format)
    Using lgb As New Drawing2D.LinearGradientBrush(path.GetBounds(), _
                                                   Color.Cyan, Color.Blue, 90, 0)
        e.Graphics.FillPath(lgb, path)
    End Using
End Sub
StartTime 例程传递给 Form Load 事件并运行。Private Sub Form2_Load(ByVal sender As System.Object, _
                        ByVal e As System.EventArgs) Handles MyBase.Load
    StartTimer(1000)
End Sub
以下代码演示了如何使用 System.Threading.Timer 在窗体上绘制内容,遵循上述相同步骤。
Public Class Form1
    Private ThreadTimer As System.Threading.Timer
    Private Sub StartTimer(ByVal Interval As Integer)
        Dim CallBackTimer As New Threading.TimerCallback(AddressOf TimerTick)
        ThreadTimer = New System.Threading.Timer(CallBackTimer, Nothing, 0, Interval)
    End Sub
    ' ........ rest of the code as explained before
End Class
卷帘式视觉效果 (Barn Visual Effect)
以下代码示例演示了如何应用简单的垂直卷帘式过渡。
- 声明变量,如定时器、过渡步数和开始时间。
Public Class frmTransition
    Private m_timer As New System.Timers.Timer()
    Private m_Steps As Single
    Private m_startTime As DateTime
    ' .......... rest of code
End Class
Elapsed 事件,初始化变量,并启用定时器(StartTimer 例程)。Private Sub StartTimer(ByVal m_Interval As Integer)
    AddHandler m_timer.Elapsed, AddressOf TimerTick
    m_timer.Interval = m_Interval
    m_timer.Enabled = True
    m_Steps = 0
    m_startTime = DateTime.Now
    Invalidate()
End Sub
' .......... rest of code
Elapsed 事件触发时要执行的操作。定义和计算已用时间,定义动画时间(值越小,速度越快),计算过渡步数并使控件失效以进行重绘。Private Sub TimerTick(ByVal source As Object, _
                      ByVal e As System.Timers.ElapsedEventArgs)
    ' define & calculate ellapsed time
    Dim timeEllapsed As TimeSpan = DateTime.Now - m_startTime
    ' define animation time
    'the lowest the value the highest the speed
    Dim AnimationTime As Integer = 2
    ' calcualte transition steps
    m_Steps = CSng((100.0F / AnimationTime * timeEllapsed.TotalSeconds))
    ' check transition steps value & fix it
    If m_Steps > 100 Then
        m_Steps = 100
    End If
    ' invalidate the paint
    Invalidate()
End Sub
' .......... rest of code
要绘制过渡效果,请按照以下步骤操作:
- 定义一个过渡位图。
- 定义一个过渡矩形。
- 通过定义目标矩形的上部来绘制卷帘式的上部效果。定义图像的上部源矩形,然后绘制图像的上部。
- 重复步骤 3,通过定义目标矩形的下部来绘制卷帘式的下部效果。定义图像的下部源矩形,然后绘制图像的下部。
- 将 StartTime例程传递给 Form Load 事件并运行。
- 将时间间隔设置为 60 毫秒。
- 为了获得更好的性能,我认为间隔值不应超过 100 毫秒。值越小,性能越好。
Private Sub frmTransition_Paint(ByVal sender As Object, _
                                ByVal e As PaintEventArgs) _
                                Handles MyBase.Paint
    ' define transition bitmap
    Dim bmp As New Bitmap(My.Resources.baby_5)
    ' define transition rectangle
    Dim m_rect As New Rectangle(0, 0, 100, 100)
    ' draw the top part barns effect 
    ' draw the top part of the image based on transition steps
    ' define the top part of destination rectangle
    Dim destTopRect As New Rectangle(0, 0, m_rect.Width, _
                    CInt(m_rect.Height * m_Steps / 200))
    ' define the image top part source rectangle
    Dim srcTopRect As New Rectangle(0, 0, bmp.Width, CInt(bmp.Height / 2))
    ' draw the top part of the image
    e.Graphics.DrawImage(bmp, destTopRect, srcTopRect, GraphicsUnit.Pixel)
    ' let us repeat the steps above to draw the bottom part of the barns effect
    ' define the destination rectangle bottom part
    Dim destBottomRect As New Rectangle(0, _
        CInt(m_rect.Height - CInt(m_rect.Height * m_Steps / 200)), _
        m_rect.Width, _
        CInt((m_rect.Height * m_Steps / 200)))
    ' define the image's bottom part source rectangle
    Dim srcBottomRect As New Rectangle(0, CInt(bmp.Height / 2), _
                                       bmp.Width, CInt(bmp.Height / 2))
    ' draw the bottom part of the image
    e.Graphics.DrawImage(bmp, destBottomRect, srcBottomRect, GraphicsUnit.Pixel)
End Sub
Private Sub frmTransition_Load(ByVal sender As Object, _
                               ByVal e As EventArgs) Handles MyBase.Load
    StartTimer(60)
End Sub
以下代码示例演示了如何应用水平卷帘式过渡。
Private Sub frmTransition_Paint(ByVal sender As Object, _
                                ByVal e As PaintEventArgs) Handles MyBase.Paint
    Dim bmp As New Bitmap(My.Resources.baby_5)
    Dim m_rect As New Rectangle(0, 0, 100, 100)
    ' draw the left part barns effect 
    Dim destTopRect As New Rectangle(0, 0, _
        CInt(m_rect.Width * m_Steps / 200), m_rect.Height)
    Dim srcTopRect As New Rectangle(0, 0, CInt(bmp.Width / 2), bmp.Height)
    e.Graphics.DrawImage(bmp, destTopRect, srcTopRect, GraphicsUnit.Pixel)
    ' draw the right part barns effect
    Dim destBottomRect As New Rectangle(_
        CInt(m_rect.Width - CInt(m_rect.Width * m_Steps / 200)), _
        0, _
        CInt((m_rect.Width * m_Steps / 200)), _
        m_rect.Height)
    Dim srcBottomRect As New Rectangle(CInt(bmp.Width / 2), 0, _
                                       CInt(bmp.Width / 2), bmp.Height)
    e.Graphics.DrawImage(bmp, destBottomRect, srcBottomRect, GraphicsUnit.Pixel)
End Sub
如果我们混合以上两种卷帘式视觉效果(水平和垂直效果)会发生什么……!!!??? 我们会得到不同的效果……您可能称之为“卷帘式爆炸” (Barns Boom)!
以下代码示例演示了如何应用卷帘式爆炸过渡。
Private Sub frmTransition_Paint(ByVal sender As Object, _
                                ByVal e As PaintEventArgs) Handles MyBase.Paint
    Dim bmp As New Bitmap(My.Resources.baby_5)
    Dim m_rect As New Rectangle(0, 0, 100, 100)
    ' ------------------------------------------------------->
    ' draw the left part of the barn
    Dim destLeftRect As New Rectangle(0, 0, _
                        CInt(m_rect.Width * m_Steps / 200), m_rect.Height)
    Dim srcLeftRect As New Rectangle(0, 0, CInt(bmp.Width / 2), bmp.Height)
    e.Graphics.DrawImage(bmp, destLeftRect, srcLeftRect, GraphicsUnit.Pixel)
    ' ------------------------------------------------------->
    ' draw the right part of the barn
    Dim destRightRect As New Rectangle(_
                CInt(m_rect.Width - CInt(m_rect.Width * m_Steps / 200)), _
                0, _
                CInt((m_rect.Width * m_Steps / 200)), _
                m_rect.Height)
    Dim srcRightRect As New Rectangle(CInt(bmp.Width / 2), 0, _
                                       CInt(bmp.Width / 2), bmp.Height)
    e.Graphics.DrawImage(bmp, destRightRect, srcRightRect, GraphicsUnit.Pixel)
    ' ------------------------------------------------------->
    ' draw the top part of the barn
    Dim destTopRect As New Rectangle(0, 0, m_rect.Width, _
                    CInt(m_rect.Height * m_Steps / 200))
    Dim srcTopRect As New Rectangle(0, 0, bmp.Width, CInt(bmp.Height / 2))
    e.Graphics.DrawImage(bmp, destTopRect, srcTopRect, GraphicsUnit.Pixel)
    ' ------------------------------------------------------->
    ' draw the bottom part of the barn
    Dim destBottomRect As New Rectangle(0, _
                        CInt(m_rect.Height - CInt(m_rect.Height * m_Steps / 200)), _
                        m_rect.Width, _
                        CInt((m_rect.Height * m_Steps / 200)))
    Dim srcBottomRect As New Rectangle(0, CInt(bmp.Height / 2), _
                                       bmp.Width, CInt(bmp.Height / 2))
    e.Graphics.DrawImage(bmp, destBottomRect, srcBottomRect, GraphicsUnit.Pixel)
End Sub
旋转、放大和旋转视觉效果 (Rotate, Maximize, and Spin Visual Effect)
Drawin2D.Matrix 类在旋转、放大和旋转图像等过渡效果中起着重要作用,以下步骤重点介绍了最大化视觉效果中使用的主要要点。
- 定义过渡位图
- 定义过渡矩形
- 定义和计算矩阵中使用的过渡缩放因子。
- 定义矩阵类并将缩放因子传递给它,效果将从矩形的中点开始。
- 将矩阵传递给 graphics 对象并绘制效果。
以下代码示例演示了如何应用放大过渡。
Private Sub frmTransition_Paint(ByVal sender As Object, _
                                ByVal e As PaintEventArgs) Handles MyBase.Paint
    ' draw maximize effect
    Dim bmp As New Bitmap(My.Resources.baby_5)
    Dim m_rect As New Rectangle(0, 0, 150, 150)
    ' define and calculate transition scale factor used in the matrix
    Dim sf As Single = m_Steps / 100
    If sf = 0 Then
        sf = 0.01F
    End If
    ' define the matrix class & pass the scale factor to it
    ' effect will strat from the rectangle mid-point
    Dim mx As New Drawing2D.Matrix(sf, 0, 0, sf, m_rect.Width / 2, m_rect.Height / 2)
    ' pass the matrix to graphics
    e.Graphics.Transform = mx
    Dim destRect As New Rectangle(-m_rect.Width / 2, -m_rect.Height / 2, _
                                  m_rect.Width, m_rect.Height)
    Dim srcRect As New Rectangle(0, 0, bmp.Width, bmp.Height)
    e.Graphics.DrawImage(bmp, destRect, srcRect, GraphicsUnit.Pixel)
End Sub
以下步骤重点介绍了旋转和旋转视觉效果使用的主要要点。
- 定义过渡位图
- 定义过渡矩形
- 定义和计算矩阵中使用的过渡缩放因子。
- 定义矩阵类并将缩放因子传递给它,效果将从矩形的中点开始。
- 定义旋转因子
- 将旋转因子传递给矩阵以旋转矩阵,并将 Drawing2D.MatrixOrder值设置为Append(对于旋转效果,只需将Drawing2D.MatrixOrder值设置为Prepend)。
- 将矩阵传递给 graphics 对象并绘制效果。
Private Sub frmTransition_Paint(ByVal sender As Object, _
                                ByVal e As PaintEventArgs) _
                                Handles MyBase.Paint
    ' draw maximize effect
    Dim bmp As New Bitmap(My.Resources.baby_5)
    Dim m_rect As New Rectangle(0, 0, 150, 150)
    ' define and calculate transition scale factor used in the matrix
    Dim sf As Single = m_Steps / 100
    If sf = 0 Then
        sf = 0.01F
    End If
    ' define the matrix class & pass the scale factor to it
    ' effect will strat from the rectangle mid-point
    Dim mx As New Drawing2D.Matrix(sf, 0, 0, sf, _
                  m_rect.Width / 2, m_rect.Height / 2)
    ' pass the matrix to graphics
    ' define rotation factor
    Dim rf As Single = m_Steps * 360 / 100
    ' pass the rotation factor to the matrix 
    ' to rotate the matrix and set the matrixorder
    ' for spin set MatrixOrder value to Prepend
    mx.Rotate(rf, Drawing2D.MatrixOrder.Append)
    e.Graphics.Transform = mx
    Dim destRect As New Rectangle(-m_rect.Width / 2, -m_rect.Height / 2, _
                                  m_rect.Width, m_rect.Height)
    Dim srcRect As New Rectangle(0, 0, bmp.Width, bmp.Height)
    e.Graphics.DrawImage(bmp, destRect, srcRect, GraphicsUnit.Pixel)
End Sub
有关其他效果(大约有 28 种效果等着您去尝试)的更多详细信息,您可以下载附加到文章的文件。
动画控件 (The Animation Control)
- 动画控件文件
- AnimationTypes.vb:大约 28 种视觉效果/过渡的公共枚举。
- AnimationControl.vb:扩展 Control类。
- 控件属性
- AnimationSpeed:用于设置动画速度;值越大,速度越慢。
- AnimationType:用于设置- Animationtypes枚举。
- AnimatedImage:用于设置视觉效果所需的图像。
- AnimatedFadeImage:用于设置将与动画图像一起使用的淡入淡出图像。
- Opacity,- Transparent,- TransparenColor:用于设置控件的透明度(有关更多详细信息,您可以参考我关于使用 GDI+ 的 VB.NET 透明控件的上一篇文章,可在 此处 找到)。
使用控件
构建 AnimationControl 并在您的窗体中使用它。以下简单代码演示了如何在 Windows Forms 中使用 AnimationControl。
Public Class TestForm
    Private bmp As New Bitmap(My.Resources.baby_5)
    Private Sub TestForm_Load(ByVal sender As System.Object, _
            ByVal e As System.EventArgs) Handles MyBase.Load
        AnimationControl1.AnimatedFadeImage = Panel1.BackgroundImage
        AnimationControl1.AnimatedImage = bmp
    End Sub
    Private Sub btnSpiarlBoom_Click(ByVal sender As System.Object, _
            ByVal e As System.EventArgs) Handles btnSpiarlBoom.Click
        AnimationControl1.AnimationType = AnimationTypes.SpiralBoom
        AnimationControl1.Animate(40)
    End Sub
    ' rest of code 
End Class
下载附件文件,进行尝试,您还可以添加自己的视觉效果……这既简单又有趣。尽情享受吧。
历史
- 2009 年 11 月 7 日:首次发布。




