Efectos de Transición de Imágenes (Actualizado)
(Al estilo de los de PowerPoint)

Fecha: 01/Abr/2004 (02/Abr/2004)
Revisado: 20/Abr/2004, 05/Abr/2004
Autor: Aníbal Svarcas. asvarcas@montevideo.com.uy

.

Programa en Ejecución

 

Este proyecto explica como realizar efectos de transición (movimiento) al estilo de los de Microsoft PowerPoint con GDI+.

Para eso vamos a usar un objeto Graphics y un objeto TextureBrush para dibujar sobre una imagen.

Para crear el proyecto de ejemplo, abrir el Visual Studio y seleccionar "Aplicación para Windows".

Vamos a usar un PictureBox donde pondremos una imagen donde dibujaremos los efectos; un ListBox que va a tener listados los efectos; un Botón para reproducirlos y un ComboBox para modificar la velocidad de los efectos.

Copiar el siguiente código y pegarlo en la vista de código del formulario "Form1" sobrescribiendo

todo el código que se encuentre.

 

Option Explicit On 
Option Strict On
Imports System
Imports System.Drawing.Drawing2D
Imports System.Drawing.Imaging
#Region " Nota Importante "
'PARA EVITAR PARPADEO EN EFECTOS VISUALES  DE ESTE TIPO CON IMAGENES,
'DIBUJAR SOBRE UNA IMAGEN VACIA UBICADA EN UN CONTROL,
'Y REFRESCAR EL CONTROL (METODO REFRESH) EN CADA ITERACIÓN
'DE BUCLE O PASADA DE TIMER.
#End Region
Public Class frmEfectos
   Inherits System.Windows.Forms.Form
#Region " Código generado por el Diseñador de Windows Forms "
   Public Sub New()
      MyBase.New()
      'El Diseñador de Windows Forms requiere esta llamada.
      InitializeComponent()
      'Agregar cualquier inicialización después de la llamada a InitializeComponent()
      'Iniciamos algunos elementos de nuestra interface
      Iniciar()
   End Sub
   'Form reemplaza a Dispose para limpiar la lista de componentes.
   Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
      If disposing Then
         If Not (components Is Nothing) Then
            components.Dispose()
         End If
      End If
      MyBase.Dispose(disposing)
   End Sub
   'Requerido por el Diseñador de Windows Forms
   Private components As System.ComponentModel.IContainer
   'NOTA: el Diseñador de Windows Forms requiere el siguiente procedimiento
   'Puede modificarse utilizando el Diseñador de Windows Forms. 
   'No lo modifique con el editor de código.
   Friend WithEvents pic As System.Windows.Forms.PictureBox
   Friend WithEvents lbEfectos As System.Windows.Forms.ListBox
   Friend WithEvents btnReproducir As System.Windows.Forms.Button
   Friend WithEvents lbSpeed As System.Windows.Forms.Label
   Friend WithEvents lblMulti As System.Windows.Forms.Label
   Friend WithEvents cboEjes As System.Windows.Forms.ComboBox
   Friend WithEvents cboSpeed As System.Windows.Forms.ComboBox
   <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
      Me.pic = New System.Windows.Forms.PictureBox()
      Me.lbEfectos = New System.Windows.Forms.ListBox()
      Me.btnReproducir = New System.Windows.Forms.Button()
      Me.lbSpeed = New System.Windows.Forms.Label()
      Me.cboEjes = New System.Windows.Forms.ComboBox()
      Me.lblMulti = New System.Windows.Forms.Label()
      Me.cboSpeed = New System.Windows.Forms.ComboBox()
      Me.SuspendLayout()
      '
      'pic
      '
      Me.pic.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle
      Me.pic.Location = New System.Drawing.Point(205, 82)
      Me.pic.Name = "pic"
      Me.pic.Size = New System.Drawing.Size(581, 359)
      Me.pic.TabIndex = 0
      Me.pic.TabStop = False
      '
      'lbEfectos
      '
      Me.lbEfectos.HorizontalScrollbar = True
      Me.lbEfectos.Items.AddRange(New Object() {"Barrido Horizontal", "Barrido Vertical", "Circulos Concentricos", "División Horizontal Entrante", "División Horizontal Saliente", "División Vertical Entrante", "División Vertical Saliente", "Empuja División Lados", "Empuja División Topes", "Empujar a la Derecha", "Empujar a la Izquierda", "Empujar en Diagonal (ángulo inferior derecho)", "Empujar en Diagonal (ángulo inferior izquierdo)", "Empujar en Diagonal (ángulo superior derecho)", "Empujar en Diagonal (ángulo superior izquierdo)", "Empujar Hacia Abajo", "Empujar Hacia Arriba", "Persianas Horizontales Abajo", "Persianas Horizontales Arriba", "Persianas Verticales Derecha", "Persianas Verticales Izquierda", "Rueda en sentido de las agujas del Reloj", "Rueda Multiple", "Simetrico"})
      Me.lbEfectos.Location = New System.Drawing.Point(8, 82)
      Me.lbEfectos.Name = "lbEfectos"
      Me.lbEfectos.Size = New System.Drawing.Size(182, 290)
      Me.lbEfectos.Sorted = True
      Me.lbEfectos.TabIndex = 0
      '
      'btnReproducir
      '
      Me.btnReproducir.Location = New System.Drawing.Point(8, 468)
      Me.btnReproducir.Name = "btnReproducir"
      Me.btnReproducir.Size = New System.Drawing.Size(90, 34)
      Me.btnReproducir.TabIndex = 1
      Me.btnReproducir.Text = "Reproducir"
      '
      'lbSpeed
      '
      Me.lbSpeed.Location = New System.Drawing.Point(8, 394)
      Me.lbSpeed.Name = "lbSpeed"
      Me.lbSpeed.Size = New System.Drawing.Size(89, 20)
      Me.lbSpeed.TabIndex = 3
      Me.lbSpeed.Text = "Velocidad"
      Me.lbSpeed.TextAlign = System.Drawing.ContentAlignment.MiddleCenter
      '
      'cboEjes
      '
      Me.cboEjes.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList
      Me.cboEjes.ItemHeight = 13
      Me.cboEjes.Items.AddRange(New Object() {"2", "3", "4", "8"})
      Me.cboEjes.Location = New System.Drawing.Point(101, 427)
      Me.cboEjes.Name = "cboEjes"
      Me.cboEjes.Size = New System.Drawing.Size(89, 21)
      Me.cboEjes.TabIndex = 4
      Me.cboEjes.Visible = False
      '
      'lblMulti
      '
      Me.lblMulti.Location = New System.Drawing.Point(8, 427)
      Me.lblMulti.Name = "lblMulti"
      Me.lblMulti.Size = New System.Drawing.Size(89, 20)
      Me.lblMulti.TabIndex = 5
      Me.lblMulti.Text = "Nº de Ejes"
      Me.lblMulti.TextAlign = System.Drawing.ContentAlignment.MiddleCenter
      Me.lblMulti.Visible = False
      '
      'cboSpeed
      '
      Me.cboSpeed.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList
      Me.cboSpeed.Items.AddRange(New Object() {"Rápido", "Medio", "Lento"})
      Me.cboSpeed.Location = New System.Drawing.Point(101, 394)
      Me.cboSpeed.Name = "cboSpeed"
      Me.cboSpeed.Size = New System.Drawing.Size(89, 21)
      Me.cboSpeed.TabIndex = 6
      '
      'frmEfectos
      '
      Me.AcceptButton = Me.btnReproducir
      Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
      Me.AutoScroll = True
      Me.ClientSize = New System.Drawing.Size(804, 525)
      Me.Controls.AddRange(New System.Windows.Forms.Control() {Me.cboSpeed, Me.lblMulti, Me.cboEjes, Me.lbSpeed, Me.btnReproducir, Me.lbEfectos, Me.pic})
      Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog
      Me.MinimumSize = New System.Drawing.Size(700, 550)
      Me.Name = "frmEfectos"
      Me.Text = "Efectos"
      Me.WindowState = System.Windows.Forms.FormWindowState.Maximized
      Me.ResumeLayout(False)
   End Sub
