|
Clase para generar efectos de transición entre imágenes con GDI+
Fecha: 13/Dic/2004 (12/Dic/04)
|
Programa en Ejecución
Este proyecto muestra como realizar 55 diferentes efectos de transición (movimiento) entre imágenes con GDI+.
Para eso vamos a crear una clase de nombre "EfectosTrans" la cual recibirá en su constructor un control que será
donde se dibujarán los efectos, y una propiedad de tipo "Image" de dicho control donde pondremos una imagen vacía sobre la cual se va a dibujar una imagen mediante un efecto que también hay que pasarle a la clase.
A continuación pongo el código completo de la clase EfectosTrans y del código de ejemplo (también pueden descargar la clase junto al proyecto de ejemplo siguiendo el link que está al final de la página).
Imports System
Imports System.Windows.Forms
Imports System.Drawing
Imports System.Drawing.Drawing2D
Imports System.Drawing.Imaging
Imports System.ComponentModel
Friend Class EfectosTrans
Implements IDisposable
'Evento EffectEnded: se dispara al finalizar un efecto
Public Event EffectEnded As EventHandler
#Region " Enumeraciones "
'Enumeración de Efectos
Public Enum Efectos
Abanico_Derecha = 0
Abanico_Izquierda
Aparecer
Barras_Horizontales
Barras_Verticales
Barrido_Horizontal
Barrido_Vertical
Circulos_Dentro
Circulos_Fuera
Desplegar_Centro
Desplegar_ID
Desplegar_II
Desplegar_SD
Desplegar_SI
Diagonal
DivisiónH_Entrante
DivisiónH_Saliente
DivisiónV_Entrante
DivisiónV_Saliente
Empuja_División_Lados
Empuja_División_Topes
Empujar_Abajo
Empujar_Arriba
Empujar_Derecha
Empujar_Diagonal_ID
Empujar_Diagonal_II
Empujar_Diagonal_SD
Empujar_Diagonal_SI
Empujar_Izquierda
Estirar_Centro
Estirar_ID
Estirar_II
Estirar_SD
Estirar_SI
Girar_Centro
Girar_Espiral_Abajo
Girar_Espiral_Arriba
PersianasH_Abajo
PersianasH_Arriba
PersianasV_Derecha
PersianasV_Izquierda
Reloj
Reloj_AntiHorario
Rodar_DAbajo
Rodar_DArriba
Rodar_IAbajo
Rodar_IArriba
Rueda_2Ejes
Rueda_3Ejes
Rueda_4Ejes
Rueda_8Ejes
Simetrico_Adentro
Simetrico_Afuera
Simetrico_Derecha
Simetrico_Izquierda
End Enum
#End Region
#Region " Miembros Privados "
'Velocidad a la que se dibujará el efecto
Private _Velocidad As Integer = 20
'Contiene el efecto actual
Private _Efecto As Efectos = Efectos.Abanico_Derecha
'Control donde se mostrarán los efectos
Private _Contenedor As Control
'Propiedad de _Contenedor donde se colocará una imagen para realizar los efectos
Private _Propiedad As Reflection.PropertyInfo
'Color que se usará para rellenar el fondo de la imagen
Private _Color As Color = Color.Transparent
'Control Timer que controlará la ejecución de los efectos
Private _Tiempo As Timer
'Cuenta las veces que se debe ejecutar el evento Tick para cada efecto
Private _Contador As Integer = 0
'Contiene una copia de la imagén pasada a uno de los constructores para realizar los efectos
Private _bmpTextura As Image
'Contiene una copia vacia de _bmpTextura donde se dibujará el efecto
Private _bmpDibujar As Image
'Creamos un objeto _Graphics para dibujar en _bmpDibujar
Private _Gr As Graphics
'Creamos un TextureBrush con la textura de la imágen _bmpTextura
Private _Brocha As TextureBrush
'Contienen el Ancho y Alto de _bmpTextura respectivamente
Private _AnchoImagen, _AltoImagen As Single
'Indica si actualmente hay un efecto ejecutandose
Private blnEfectoEjecutandose As Boolean = False
Private disposed As Boolean = False
#Region " Variables especificas de algunos efectos "
'Ángulo de giro
Private _AnguloGiro As Single
'Cantidad en que se aumentan las transformaciones de ejes.
Private _xAumentaPos, _yAumentaPos As Single
'_Aumento progresivo de los dibujos
Private _Aumento As Single
#End Region
#End Region
#Region " Métodos Publicos "
'Constructor
Public Sub New(ByVal RutaImagen As String, ByVal Contenedor As Control, ByVal strPropiedad As String, Optional ByVal intVelocidad As Integer = 20)
Try
_bmpTextura = Image.FromFile(RutaImagen)
Iniciar(Contenedor, intVelocidad, strPropiedad)
Catch ex As Exception When Not IO.Directory.Exists(RutaImagen)
MessageBox.Show("La ruta de la imagen no es válida")
Catch ex As Exception When _bmpTextura Is Nothing
MessageBox.Show("La imagen no puede ser nula.")
End Try
End Sub
'Constructor
Public Sub New(ByVal Imagen As Image, ByVal Contenedor As Control, ByVal strPropiedad As String, Optional ByVal intVelocidad As Integer = 20)
Try
_bmpTextura = Imagen
Iniciar(Contenedor, intVelocidad, strPropiedad)
Catch ex As Exception When _bmpTextura Is Nothing
MessageBox.Show("La imagen no puede ser nula.")
End Try
End Sub
'Ejecuta el efecto en la propiedad "EfectosTrans.EfectoActual"
Public Overloads Sub Start()
If Not disposed Then
'Iniciamos los objetos de dibujo y el Timer
EstableceObjetos()
Else
Throw New ObjectDisposedException("", "Esta instancia de la clase EfectosTrans ha sido desechada previamente y por lo tanto, ya no es accesible.")
End If
End Sub
'Ejecuta el efecto pasado en eEfecto
Public Overloads Sub Start(ByVal eEfecto As Efectos)
If Not disposed Then
_Efecto = eEfecto
'Iniciamos los objetos de dibujo y el Timer
EstableceObjetos()
Else
Throw New ObjectDisposedException("", "Esta instancia de la clase EfectosTrans ha sido desechada previamente y por lo tanto, ya no es accesible.")
End If
End Sub
#End Region
#Region " Propiedades "
'EfectoActual
<DefaultValue(GetType(Efectos), "Abanico_Derecha"), _
Category("Comportamiento"), _
Description("Establece el efecto actual para este objeto.")> _
Public Property EfectoActual() As Efectos
Get
Return _Efecto
End Get
Set(ByVal Value As Efectos)
'Si no se esta ejecutando un efecto en este momento cambiamos el efecto
If Not Me.blnEfectoEjecutandose Then
_Efecto = Value
End If
End Set
End Property
'VelocidadEfecto
<DefaultValue(GetType(Integer), "20"), _
Category("Comportamiento"), _
Description("Establece la velocidad de dibujado para este objeto.")> _
Public Property VelocidadEfecto() As Integer
Get
Return _Velocidad
End Get
Set(ByVal Value As Integer)
_Velocidad = ValidaVelocidad(Value)
End Set
End Property
'ColorTrans
<DefaultValue(GetType(Color), "Transparent"), _
Category("Apariencia"), _
Description("Establece el color de relleno para este objeto.")> _
Public Property ColorTrans() As Color
Get
Return _Color
End Get
Set(ByVal Value As Color)
_Color = Value
End Set
End Property
'Image
<Category("Apariencia"), _
Description("Establece la imagen que se usará para realizar el efecto seleccionado.")> _
Public Property Image() As Image
Get
Return _bmpTextura
End Get
Set(ByVal Value As Image)
Try
_bmpTextura = Value
_AnchoImagen = _bmpTextura.Width
_AltoImagen = _bmpTextura.Height
'Hay imagenes que tienen un formato de pixel
'que no permite crear un objeto Graphics para dibujar sobre
'ellas, por eso creamos una copia de la imagen, del mismo tamaño
'y con un formato de pixel que no de problemas
_bmpDibujar = New Bitmap(CInt(_AnchoImagen), CInt(_AltoImagen), PixelFormat.Format32bppArgb)
Catch ex As Exception When Value Is Nothing
Throw New NullReferenceException("La imagen no puede ser nula.")
End Try
End Set
End Property
#End Region
#Region " Destructores "
Public Overloads Sub Dispose() Implements System.IDisposable.Dispose
Dispose(True)
End Sub
Protected Overridable Overloads Sub Dispose(ByVal disposing As Boolean)
If Not Me.disposed Then
If disposing Then
'Acá, limpiamos recursos administrados de esta clase
'En este caso no hay ningúno (que no haya sido descargado)
End If
'Acá, limpiamos recursos no administrados de esta clase
If Not _bmpTextura Is Nothing Then
_bmpTextura.Dispose()
End If
_bmpDibujar.Dispose()
Me.disposed = True
End If
End Sub
' Este metodo Finalize se ejecutará (automaticamente) solamente
' si el metodo Dispose no es usado por el usuario.
Protected Overrides Sub Finalize()
'Limpiamos solamente los recursos no manejados pasando False
Dispose(False)
End Sub
#End Region
#Region " Métodos Privados "
'Establecemos las variables de nivel de modulo
Private Sub Iniciar(ByVal Contenedor As Control, ByVal intVelocidad As Integer, _
ByVal strPropiedad As String)
_Contenedor = Contenedor
_Velocidad = ValidaVelocidad(intVelocidad)
_AnchoImagen = _bmpTextura.Width
_AltoImagen = _bmpTextura.Height
'Hay imagenes que tienen un formato de pixel
'que no permite crear un objeto Graphics para dibujar sobre
'ellas, por eso creamos una copia de la imagen, del mismo tamaño
'y con un formato de pixel que no de problemas
_bmpDibujar = New Bitmap(CInt(_AnchoImagen), CInt(_AltoImagen), PixelFormat.Format32bppArgb)
'Obtenemos y utilizamos la propiedad de tipo Image a partir de strPropiedad
ObtenerPropiedad(strPropiedad)
_Tiempo = New Timer
_Tiempo.Interval = 70
'Controlador del evento Tick del objeto Timer
AddHandler _Tiempo.Tick, AddressOf TiempoTick
End Sub
'El siguiente código me fue proporcionado por Eduardo A. Morcillo [MS MVP VB]
Private Sub ObtenerPropiedad(ByVal strPropiedad As String)
' Obtengo el tipo de control
Dim t As Type = _Contenedor.GetType
' Obtengo la propiedad
_Propiedad = t.GetProperty(strPropiedad)
' Verifico que se devolvio la propiedad
If _Propiedad Is Nothing Then
Throw New ArgumentException("El control no posee la propiedad")
End If
' Verifico que la propiedad devuelva
' el tipo Image o una subclase de el
If Not GetType(Drawing.Image).IsAssignableFrom(_Propiedad.PropertyType) Then
Throw New ArgumentException("La propiedad no es de tipo Image")
End If
End Sub
'(Re)Establece objetos y variables globales a su estado original antes de ejecutar un efecto
Private Sub EstableceObjetos()
Try
'Si el efecto anterior todabia no terminó de ejecutarse salimos
If blnEfectoEjecutandose Then Exit Sub
'Iniciamos el objeto Graphics
_Gr = Graphics.FromImage(_bmpDibujar)
Select Case _Efecto
Case Efectos.Aparecer, Efectos.Estirar_Centro To Efectos.Girar_Espiral_Arriba, Efectos.Rodar_DAbajo, Efectos.Rodar_DArriba
'Para estos efectos no se necesita el objeto _Brocha (se usa DrawImage)
'En el caso de 'Rodar_DArriba' y 'Rodar_DAbajo',
'el código de estos efectos inicializa el objeto
Case Else
'Iniciamos el objeto TextureBrush
IniTextureBrush()
End Select
'******* Aquí se modifica el contenedor de la imagen *******
'Asignamos _bmpDibujar a la propiedad del Contenedor de la imagen
_Propiedad.SetValue(_Contenedor, _bmpDibujar, Nothing)
'******* Aquí se modifica el contenedor de la imagen *******
'Limpiamos el contenido de _bmpDibujar
_Gr.Clear(_Color)
'Reiniciamos variables
_Contador = 0
_Aumento = 0
_AnguloGiro = 0
_xAumentaPos = 0
_yAumentaPos = 0
'True paar indicar que el efecto se va a iniciar
blnEfectoEjecutandose = True
'Iniciamos el objeto Timer
_Tiempo.Start()
Catch ex As Exception When _bmpDibujar Is Nothing
Throw New NullReferenceException("No se ha establecido la imagen donde se dibujará el efecto")
End Try
End Sub
'Inicializa el objeto TextureBrush
Private Sub IniTextureBrush()
_Brocha = New TextureBrush(_bmpTextura)
End Sub
'Controlador del evento Tick del control _Timer
Private Sub TiempoTick(ByVal sender As System.Object, ByVal e As System.EventArgs)
Try
'Ejecutamos cada 80 milesimas de segundo, el efecto selecionado
Select Case _Efecto
Case Efectos.Abanico_Derecha, Efectos.Abanico_Izquierda
Abanico(_Efecto)
Case Efectos.Aparecer
Aparecer()
Case Efectos.Barras_Horizontales, Efectos.Barras_Verticales
Barras(_Efecto)
Case Efectos.Barrido_Horizontal, Efectos.Barrido_Vertical
Barrido(_Efecto)
Case Efectos.Circulos_Fuera
Circulos_Fuera()
Case Efectos.Circulos_Dentro
Circulos_Dentro()
Case Efectos.Desplegar_Centro To Efectos.Desplegar_SI, Efectos.Estirar_Centro To Efectos.Estirar_SI
Desplegar_Estirar(_Efecto)
Case Efectos.Diagonal
Diagonal()
Case Efectos.DivisiónH_Entrante, Efectos.DivisiónH_Saliente
DivisionH(_Efecto)
Case Efectos.DivisiónV_Entrante, Efectos.DivisiónV_Saliente
DivisionV(_Efecto)
Case Efectos.Empujar_Abajo To Efectos.Empujar_Izquierda
Empujar(_Efecto)
Case Efectos.Girar_Centro To Efectos.Girar_Espiral_Arriba
Girar(_Efecto)
Case Efectos.PersianasH_Abajo, Efectos.PersianasH_Arriba
PersianasH(_Efecto)
Case Efectos.PersianasV_Derecha, Efectos.PersianasV_Izquierda
PersianasV(_Efecto)
Case Efectos.Reloj, Efectos.Reloj_AntiHorario
Reloj(_Efecto)
Case Efectos.Simetrico_Adentro To Efectos.Simetrico_Izquierda
Simetrico(_Efecto)
Case Efectos.Rueda_2Ejes To Efectos.Rueda_8Ejes
RuedaMultiple(_Efecto)
Case Efectos.Empuja_División_Lados, Efectos.Empuja_División_Topes
EmpujaDivision(_Efecto)
Case Efectos.Rodar_DAbajo To Efectos.Rodar_IArriba
Rodar(_Efecto)
End Select
Catch ex As System.Exception
Throw New System.Exception(ex.ToString)
End Try
End Sub
'Descarga objetos y detiene el Timer
Private Sub DescargaObjetos()
_Gr.Dispose()
If Not (_Brocha Is Nothing) Then
_Brocha.Dispose()
End If
_Tiempo.Stop()
'False para indicar que el efecto acaba de terminar
blnEfectoEjecutandose = False
'El efecto terminó, disparamos un evento para indicarlo
RaiseEvent EffectEnded(Me, New EventArgs)
End Sub
'Valida la propiedad VelocidadEfecto
Private Function ValidaVelocidad(ByVal intValor As Integer) As Integer
If intValor <= 0 Then
Return _Velocidad ' Valor por defecto (20)
ElseIf intValor > 100 Then
Return 100
Else : Return intValor
End If
End Function
#Region " Efectos de Transición "
'Genera los efecto de abanico
Private Sub Abanico(ByVal Efecto As Efectos)
'Calculamos por Pitagoras el radio de la circunferencia con centro en (_AnchoImagen / 2, _AltoImagen)
'que dibujará la imagen.
Dim Radio As Single = CSng(Math.Pow(((_AnchoImagen / 2) ^ 2) + (_AltoImagen ^ 2), 1 / 2))
'Negativo: dirección del efecto
Dim AnguloInicio, Negativo As Integer
'iniciamos variables según el efecto
If Efecto = Efectos.Abanico_Derecha Then
AnguloInicio = 180