Entre los correos y mensajes en facebook y whatsapp que me llegan (y de entrada pido perdón a quienes no puedo responder o me olvide responder) me preguntaron como hacer un efecto flip (dar la vuelta) para un botón en java.
Bueno java cuenta con clases y métodos que nos permiten manipular imágenes, no son los mejores pero es lo que hay y con un poco de imaginación podemos hacer casi cualquier cosa. En el siguiente tutorial crearemos un componente swing que nos permita implementar el efecto flip con imágenes
Necesitamos
Tiempo 30 minutos
Nivel: Intermedio
TUTORIAL EFECTO FLIP
Paso 1. El Proyecto
File -> New project… -> Java Class Library, en nombre de proyecto ponemos JFlip y presionamos el boton finish para terminar la creación del proyecto.
A continuación creamos la estructura del proyecto como se ve a continuación:
Paso 2. Flip
Abre la clase JFlip:
package com.bolivia.flip; public class JFlip { }
La clase JFlip se extenderá de la clase JComponent, esta clase es la base de todas los componentes swing exceptuando algunos casos. Al extender de la clase JComponent tenemos la base perfecta para desarrollar nuestro swing.
Así también necesitamos controlar eventos del mouse por lo que implementaremos un MouseListener, nuestra clase queda de la siguiente forma:
import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import javax.swing.JComponent; public class JFlip extends JComponent implements MouseListener{ /**Constructor de clase*/ public JFlip(){} @Override public void mouseClicked(MouseEvent e) {/*...*/} @Override public void mousePressed(MouseEvent e) {/*...*/} @Override public void mouseReleased(MouseEvent e) {/*...*/} @Override public void mouseEntered(MouseEvent e) {/*...*/} @Override public void mouseExited(MouseEvent e) {/*...*/} }
Antes de continuar con la codificación, debemos analizar como emular en dos dimensiones el «efecto flip» siendo que en la realidad ese efecto se produce en tres dimensiones. El problema se reduce a un efecto de perspectiva es decir cuando el plano da vuelta a nuestros ojos un lado se hace más pequeño a medida que gira hasta desaparecer y reaparecer del otro lado pero esta vez aumentando de tamaño hasta alcanzar su tamaño original, en las imágenes siguientes podemos ver gráficamente a que me refiero:
Nuestro plano que es un polígono tiene 4 esquinas (A,B,C y D) , los puntos Ax, Cx, Bx y Dx tienen un desplazamiento horizontal uniforme hacia la mitad del plano, los puntos Ay y Cy son constantes, en cambio los puntos By y Dy tienen un movimiento vertical uniforme menor al desplazamiento horizontal, es menor porque no queremos que los puntos By y Dy se intercepten lo que arruinaría el efecto flip. Cuando los puntos Ax, Cx, Bx y Dx llegan hasta la mitad del plano, se debe invertir el procedimiento, es decir, los puntos Ax, Cx, Bx y Dx tienen ahora un desplazamiento negativo y el desplazamiento vertical también lo es, pero, las variables By y Dy se vuelven constantes y Ay y Cy son los que se desplazan en sentido vertical . Es decir:
Otro punto importante a considerar es que java nos permite recortar pedazos de una imagen pero esos pedazos deben ser rectangulares, por lo que no podremos hacer uso de esos métodos en este proyecto, sin embargo si podemos usar un objeto Shape para pintar secciones irregulares de una imagen usando las coordenadas del polígono descrito más arriba y así simular el efecto en perspectiva, no es lo ideal pero gracias al movimiento constante nuestros ojos no notaran la diferencia.
Continuemos con la programación
Los imports que usamos en la clase flip son:
import java.awt.BasicStroke; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.geom.GeneralPath; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JComponent; import javax.swing.Timer;
Declaramos las variables:
private Timer timer = null; private boolean inTransition = false; private int speed = 12;//velocidad de flip en milisegundos private int displacement = 2;//desplazamiento en pixeles private boolean isFront = true; private boolean inward = true;//sentido del desplazamiento //iconos por defecto private Icon iconBack = new ImageIcon(getClass().getResource("/com/bolivia/resource/photo1.jpg")); private Icon iconFront = new ImageIcon(getClass().getResource("/com/bolivia/resource/photo2.jpg")); private Image image = ((ImageIcon)iconFront).getImage(); //variables de desplazamiento private int displacementHorizontal = 0; private float displacementLeft = 0; private float displacementRight = 0; //tamaño por defecto private Dimension dimension = new Dimension(160,160);
OJO: Utilizaremos dos imágenes de 160×160 pixeles que colocaremos en el paquete /resource/
A continuación declaramos el constructor de clase, sobreescribimos el método paintComponent() y definimos el método flipAnimate() para los cambios de variable de desplazamiento
/**Constructor de clase*/ public JFlip(){ super(); setSize(dimension); setPreferredSize(dimension); setVisible(true); addMouseListener(JFlip.this); } @Override public void paintComponent(Graphics g){ Graphics2D g2 =(Graphics2D) g; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON); g2.setStroke(new BasicStroke( 0f )); //coordenas de imagen GeneralPath p = new GeneralPath(); p.moveTo(displacementHorizontal, displacementLeft);//esquina A p.lineTo(getWidth()-displacementHorizontal-1, displacementRight);//esquina B p.lineTo(getWidth()-displacementHorizontal-1, getHeight()-displacementRight-1);//esquina D p.lineTo( displacementHorizontal, getHeight()-displacementLeft-1 );//esquina C p.closePath(); Shape shp = p; g2.setClip(shp); g2.drawImage(image, 0, 0,getWidth() - displacementHorizontal,getHeight(), null); g2.setClip(null); g2.draw(shp); g.dispose(); } /** * Metodo que realiza los cambios en las variables de desplazamiento * @return Void */ private void flipAnimate(){ inTransition = !inTransition; //declaramos un evento para modificar el desplazamiento vertical y horizontal ActionListener animation = new ActionListener() { @Override public void actionPerformed(ActionEvent ae) { if(inward){ displacementHorizontal += displacement; displacementRight += (displacementRight>=getHeight()/2)?0:displacement/(getSize().getWidth()/getSize().getHeight()+1f); }else{ displacementHorizontal -= displacement; displacementLeft -=displacement/(getSize().getWidth()/getSize().getHeight()+1f); } //repinta graficos repaint(); //si se llego al medio del componente -> cambia de sentido if(displacementHorizontal >= getWidth()/2){ inward = false; displacementLeft = displacementRight; displacementRight=0; // if( isFront ){//mostrar imagen de atras isFront=false; image=((ImageIcon)iconBack).getImage(); }else{//mostrar imagen de adelante isFront =true; image=((ImageIcon)iconFront).getImage(); } } //volvio a su posicion inicial -> detener animacion if( inward == false && displacementHorizontal == 0 ){ inTransition=false; timer.stop(); displacementLeft=0; displacementRight=0; displacementHorizontal=0; inward = true; } } }; // if( inTransition ) { if(timer != null)timer.stop(); timer = new Timer( speed, animation); timer.start(); //inicia animacion } }
El efecto flip se llevara a cabo cuando el usuario de un clic sobre el swing, por lo que debemos invocar al metodo flipAnimate() en el evento mouseClicked, nuestra clase final queda de la siguiente forma:
package com.bolivia.flip; import java.awt.BasicStroke; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.geom.GeneralPath; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JComponent; import javax.swing.Timer; /** * @web https://www.jc-mouse.net/ * @author Mouse */ public class JFlip extends JComponent implements MouseListener { private Timer timer = null; private boolean inTransition = false; private int speed = 4;//velocidad de flip en milisegundos private int displacement = 2;//desplazamiento en pixeles private boolean isFront = true; private boolean inward = true;//sentido del desplazamiento //iconos por defecto private Icon iconBack = new ImageIcon(getClass().getResource("/com/bolivia/resource/photo1.jpg")); private Icon iconFront = new ImageIcon(getClass().getResource("/com/bolivia/resource/photo2.jpg")); private Image image = ((ImageIcon)iconFront).getImage(); //variables de desplazamiento private int displacementHorizontal = 0; private float displacementLeft = 0; private float displacementRight = 0; //tamaño por defecto private Dimension dimension = new Dimension(160,160); /**Constructor de clase*/ public JFlip(){ super(); setSize(dimension); setPreferredSize(dimension); setVisible(true); addMouseListener(JFlip.this); } @Override public void paintComponent(Graphics g){ Graphics2D g2 =(Graphics2D) g; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON); g2.setStroke(new BasicStroke( 0f )); //coordenas de imagen GeneralPath p = new GeneralPath(); p.moveTo(displacementHorizontal, displacementLeft);//esquina A p.lineTo(getWidth()-displacementHorizontal-1, displacementRight);//esquina B p.lineTo(getWidth()-displacementHorizontal-1, getHeight()-displacementRight-1);//esquina D p.lineTo( displacementHorizontal, getHeight()-displacementLeft-1 );//esquina C p.closePath(); Shape shp = p; g2.setClip(shp); g2.drawImage(image, 0, 0,getWidth() - displacementHorizontal,getHeight(), null); g2.setClip(null); g2.draw(shp); g.dispose(); } /** * Metodo que realiza los cambios en las variables de desplazamiento * @return Void */ private void flipAnimate(){ inTransition = !inTransition; //declaramos un evento para modificar el desplazamiento vertical y horizontal ActionListener animation = new ActionListener() { @Override public void actionPerformed(ActionEvent ae) { if(inward){ displacementHorizontal += displacement; displacementRight += (displacementRight>=getHeight()/2)?0:displacement/(getSize().getWidth()/getSize().getHeight()+1f); }else{ displacementHorizontal -= displacement; displacementLeft -=displacement/(getSize().getWidth()/getSize().getHeight()+1f); } //repinta graficos repaint(); //si se llego al medio del componente -> cambia de sentido if(displacementHorizontal >= getWidth()/2){ inward = false; displacementLeft = displacementRight; displacementRight=0; // if( isFront ){//mostrar imagen de atras isFront=false; image=((ImageIcon)iconBack).getImage(); }else{//mostrar imagen de adelante isFront =true; image=((ImageIcon)iconFront).getImage(); } } //volvio a su posicion inicial -> detener animacion if( inward == false && displacementHorizontal == 0 ){ inTransition=false; timer.stop(); displacementLeft=0; displacementRight=0; displacementHorizontal=0; inward = true; } } }; // if( inTransition ) { if(timer != null)timer.stop(); timer = new Timer( speed, animation); timer.start(); //inicia animacion } } @Override public void mouseClicked(MouseEvent e) { flipAnimate(); } @Override public void mousePressed(MouseEvent e) {/*...*/} @Override public void mouseReleased(MouseEvent e) {/*...*/} @Override public void mouseEntered(MouseEvent e) {/*...*/} @Override public void mouseExited(MouseEvent e) {/*...*/} public Icon getIconBack() { return iconBack; } public void setIconBack(Icon iconBack) { this.iconBack = iconBack; } public Icon getIconFront() { return iconFront; } public void setIconFront(Icon iconFront) { this.iconFront = iconFront; } public int getDisplacement() { return displacement; } public void setDisplacement(int displacement) { this.displacement = displacement; } public int getSpeed() { return speed; } public void setSpeed(int speed) { this.speed = speed; } }//JFlip:end
Compilamos el proyecto, para usar el componente solo lo arrastramos al jframe y también podemos modificar la velocidad de animación, la cantidad de desplazamiento en pixeles y cargar nuevas imagenes desde su menú de propiedades, ejecutamos y probamos el efecto flip.
Descargar proyecto FLIP aquí 🙂
enjoy!!!
Un ProgressDialog muestra una ventana con un texto y una barra de progreso que indica el tiempo que tarda una tarea en r[...]
Una Prueba Unitaria, es una forma de comprobar que nuestro código, hace lo que se supone debe hacer; es decir, se asegur[...]
Problema: Desarrollar un juego en consola con Kotlin que genere un numero aleatorio entre un rango preestablecido y perm[...]
Un JSON Web Token es un estandar abierto para la creación de Token de Acceso el cual permite firmar digitalmente informa[...]
Antes de la aparición de los celulares inteligentes y de las grandes mejoras tecnológicas que trajeron con ellas, las ap[...]
En este tutorial crearemos un interesante efecto de explosión al momento de abrir un JPanel, este efecto puede extenders[...]