#End Region
#Region " Enumeraciones de Efectos "
   'Enumeración de Efectos
   Private Enum Efectos
      BarridoH = 0
      BarridoV
      Circulos
      DivisiónH_Entrante
      DivisiónH_Saliente
      DivisiónV_Entrante
      DivisiónV_Saliente
      Empuja_División_Lados
      Empuja_División_Topes
      Empujar_Derecha
      Empujar_Izquierda
      Empujar_DiagonalID
      Empujar_DiagonalII
      Empujar_DiagonalSD
      Empujar_DiagonalSI
      Empujar_Abajo
      Empujar_Arriba
      PersianasH_Abajo
      PersianasH_Arriba
      PersianasV_Derecha
      PersianasV_Izquierda
      Rueda
      RuedaMultiple
      Simetrico
   End Enum
   'Enumeración de Efectos de Rueda Multiple
   Private Enum Ejes
      DosEjes = 0
      TresEjes
      CuatroEjes
      OchoEjes
   End Enum
#End Region
#Region " Variables a nivel de modulo "
   'Control Timer que controlará los Efectos
   Private Tiempo  As New Timer()
   'Velocidad de los efectos
   Private VelocidadEfecto As Integer 
   'Cuenta las veces que se debe ejecutar el evento Tick para cada efecto
   Private Contador As Integer 
   'Contiene una copia de la imagén usada
   Private bmCopia As Bitmap
   'Creamos un objeto Graphics para dibujar en Copia
   Private Gr As Graphics
   'Creamos un TextureBrush con la textura de la imágen
   'del PictureBox
   Private Brocha As TextureBrush
   'Contienen el Ancho y Alto
   'de la Imágen respectivamente
   Private AnchoImagen, AltoImagen As Single
#Region " Variables especificas de Efectos "
   'Ángulo de giro 
   Private AnguloGiro As Single 
   'Cantidad en que se aumentan las transformaciones de ejes.
   Private xAumentaPos, yAumentaPos As Single 
   'Determina si es la primera vez que se ejecuta un efecto
   Private EsPrimera As Boolean
   'Aumento progresivo de los dibujos
   Private Aumento As Single
