是否可以使用 VB.NET 语言中的标准 WinForms 控件制作动画,如 Bunifu 生成的过渡?
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
PanelForm.Visible = False
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
BunifuTransition1.Show(PanelForm)
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
BunifuTransition1.Hide(PanelForm)
End Sub
End Class
要向窗口(控件是窗口)添加一些动画效果,可以使用 Win32 AnimateWindow 函数。
此函数允许生成一些预定义的效果:滚动、滑动、折叠或展开以及 alpha 混合淡入淡出(但只能应用于顶级窗口)。
它缺少任何缩放窗口内容的效果,如问题所示。
我在扩展方法
Animate()
中添加了这个缺失的效果(以及已经支持的效果),该方法应用于从 Control 派生的所有类。
例如,要为面板(及其承载的控件)设置动画,请编写:
SomePanel.Animate(AnimationMode.OpenScale, 500)
添加持续 500 毫秒的开场动画。或者,例如,
SomePanel.Animate(AnimationMode.CloseScale, 500)
添加具有相同持续时间的结束动画。
尝试您可以选择的其他效果,设置不同的
AnimationMode
。
这就是它的工作原理:
GIF动画有点迟缓
将此模块添加到任何 WinForms 项目中。
Imports System.Drawing
Imports System.Drawing.Imaging
Imports System.Runtime.CompilerServices
Imports System.Runtime.InteropServices
Module ExtensionMethods
''' <summary>
''' Add opening or closing effects to a Control
''' </summary>
''' <param name="window">The Control to animate</param>
''' <param name="mode">The animation effect</param>
''' <param name="animationSpeed">The overall time, in milliseconds, of the animation</param>
<Extension()>
Public Sub Animate(window As Control, mode As AnimationMode, animationSpeed As Integer)
If mode.HasFlag(AnimationMode.OpenScale) OrElse mode.HasFlag(AnimationMode.CloseScale) Then
AnimateScale(window, mode, animationSpeed)
Else
window.Show()
If Not mode.HasFlag(AnimationMode.Hide) Then window.Hide()
AnimateWindow(window.Handle, CType(animationSpeed, UInteger), mode)
If Not mode.HasFlag(AnimationMode.Hide) Then window.Show()
End If
End Sub
Private animateBitmap As Bitmap = Nothing
Private animateBitmapBoounds As Rectangle = Rectangle.Empty
Private Sub AnimateScale(window As Control, mode As AnimationMode, Optional animationSpeed As Integer = 200)
Dim steps = 10
If animationSpeed < 200 Then animationSpeed = 200
Dim timerSteps = animationSpeed \ steps
Dim animationFrames As List(Of Bitmap) = Nothing
Using bmpSource = New Bitmap(window.Width, window.Height, PixelFormat.Format32bppArgb)
window.Show()
window.DrawToBitmap(bmpSource, New Rectangle(Point.Empty, window.Size))
window.Hide()
animationFrames = GetAnimationFrames(steps, bmpSource, mode.HasFlag(AnimationMode.CloseScale))
End Using
Dim windowParent = If(window.Parent Is Nothing, window, window.Parent)
animateBitmapBoounds = window.Bounds
Dim timer = New System.Windows.Forms.Timer() With {.Interval = timerSteps}
AddHandler timer.Tick,
Sub()
If steps < 0 Then
timer.Stop()
RemoveHandler windowParent.Paint, AddressOf AnimateWindowParent
animationFrames.ForEach(Sub(img) img.Dispose())
animationFrames = Nothing
animateBitmap = Nothing
timer.Dispose()
Else
animateBitmap = animationFrames(steps)
windowParent.Invalidate(animateBitmapBoounds)
If steps = 0 AndAlso mode.HasFlag(AnimationMode.OpenScale) Then window.Show()
End If
steps -= 1
End Sub
AddHandler windowParent.Paint, AddressOf AnimateWindowParent
window.Hide()
timer.Start()
End Sub
Private Function GetAnimationFrames(framesCount As Integer, sourceImage As Image, collapse As Boolean) As List(Of Bitmap)
Dim animationFrames As New List(Of Bitmap)(framesCount)
Dim scaleWidth = sourceImage.Width \ framesCount
Dim scaleHeight = sourceImage.Height \ framesCount
For frame As Integer = 1 To framesCount
Dim imageSize = New Size(sourceImage.Width - frame * scaleWidth, sourceImage.Height - frame * scaleHeight)
Dim imagePos = New Point((sourceImage.Width - imageSize.Width) \ 2, (sourceImage.Height - imageSize.Height) \ 2)
Dim imageFrame = New Bitmap(sourceImage.Width, sourceImage.Height, PixelFormat.Format32bppArgb)
Using g = Graphics.FromImage(imageFrame)
g.DrawImage(sourceImage, New RectangleF(imagePos, imageSize),
sourceImage.GetBounds(GraphicsUnit.Pixel), GraphicsUnit.Pixel)
If collapse Then
animationFrames.Insert(0, imageFrame)
If frame = framesCount Then animationFrames.Insert(0, New Bitmap(2, 2))
Else
animationFrames.Add(imageFrame)
If frame = framesCount Then animationFrames.Add(New Bitmap(2, 2))
End If
End Using
Next
Return animationFrames
End Function
Private Sub AnimateWindowParent(sender As Object, e As PaintEventArgs)
If animateBitmap Is Nothing Then Return
e.Graphics.DrawImage(animateBitmap, animateBitmapBoounds)
End Sub
<Flags()>
Public Enum AnimationMode As UInteger
CloseCentered = AW_CENTER Or AW_HIDE
CloseDownwards = AW_VER_POSITIVE Or AW_HOR_POSITIVE Or AW_HIDE
CloseUpwards = AW_VER_NEGATIVE Or AW_HOR_NEGATIVE Or AW_HIDE
OpenCentered = AW_CENTER
OpenDownwards = AW_HOR_POSITIVE Or AW_VER_POSITIVE
OpenUpwards = AW_HOR_NEGATIVE Or AW_VER_NEGATIVE
OpenScale = &H20
CloseScale = &H40 Or Hide
SlideToRight = AW_SLIDE Or AW_HOR_POSITIVE
SlideToLeft = AW_SLIDE Or AW_HOR_NEGATIVE
SlideDownwards = AW_SLIDE Or AW_VER_POSITIVE
SlideUpwards = AW_SLIDE Or AW_VER_NEGATIVE
Hide = AW_HIDE
End Enum
Private Const AW_HOR_POSITIVE As UInteger = &H1
Private Const AW_HOR_NEGATIVE As UInteger = &H2
Private Const AW_VER_POSITIVE As UInteger = &H4
Private Const AW_VER_NEGATIVE As UInteger = &H8
Private Const AW_CENTER As UInteger = &H10
Private Const AW_HIDE As UInteger = &H10000
Private Const AW_ACTIVATE As UInteger = &H20000
Private Const AW_SLIDE As UInteger = &H40000
Private Const AW_BLEND As UInteger = &H80000
<DllImport("user32.dll", SetLastError:=True)>
Private Function AnimateWindow(hwnd As IntPtr, time As UInteger, flags As AnimationMode) As Boolean
End Function
End Module