#End Region
#End Region
   'Inicia las variables de nivel de modulo y
   'establece algunas propiedades de la interface
   Private Sub Iniciar()
      'Cargamos la imagen. Si no se descarga el proyecto de ejemplo,
      'ASEGURARSE DE UBICAR UNA IMAGEN VÁLIDA en la línea siguiente
      pic.Image = New Bitmap("..\Dibujo1.jpg")
      pic.SizeMode = PictureBoxSizeMode.AutoSize
      '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 pic.Image,del mismo tamaño
      'y con un formato de pixel que no de problemas
      bmCopia = New Bitmap(pic.Image.Width, pic.Image.Height, PixelFormat.Format32bppArgb)
      'Establecemos las variables de nivel de modulo
      AnchoImagen = bmCopia.Width
      AltoImagen = bmCopia.Height
      'Controlador del evento Tick del objeto Timer
      AddHandler Tiempo.Tick, AddressOf TiempoEfectos
      Tiempo.Interval = 100
   End Sub
   'Seleccionamos el primer elemento del ListBox y los ComboBox para que todo quede listo para usar.
   Private Sub frmEfectos_Load(ByVal sender As Object, ByVale As System.EventArgs) Handles MyBase.Load
      lbEfectos.SetSelected(0, True)
      cboSpeed.SelectedIndex = 1
      cboEjes.SelectedIndex = 0
   End Sub
   'Inicia objetos, variables y controles
   Private Sub EstableceObjetos()
      'Iniciamos el objeto Graphics
      Gr = Graphics.FromImage(bmCopia)
      'Iniciamos el objeto TextureBrush
      Brocha = New TextureBrush(pic.Image)
      'Asignamos bmCopia a el PictureBox
      pic.Image = bmCopia
      'Limpiamos el contenido de bmCopia
      Gr.Clear(pic.BackColor)
      'Elección de Velocidades
      Select Case cboSpeed.SelectedItem.ToString
         Case "Rápido"
            VelocidadEfecto = 5
         Case "Medio"
            VelocidadEfecto = 15
         Case "Lento"
            VelocidadEfecto = 30
      End Select 
      'Deshabilitamos controles hasta que se termine el efecto
      btnReproducir.Enabled = False
      lbEfectos.Enabled = False
      'Reiniciamos variables
      Contador = 0
      Aumento = 0
      AnguloGiro = 0
      xAumentaPos = 0
      yAumentaPos = 0
      EsPrimera = True
      'Iniciamos el objeto Timer
      Tiempo.Start()
   End Sub
   'Muestra los controles de los efectos de Rueda Multiple
   Private Sub lbEfectos_SelectedValueChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles lbEfectos.SelectedValueChanged
      lblMulti.Visible = (lbEfectos.SelectedIndex = Efectos.RuedaMultiple)
      cboEjes.Visible = (lbEfectos.SelectedIndex = Efectos.RuedaMultiple)
   End Sub
   'Reproduce el Efecto seleccionado en el ListBox
   Private Sub btnReproducir_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
                                   Handles btnReproducir.Click, lbEfectos.DoubleClick
      'Iniciamos los objetos de dibujo ,el PictureBox y el Timer
      EstableceObjetos()
   End Sub
   'Controlador del evento Tick del control Timer
   Private Sub TiempoEfectos(ByVal sender As System.Object, ByVal e As System.EventArgs)
      Try
         'Ejecutamos cada 100 milesimas de segundo, el efecto selecionado
         Select Case lbEfectos.SelectedIndex
            Case Efectos.BarridoH
               Barrido(Efectos.BarridoH)
            Case Efectos.BarridoV
               Barrido(Efectos.BarridoV)
            Case Efectos.Circulos
               Circulos()
            Case Efectos.DivisiónH_Entrante
               DivisionH(Efectos.DivisiónH_Entrante)
            Case Efectos.DivisiónH_Saliente
               DivisionH(Efectos.DivisiónH_Saliente)
            Case Efectos.DivisiónV_Entrante
               DivisionV(Efectos.DivisiónV_Entrante)
            Case Efectos.DivisiónV_Saliente
               DivisionV(Efectos.DivisiónV_Saliente)
            Case Efectos.Empujar_Abajo
               Empujar(Efectos.Empujar_Abajo)
            Case Efectos.Empujar_Arriba
               Empujar(Efectos.Empujar_Arriba)
            Case Efectos.Empujar_Derecha
               Empujar(Efectos.Empujar_Derecha)
            Case Efectos.Empujar_DiagonalID
               Empujar(Efectos.Empujar_DiagonalID)
            Case Efectos.Empujar_DiagonalII
               Empujar(Efectos.Empujar_DiagonalII)
            Case Efectos.Empujar_DiagonalSD
               Empujar(Efectos.Empujar_DiagonalSD)
            Case Efectos.Empujar_DiagonalSI
               Empujar(Efectos.Empujar_DiagonalSI)
            Case Efectos.Empujar_Izquierda
               Empujar(Efectos.Empujar_Izquierda)
            Case Efectos.PersianasH_Abajo
               PersianasH(Efectos.PersianasH_Abajo)
            Case Efectos.PersianasH_Arriba
               PersianasH(Efectos.PersianasH_Arriba)
            Case Efectos.PersianasV_Derecha
               PersianasV(Efectos.PersianasV_Derecha)
            Case Efectos.PersianasV_Izquierda
               PersianasV(Efectos.PersianasV_Izquierda)
            Case Efectos.Rueda
               Rueda()
            Case Efectos.Simetrico
               Simetrico()
            Case Efectos.RuedaMultiple
               'Seleciona la variante de efecto de Rueda Multiple
               Select Case cboEjes.SelectedIndex
                  Case 0
                     RuedaMultiple(Ejes.DosEjes)
                  Case 1
                     RuedaMultiple(Ejes.TresEjes)
                  Case 2
                     RuedaMultiple(Ejes.CuatroEjes)
                  Case 3
                     RuedaMultiple(Ejes.OchoEjes)
               End SelectCase Efectos.Empuja_División_Lados
               EmpujaDivision(Efectos.Empuja_División_Lados)
            Case Efectos.Empuja_División_Topes
               EmpujaDivision(Efectos.Empuja_División_Topes)
         End Select
      Catch ex As System.Exception
      End Try
   End Sub
 
Efectos con Persianas.
Estos efectos dibujan una imagen con franjas que se mueven en una misma dirección,
como persianas que se cierran. 
Para generar este tipo de efectos, la idea es dividir la imagen en rectángulos,
de igual tamaño, e ir dibujándolos todos al mismo tiempo pero solamente una franja de cada
rectángulo por vez (ejecución del evento del Timer). Para poder ir dibujando todos los rectángulos a la vez,
lo que hacemos es primero dibujarlos todos en memoria y luego los mostramos de una sola vez.
   
   'Genera los efectos con Persianas Horizontales
   Private Sub PersianasH(ByVal Efecto As Efectos)
      Dim Persiana As Integer 'Total de Persianas
      Dim TotalPersianas As Integer= 12
      Dim Velocidad As Integer= TotalPersianas * VelocidadEfecto
      'Si es la primera vez que se llega aquí, establecemos algunas variables
      If EsPrimera Then
         If Efecto = Efectos.PersianasH_Abajo Then
            Aumento = 0
         ElseIf Efecto = Efectos.PersianasH_Arriba Then
            Aumento = AltoImagen / TotalPersianas
         End If
         EsPrimera = False
      End If
      For Persiana = 0 To TotalPersianas - 1
         'Dibuja un trozo de cada una de las persianas por cada ejecución del evento
         Dim SupIzquierda  As New PointF(0, Aumento + (Persiana * (AltoImagen / TotalPersianas)))
         Dim TamañoPersiana  As New SizeF(AnchoImagen, AltoImagen / Velocidad)
         Dim RecPersiana  As New RectangleF(SupIzquierda, TamañoPersiana)
         Gr.FillRectangle(Brocha, RecPersiana)
      Next
      pic.Refresh()
      'Aumentamos el tamaño de cada persiana para la próxima vez que se dibujen
      If Efecto = Efectos.PersianasH_Abajo Then
         Aumento += AltoImagen / Velocidad
      ElseIf Efecto = Efectos.PersianasH_Arriba Then
         Aumento -= AltoImagen / Velocidad
      End If
      'Contamos las veces que se ejecutó este evento
      Contador += 1
      'Cuando el evento se halla ejecutado VelocidadEfecto + 1 veses terminamos el efecto
      If Contador = VelocidadEfecto + 1 Then
         DescargaObjetos()
      End If
   End Sub
   'Genera los efectos con Persianas Verticales
   Private Sub PersianasV(ByVal Efecto As Efectos)
      Dim Persiana As IntegerDim TotalPersianas As Integer= 12
      Dim Velocidad As Integer= TotalPersianas * VelocidadEfecto
      'Si es la primera vez que se llega aquí, establecemos algunas variables
      If EsPrimera Then
         If Efecto = Efectos.PersianasV_Izquierda Then
            Aumento = 0
         ElseIf Efecto = Efectos.PersianasV_Derecha Then
            Aumento = AnchoImagen / TotalPersianas
         End If
         EsPrimera = False
      End If
      For Persiana = 0 To TotalPersianas - 1
         'Dibuja un trozo de cada una de las persianas por cada ejecución del evento
         Dim SupIzquierda  As New PointF(Aumento + (Persiana * (AnchoImagen / TotalPersianas)), 0)
         Dim TamañoPersiana  As New SizeF(AnchoImagen / Velocidad, AltoImagen)
         Dim RecPersiana  As New RectangleF(SupIzquierda, TamañoPersiana)
         Gr.FillRectangle(Brocha, RecPersiana)
      Next
      pic.Refresh()
      'Aumentamos el tamaño de cada persiana para la próxima vez que se dibujen
      If Efecto = Efectos.PersianasV_Izquierda Then
         Aumento += AnchoImagen / Velocidad
      ElseIf Efecto = Efectos.PersianasV_Derecha Then
         Aumento -= AnchoImagen / Velocidad
      End If
      'Contamos las veces que se ejecutó este evento
      Contador += 1
      'Cuando el evento se halla ejecutado VelocidadEfecto + 1 veses terminamos el efecto
      If Contador = VelocidadEfecto + 1 Then
         DescargaObjetos()
      End If
   End Sub
Efecto de Persianas Horizontales.
 
Efectos de Empuje.
Para generar este tipo de efectos, lo que vamos a hacer es cambiar el centro de los ejes de coordenadas
del objeto Graphics con el método TranslateTransform, lo suficientemente hacia 
un lado u otro para que inicialmente la imagen se dibuje pero no se vea, y luego con deshaciendo
la transformación progresivamente y volviendo a dibujar cada vez toda la imagen.
El efecto que se crea es de una imagen que se mueve de un lado para otro, como si la empujaran.
      
   'Genera los efectos de Empuje
   Private Sub Empujar(ByVal Efecto As Efectos)
      'Si es la primera vez que se llega aquí, 
      'Establecemos las transformaciónes iniciales
      'según la variante de efecto de Empuje
      If EsPrimera Then
         If Efecto = Efectos.Empujar_Abajo Then
            Gr.TranslateTransform(0, -AltoImagen)
            xAumentaPos = 0 : yAumentaPos = AltoImagen / VelocidadEfecto
         ElseIf Efecto = Efectos.Empujar_Arriba Then
            Gr.TranslateTransform(0, AltoImagen)
            xAumentaPos = 0 : yAumentaPos = -AltoImagen / VelocidadEfecto
         ElseIf Efecto = Efectos.Empujar_Derecha Then
            Gr.TranslateTransform(-AnchoImagen, 0)
            xAumentaPos = AnchoImagen / VelocidadEfecto : yAumentaPos = 0
         ElseIf Efecto = Efectos.Empujar_Izquierda Then
            Gr.TranslateTransform(AnchoImagen, 0)
            xAumentaPos = -AnchoImagen / VelocidadEfecto : yAumentaPos = 0
         ElseIf Efecto = Efectos.Empujar_DiagonalSI Then
            Gr.TranslateTransform(-AnchoImagen, -AltoImagen)
            xAumentaPos = AnchoImagen / VelocidadEfecto : yAumentaPos = AltoImagen / VelocidadEfecto
         ElseIf Efecto = Efectos.Empujar_DiagonalSD Then
            Gr.TranslateTransform(AnchoImagen, -AltoImagen)
            xAumentaPos = -AnchoImagen / VelocidadEfecto : yAumentaPos = AltoImagen / VelocidadEfecto
         ElseIf Efecto = Efectos.Empujar_DiagonalII Then
            Gr.TranslateTransform(-AnchoImagen, AltoImagen)
            xAumentaPos = AnchoImagen / VelocidadEfecto : yAumentaPos = -AltoImagen / VelocidadEfecto
         ElseIf Efecto = Efectos.Empujar_DiagonalID Then
            Gr.TranslateTransform(AnchoImagen, AltoImagen)
            xAumentaPos = -AnchoImagen / VelocidadEfecto : yAumentaPos = -AltoImagen / VelocidadEfecto
         End If
         EsPrimera = False
      End If
      'Limpiamos el contenido de bmCopia
       Gr.Clear(pic.BackColor)

      'Dibujamos la imagen con las transformaciónes de ejes.
      Dim RecEmpuja  As New RectangleF(0, 0, AnchoImagen, AltoImagen)
      Gr.FillRectangle(Brocha, RecEmpuja)
      Gr.TranslateTransform(xAumentaPos, yAumentaPos)
      pic.Refresh()
      'Contamos las veces que se ejecutó este evento
      Contador += 1
      'Cuando el evento se halla ejecutado VelocidadEfecto + 1 veses terminamos el efecto
      If Contador = VelocidadEfecto + 1 Then
         DescargaObjetos()
      End If
   End Sub
Efecto de Empuje Diagonal.
 
Efectos de División.
Hay cuatro variantes; dos de ellas dibujan la imagen desde los lados (o los topes) hacia el centro
como puertas deslizantes que se cierran. Y las otras dos hacen lo opuesto, o sea, dibujan 
desde el centro hacia los extremos. Lo que vamos a hacer es dibujar 
de a dos rectángulos al mismo tiempo por iteración aumentándoles el tamaño. Esto lo podemos hacer
como en algunos de los efectos anteriores, mediante lo que se llama DoubleBuffer, que consiste en 
ir dibujando primero en memoria todo para luego mostrarlo, de esta manera se logran efectos más "limpios",
o sea, con menos parpadeos y en nuestro caso, también nos sirve para aparentar visualmente
que varios dibujos se crean en el mismo instante.
 
   'Genera los efectos de Divición Horizontal
   Private Sub DivisionH(ByVal Efecto As Efectos)
      Dim Velocidad As Single= AnchoImagen / VelocidadEfecto
      'Si es la primera vez que se llega aquí, establecemos algunas variables
      If EsPrimera Then
         If Efecto = Efectos.DivisiónH_Entrante Then
            Aumento = 0
         ElseIf Efecto = Efectos.DivisiónH_Saliente Then
            Aumento = AnchoImagen / 2
         End If
         EsPrimera = False
      End If
      Dim RecDivision  As New RectangleF(Aumento, 0, Velocidad, AltoImagen)
      Dim RecDivision2  As New RectangleF(AnchoImagen - Aumento, 0, Velocidad, AltoImagen)
      'Dibujamos los rectangulos de división
      Gr.FillRectangle(Brocha, RecDivision)
      Gr.FillRectangle(Brocha, RecDivision2)
      pic.Refresh()
      'Aumentamos el tamaño de los rectangulos
      Aumento += Velocidad
      'Contamos las veces que se ejecutó este evento
      Contador += 1
      'Cuando el evento se halla ejecutado la mitad de la cantidad de
      'diviciones del Ancho + 1 veses terminamos el efecto
      If Contador = ((VelocidadEfecto + 1) \ 2) + 1 Then
         DescargaObjetos()
      End If
   End Sub
   'Genera los efectos de Divición Vertical
   Private Sub DivisionV(ByVal Efecto As Efectos)
      Dim Velocidad As Single= AltoImagen / VelocidadEfecto
      'Si es la primera vez que se llega aquí, establecemos algunas variables
      If EsPrimera Then
         If Efecto = Efectos.DivisiónV_Entrante Then
            Aumento = 0
         ElseIf Efecto = Efectos.DivisiónV_Saliente Then
            Aumento = AltoImagen / 2
         End If
         EsPrimera = False
      End If
      Dim RecDivision  As New RectangleF(0, Aumento, AnchoImagen, Velocidad)
      Dim RecDivision2  As New RectangleF(0, AltoImagen - Aumento, AnchoImagen, Velocidad)
      'Dibujamos los rectangulos de división
      Gr.FillRectangle(Brocha, RecDivision)
      Gr.FillRectangle(Brocha, RecDivision2)
      pic.Refresh()
      'Aumentamos el tamaño de los rectangulos
      Aumento += Velocidad
      'Contamos las veces que se ejecutó este evento
      Contador += 1
      'Cuando el evento se halla ejecutado la mitad de la cantidad de
      'diviciones del Alto + 1 veses terminamos el efecto
      If Contador = ((VelocidadEfecto + 1) \ 2) + 1 Then
         DescargaObjetos()
      End If
   End Sub

 

Efecto de División Horizontal.
 
Efectos de Barrido.
Este efecto divide la imagen en rectángulos de igual tamaño para luego ir moviendo la mitad
de estos rectángulos desde la derecha hacia el centro, y la otra mitad desde la izquierda hacia el centro
de manera que al encontrarce se intercalen para formar la imagen completa. (También hay una variante 
donde los rectángulos se dibujan de abjo y de arriba hacia el centro)
La metodología es similar a la de los efectos de Empuje pero ahora
necesitamos no uno sino dos objetos Graphics para dibujar en la misma imagen ya que vamos 
a hacer más de una transformación al mismo tiempo. A uno le vamos a aplicar las transformaciones 
de los ejes de coordenadas hacia un lado y al otro hacia el lado opuesto. Luego mediante el Timer
movemos progresivamente el centro de coordenadas a su posición original (La esquina superior izquierda)
para los dos objetos Graphics y cada vez dibujamos con un bucle una mitad de los rectángulos con uno 
de los objetos Graphics y el la otra mitad con el otro.
 
   'Genera los efectos de Barrido
   Private Sub Barrido(ByVal Efecto As Efectos)
      'Creamos el segundo objeto Graphics para dibujar en bmCopia
      Static Gr2 As Graphics
      Dim Persiana As Single
      Static xEsquinaPersiana, yEsquinaPersiana As Single 
      Static AnchoPersiana, AltoPersiana As Single 
      'Si es la primera vez que se llega aquí, establecemos algunas variables
      If EsPrimera Then
         Gr2 = Graphics.FromImage(bmCopia)
         If Efecto = Efectos.BarridoH Then
            xAumentaPos = AnchoImagen : yAumentaPos = 0
            xEsquinaPersiana = 0 : yEsquinaPersiana = AltoImagen / 20
            AnchoPersiana = AnchoImagen : AltoPersiana = AltoImagen / 20
         ElseIf Efecto = Efectos.BarridoV Then
            xAumentaPos = 0 : yAumentaPos = AltoImagen
            xEsquinaPersiana = AnchoImagen / 20 : yEsquinaPersiana = 0
            AnchoPersiana = AnchoImagen / 20 : AltoPersiana = AltoImagen
         End If
         'Movemos el centro del eje de coordenadas la primera vez
         'que se ejecuta el evento, para que la imagen se dibuje pero no se vea
         Gr.TranslateTransform(xAumentaPos, yAumentaPos)
         Gr2.TranslateTransform(-xAumentaPos, -yAumentaPos)
         EsPrimera = False
      End If
      'Limpiamos el contenido de bmCopia
      Gr.Clear(pic.BackColor)
      For Persiana = 0 To 20 'Nº de Persianas (20)
         'Las persianas pares las dibujamos con Gr,
         'y las impares con Gr2
         Dim SupIzquierda  As New PointF(Persiana * xEsquinaPersiana, Persiana * yEsquinaPersiana)
         Dim TamañoPersiana  As New SizeF(AnchoPersiana, AltoPersiana)
         If Persiana Mod 2 = 0 Then
            Dim RecBarridoPar  As New RectangleF(SupIzquierda, TamañoPersiana)
            Gr.FillRectangle(Brocha, RecBarridoPar)
         Else
            Dim RecBarridoImpar  As New RectangleF(SupIzquierda, TamañoPersiana)
            Gr2.FillRectangle(Brocha, RecBarridoImpar)
         End If
      Next
      'Transladamos los ejes progresivamente
      Gr.TranslateTransform(-xAumentaPos / VelocidadEfecto, -yAumentaPos / VelocidadEfecto)
      Gr2.TranslateTransform(xAumentaPos / VelocidadEfecto, yAumentaPos / VelocidadEfecto)
      'Contamos las veces que movimos la imágen
      Contador += 1
      'Refrescamos el PictureBox
      pic.Refresh()
      'Cuando el evento se halla ejecutado VelocidadEfecto + 1
      'veses terminamos el efecto
      If Contador = VelocidadEfecto + 1 Then
         Gr2.Dispose()
         xEsquinaPersiana = 0 : yEsquinaPersiana = 0
         AnchoPersiana = 0 : AltoPersiana = 0
         DescargaObjetos()
      End If
   End Sub

  

Efecto de Barrido Horizontal.
 
Efecto de Rueda.
Este efecto dibuja la imagen en forma de rueda en sentido de las agujas del reloj.
Para lograrlo vamos a usar el método FillPie del Objeto Graphics el cual llena una torta
con el objeto TextureBrush especificado y con un ángulo de giro especificado.
O sea que iremos agrandando progresivamente el ángulo de giro hasta 
dar una vuelta completa (360º).
Como una imagen obviamente no es una torta sino un rectángulo lo que vamos a hacer
es llenar con nuestro objeto TextureBrush una torta que sea más grande que el rectángulo
de la imagen (El diametro de la torta será la diagonal de la imagen) y que este centrada 
en la imagen, permitiendo dibujar toda la imagen.
   
   'Genera el efecto de Rueda
   Private Sub Rueda()
      'La diagonal de la imágen va a ser el diametro. 
      'La calculamos por Pitagoras.
      Dim Diagonal As Single= CSng(Math.Pow((AltoImagen ^ 2) + (AnchoImagen ^ 2), 1 / 2))
      'Llenamos una torta con centro en el centro del PictureBox,
      'con la textura de la imágen original,
      'agregandole trozos de AnguloGiro
      Gr.FillPie(Brocha, CSng(-(Diagonal - AnchoImagen) / 2), CSng(-(Diagonal - AltoImagen) / 2), Diagonal, Diagonal, -90, AnguloGiro)
      'Mostramos el trozo actual
      pic.Refresh()
      AnguloGiro += CSng(360 / VelocidadEfecto)
      'Contamos las veces que se ejecutó este evento
      Contador += 1
      'Cuando el evento se halla ejecutado VelocidadEfecto + 1
      'veses terminamos el efecto
      If Contador = VelocidadEfecto + 1 Then
         DescargaObjetos()
      End If
   End Sub
Efecto de Rueda.
 
Efecto con Círculos.
Este efecto dibuja la imagen llenando círculos concéntricos con nuestro TextureBrush. 
Para lograrlo vamos a usar el método FillEllipse del Objeto Graphics el cual llena una elipse
con el objeto TextureBrush especificado. (Recordar que una circunferencia es un caso particular de elipse)
Vamos a centrar los círculos en el centro de nuestra imagen y mediante el objeto Timer vamos a ir 
llenando de a un circulo y agrandando el radio para que se dibujen los sucesivos círculos hasta haber 
pintado toda la imagen. Ya que tenemos que dibujar círculos dentro de un rectángulo para saber
cuando dejar de pintar vamos a hacer que el diámetro máximo sea el tamaño de la diagonal del rectángulo.
Además, para entender mejor el código recordar que una elipse se dibuja pasándole un rectángulo 
al método FillElipse en el cual se dibujará la elipse circunscripta en él.
   
   'Genera el efecto con Circulos
   Private Sub Circulos()
      'El diametro de los circulos
      Static Diametro As Single 
      'La diagonal de la imágen va a ser el diametro máximo. 
      'La calculamos por Pitagoras.
      Dim Diagonal As Single= CSng(Math.Pow((AltoImagen ^ 2) + (AnchoImagen ^ 2), 1 / 2))
      'Definimos las coordenadas del punto superior
      'izquierdo del rectángulo a partir del cual
      'se dibujará un circulo circunscripto en él, de modo que 
      'el centro de el circulo coincida con el centro del PictureBox
      Dim xEsquina As Single = (AnchoImagen / 2) - (Diametro / 2)
      Dim yEsquina As Single = (AltoImagen / 2) - (Diametro / 2)
      Dim MiPunto  As New PointF(xEsquina, yEsquina)
      'Tamaño del rectángulo donde se dibujará el circulo
      Dim Tamaño As SizeF = New SizeF(Diametro, Diametro)
      Dim Rectángulo  As New RectangleF(MiPunto, Tamaño)
      Gr.FillEllipse(Brocha, Rectángulo)
      'Mostramos el circulo actual
      pic.Refresh()
      Diametro += Diagonal / VelocidadEfecto
      'Contamos las veces que se ejecutó este evento
      Contador += 1
      'Cuando el evento se halla ejecutado VelocidadEfecto + 1
      'veses terminamos el efecto
      If Contador = VelocidadEfecto + 1 Then
         Diametro = 0
         DescargaObjetos()
      End If
   End Sub
Efecto con Círculos.
 
Efectos de Empuje y División.
Estos efectos son una combinación entre los de Empuje y Divición. Vamos a usar dos 
objetos Graphics para dibujar en la misma imagen. Cambiamos el centro de los ejes de 
coordenadas del primer objeto Graphics con el método TranslateTransform, lo suficientemente hacia 
un lado para que inicialmente la imagen se dibuje pero no se vea, y hacemos lo mismo con el otro
objeto Graphics pero hacia el lado opuesto .Luego vamos deshaciendo
la transformación progresivamente y volviendo a dibujar cada vez toda la imagen, la mitad con uno de 
los objetos Graphics y la otra mitad con el otro.
El efecto que se crea es de las dos mitades de la imagen que son empujadas hasta 
encontrarse en el centro.
 
   'Genera los efectos de Empuje con división
   Private Sub EmpujaDivision(ByVal Efecto As Efectos)
      'Creamos el segundo objeto Graphics para dibujar en bmCopia
      Static Gr2 As Graphics
      Static Ancho, Alto, xEsquina, yEsquina As Single 
      'Si es la primera vez que se llega aquí,
      'Establecemos las transformaciónes iniciales
      'según la variante de efecto de Empuje y División
      If EsPrimera Then
         Gr2 = Graphics.FromImage(bmCopia)
 
         If Efecto = Efectos.Empuja_División_Lados Then
            xAumentaPos = AnchoImagen / (2 * VelocidadEfecto)
            yAumentaPos = 0
            Ancho = AnchoImagen / 2 : Alto = AltoImagen
            xEsquina = Ancho : yEsquina = 0
            Gr.TranslateTransform(-Ancho, 0)
            Gr2.TranslateTransform(Ancho, 0)
         ElseIf Efecto = Efectos.Empuja_División_Topes Then
            xAumentaPos = 0
            yAumentaPos = AltoImagen / (2 * VelocidadEfecto)
            Ancho = AnchoImagen : Alto = AltoImagen / 2
            xEsquina = 0 : yEsquina = Alto
            Gr.TranslateTransform(0, -Alto)
            Gr2.TranslateTransform(0, Alto)
         End If
         EsPrimera = False
      End If
      Dim RecEmpujaLado  As New RectangleF(0, 0, Ancho, Alto)
      Dim RecEmpujaOpuesto  As New RectangleF(xEsquina, yEsquina, Ancho, Alto)
      'Limpiamos el contenido de bmCopia
      Gr.Clear(pic.BackColor)
      'Dibujamos los rectangulos y los lenamos con la imagen   
      Gr.FillRectangle(Brocha, RecEmpujaLado)
      Gr2.FillRectangle(Brocha, RecEmpujaOpuesto)
      'Movemos los ejes de coordenadas
      Gr.TranslateTransform(xAumentaPos, yAumentaPos)
      Gr2.TranslateTransform(-xAumentaPos, -yAumentaPos)
      pic.Refresh()
      'Contamos las veces que se ejecutó este evento
      Contador += 1
      'Cuando el evento se halla ejecutado VelocidadEfecto + 1 veses terminamos el efecto
      If Contador = VelocidadEfecto + 1 Then
         Gr2.Dispose()
         Ancho = 0 : Alto = 0
         xEsquina = 0 : yEsquina = 0
         DescargaObjetos()
      End If
   End Sub
Efecto de Empuje y Divición.
 
Efectos de Rueda con Multiples Ejes.
Estos efectos son similares a los de "Rueda" pero vamos a dividir la imagen en varios trozos
definidos en la enumeración "Ejes" y vamos a ir pintando con el objeto TextureBrush
progresivamente cada trozo hasta llenar la imagen completa.
Para lograrlo vamos a usar el método FillPie del Objeto Graphics el cual llena una torta
con el objeto TextureBrush especificado y con un ángulo de giro especificado.
Como una imagen obviamente no es una torta sino un rectángulo lo que vamos a hacer
es llenar con nuestro objeto TextureBrush una torta que sea más grande que el rectángulo
de la imagen (El diametro de la torta será la diagonal de la imagen) y que este centrada 
en la imagen, permitiendo dibujar toda la imagen.
 
   'Genera los efecto con multiples Ejes
   Private Sub RuedaMultiple(ByVal Ejes As Ejes)
      Dim Eje, AnguloInicio, Paso As Integer
      'Establecemos variables según la variante del efecto seleccionados
      Select Case Ejes
         Case Ejes.DosEjes
            AnguloInicio = -90 : Eje = 2 : Paso = 180
         Case Ejes.TresEjes
            AnguloInicio = -90 : Eje = 3 : Paso = 120
         Case Ejes.CuatroEjes
            AnguloInicio = 0 : Eje = 4 : Paso = 90
         Case Ejes.OchoEjes
            AnguloInicio = 0 : Eje = 8 : Paso = 45
      End Select
      'La diagonal de la imágen va a ser el diametro. 
      'La calculamos por Pitagoras.
      Dim Diagonal As Single= CSng(Math.Pow((AltoImagen ^ 2) + (AnchoImagen ^ 2), 1 / 2))
      Dim i As Integer 
      'Llenamos una torta con centro en el centro del PictureBox,
      'con la textura de la imágen original,
      'agregandole trozos de AnguloGiro
      Dim rec  As New Rectangle(CInt(-(Diagonal - AnchoImagen) / 2), CInt(-(Diagonal - AltoImagen) / 2), CInt(Diagonal), CInt(Diagonal))
      For i = 1 To Eje
         Gr.FillPie(Brocha, rec, AnguloInicio, AnguloGiro)
         AnguloInicio += Paso
      Next
      AnguloGiro += CSng(Paso / VelocidadEfecto)
      'Mostramos los trozos actuales
      pic.Refresh()
      'Contamos las veces que se ejecutó este evento
      Contador += 1
      'Cuando el evento se halla ejecutado VelocidadEfecto + 1
      'veses terminamos el efecto
      If Contador = VelocidadEfecto + 1 Then
         DescargaObjetos()
      End If
   End Sub
Efecto Rueda Multiple (8 ejes).
 
Efecto Simetrico.
Este efecto dibuja la imagen en forma de dos ruedas que parten del mismo lugar, una dibuja una
mitad de la imagen en sentido de las agujas del reloj y la otra en sentido opuesto hasta 
completar toda la imagen.
Para lograrlo vamos a usar el método FillPie del Objeto Graphics el cual llena una torta
con el objeto TextureBrush especificado y con un ángulo de giro especificado.
O sea que iremos agrandando progresivamente el ángulo de giro hasta 
dar media vuelta (180º) con cada mitad.
Como una imagen obviamente no es una torta sino un rectángulo lo que vamos a hacer
es llenar con nuestro objeto TextureBrush una torta que sea más grande que el rectángulo
de la imagen (El diametro de la torta será la diagonal de la imagen) y que este centrada 
en la imagen, permitiendo dibujar toda la imagen.
 
   'Genera el efecto de Simetría
   Private Sub Simetrico()
      'La diagonal de la imágen va a ser el diametro. 
      'La calculamos por Pitagoras.
      Dim Diagonal As Single = CSng(Math.Pow((AltoImagen ^ 2) + (AnchoImagen ^ 2), 1 / 2))
      'Llenamos una torta con centro en el centro del PictureBox,
      'con la textura de la imágen original,
      'agregandole trozos de AnguloGiro
      Dim rec As New Rectangle(CInt(-(Diagonal - AnchoImagen) / 2), CInt(-(Diagonal - AltoImagen) / 2), CInt(Diagonal), CInt(Diagonal))
      Gr.FillPie(Brocha, rec, -90, AnguloGiro)
      Gr.FillPie(Brocha, rec, -90, -AnguloGiro)
      AnguloGiro += CSng(180 / VelocidadEfecto)
      'Mostramos los trozos actuales
      pic.Refresh()
      'Contamos las veces que se ejecutó este evento
      Contador += 1
      'Cuando el evento se halla ejecutado VelocidadEfecto + 1
      'veses terminamos el efecto
      If Contador = VelocidadEfecto + 1 Then
         DescargaObjetos()
      End If
   End Sub
Efecto Simetrico.
 
   'Descarga Gr y Brocha, activa controles y detiene el Timer
   Private Sub DescargaObjetos()
      Gr.Dispose()
      Brocha.Dispose()
      Tiempo.Stop()
      btnReproducir.Enabled = True
      lbEfectos.Enabled = True
   End Sub
End Class

ir al índice

Fichero con el código de ejemplo (anibal_EfectosTrans.zip - 37.1 KB)