Sople una imagen con un efecto de viento personalizado

Dos veces al mes, volvemos a visitar algunas de las publicaciones favoritas de nuestros lectores de toda la historia de Activetuts +. Este tutorial se publicó por primera vez en marzo de 2010..

En este tutorial, crearemos una clase personalizada que divide una imagen en mil pedazos y simula un viento que los sopla. Creé este proyecto únicamente con AS3 y FlashDevelop. No se requiere Flash.!


Vista previa del resultado final

Echemos un vistazo al resultado final en el que trabajaremos. Siga adelante y haga clic en cualquier lugar dentro del SWF:


Introducción rápida a FlashDevelop

FlashDevelop es un editor de código gratuito para Flash y Flex. Puede usarlo para editar sus archivos de clase cuando trabaje con el software Flash, o puede crear un Proyecto AS3 que no requiera Flash en absoluto, y eso es exactamente lo que haremos en este tutorial..

Entonces descarga FlashDevelop e instálalo. Desafortunadamente, FlashDevelop solo se ejecuta en Windows. Las alternativas de Mac incluyen FDT y Flex Builder, aunque ninguna de las dos es gratuita. Puedes usar Flash, y te explicaré cómo hacerlo a medida que avanzamos.


Paso 1: Crear un nuevo proyecto

Abra FlashDevelop y haga clic en Proyecto> Nuevo proyecto?


Paso 2: Configuración

Seleccione Actionscript 3> AS3 Project. Para el nombre del proyecto puesto en "WindEffect". Para la ubicación, haga clic y navegue hasta la carpeta en la que desea guardarlo. Deje la casilla de verificación "Crear directorio para el proyecto" seleccionada y haga clic en Aceptar.

Si desea utilizar Flash CS3 / CS4, cree un nuevo archivo Flash y configure el ancho y alto del escenario en 550x250px, establezca el color de fondo en negro. Llámalo "windEffect.fla" y guárdalo en el lugar que quieras.


Paso 3: Mueve la imagen de origen

Para FlashDevelop, abra el directorio del proyecto y copie o arrastre windEffect.jpg desde la descarga de origen (vinculado en la parte superior de la página) a la carpeta \ bin \.

Para Flash, copie o arrastre windEffect.jpg desde la descarga de origen a la misma carpeta donde tiene windEffect.fla.


Paso 4: Instale TweenLite

Vamos a utilizar TweenLite by Greensock para la interpolación. Puede descargar la última versión del componente aquí; También lo he incluido en la fuente de descarga..

Para FlashDevelop, siga adelante y copie o arrastre greensock.swc de la descarga de origen a la carpeta \ lib \ para este proyecto.

Desde FlashDevelop, haga clic en Ver> Administrador de proyectos


Paso 5: Biblioteca externa

Aún en FlashDevelop, haga clic en el signo '+' a la izquierda de la carpeta lib para expandirla. Haga clic con el botón derecho en greensock.swc y seleccione Agregar a la biblioteca.

Para Flash, copie o arrastre la carpeta \ com \ de la descarga de origen a la misma carpeta que su archivo windEffect.fla.


Paso 6: La clase de documentos

Para FlashDevelop, abra el administrador de proyectos nuevamente (consulte el Paso 4), expanda la carpeta \ src \ y haga doble clic en Main.as. Debajo de las importaciones y justo encima de la definición de clase, agregue la siguiente etiqueta de metadatos para configurar las propiedades de la etapa:

[SWF (ancho = 550, altura = 250, frameRate = 30, backgroundColor = 0)]

Dentro del método init () después del 'punto de entrada' del comentario, agregue el siguiente código:

 stage.scaleMode = StageScaleMode.NO_SCALE; // no estirar el efecto var de escenario: WindEffect = new WindEffect ('windEffect.jpg'); // crearemos la clase WindEffect pronto addChild (effect);

Eso es todo para la clase de documento principal.

Para Flash, cree una nueva clase Main.as en la misma carpeta que su proyecto. Asegúrese de que la clase Main.as esté en la misma carpeta que la fla. & com carpeta. Añade las siguientes líneas:

paquete import flash.display.Sprite; importar flash.display.StageScaleMode; import flash.events.Event; Clase pública Main extiende Sprite función pública Main (): void if (stage) init (); else addEventListener (Event.ADDED_TO_STAGE, init);  función privada init (e: Event = null): void removeEventListener (Event.ADDED_TO_STAGE, init); stage.scaleMode = StageScaleMode.NO_SCALE; // no estirar el efecto var de escenario: WindEffect = new WindEffect ('windEffect.jpg'); // crearemos la clase WindEffect pronto addChild (effect); 

Abra Flash y asigne "Main" como clase de documento.

(¿No está seguro de qué se trata todo esto? Lea esta introducción rápida sobre el uso de una clase de documento).

Si intenta ejecutar esto ahora, recibirá un error ya que aún no hemos creado la clase WindEffect. Solo asegúrate de guardar el archivo y dejarlo por ahora..


Paso 7: Crea la clase WindEffect

Para FlashDevelop, haga clic en Ver> Administrador de proyectos, haga clic con el botón derecho en la carpeta \ src \ y elija Agregar> Nueva clase.


Paso 8: Configuración de la clase

Asigne un nombre a la clase WindEffect, haga clic en el botón de búsqueda de la clase base y escriba flash.display.Sprite. Presiona OK para completar.


Paso 9: Importar otras clases

Agregue todas las importaciones necesarias dentro de los paréntesis del paquete justo debajo de 'import flash.display.Sprite;' y antes de la definición de la clase. Clic en Guardar.

import com.greensock.easing.Strong; import com.greensock.TweenLite; importar flash.display.Bitmap; importar flash.display.BitmapData; importar flash.display.Loader; import flash.events.Event; import flash.events.MouseEvent; importar flash.geom.Point; import flash.geom.Rectangle; importar flash.net.URLRequest;

Para Flash, cree un nuevo archivo ActionScript, asígnele el nombre "WindEffect.as" y guárdelo en el mismo directorio que ha estado usando. Debe estar justo al lado de la fla. archivo, carpeta com, y main.as.

Agregue el siguiente código:

paquete import com.greensock.easing.Strong; import com.greensock.TweenLite; importar flash.display.Bitmap; importar flash.display.BitmapData; importar flash.display.Loader; importar flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; importar flash.geom.Point; import flash.geom.Rectangle; importar flash.net.URLRequest; clase pública WindEffect extiende Sprite función pública WindEffect () 

Paso 10: Agregar una variable de instancia

Agrega una variable privada llamada "_pictureArray". Esta es la única variable que tendremos en esta clase. Su objetivo principal es mantener referencias a todos los pequeños sprites que contienen las pequeñas piezas de la imagen una vez que se ha roto.

Agregue la siguiente línea de código entre los paréntesis
de la clase:

clase pública WindEffect extiende Sprite // esto albergará todas las piezas de la imagen que animaremos. private var _pictureArray: Array; 

Paso 11: Añadir el constructor

Agregue las siguientes líneas después de la declaración _pictureArray:

clase pública WindEffect extiende Sprite // esto albergará todas las piezas de la imagen que animaremos. private var _pictureArray: Array; función pública WindEffect ($ url: String) // solo llamamos a la imagen de carga en el constructor loadPicture ($ url); 

Paso 12: Accede a la imagen

Dentro del método loadPicture () llamado por el método constructor, creamos una instancia para cargar el windEffect.jpg. También agregamos un detector de eventos COMPLETO para escuchar cuando se completa la carga.

Agregue las siguientes líneas de código después del método WindEffect (). (Tenga en cuenta que el parámetro "$ url" es la ruta de acceso a la imagen que estamos cargando pasada desde Main.as.)

función privada loadPicture ($ url: String): void // creamos un cargador con escuchas para cargar la imagen de origen que estamos usando. // y luego cargamos la imagen. var loader: Loader = new Loader; loader.contentLoaderInfo.addEventListener (Event.COMPLETE, onLoadComplete); // cuando esté cargado, llame a la función onLoadComplete () loader.load (nuevo URLRequest ($ url)); 

Paso 13: Cargando

Una vez que la imagen se ha importado correctamente, se llama a este método. Agregue las siguientes líneas de código después del método loadPicture () y guarde el archivo.

función privada onLoadComplete (e: Evento): void // para probar addChild (e.target.content); 

Paso 14: Prueba Uno

Continúa y presiona CTRL + Enter en tu teclado. Debería funcionar y la imagen debería estar en la esquina superior izquierda del escenario..

Ahora que hemos comprobado que se está cargando correctamente, elimine el método addChild y sustitúyalo por el siguiente código:

createEffect (e.target.content);

Tu clase de WindEffect debería verse algo como esto:

paquete import com.greensock.easing.Strong; import com.greensock.TweenLite; importar flash.display.Bitmap; importar flash.display.BitmapData; importar flash.display.Loader; importar flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; importar flash.geom.Point; import flash.geom.Rectangle; importar flash.net.URLRequest; [SWF (ancho = 550, altura = 250, frameRate = 30, backgroundColor = 0)] clase pública WindEffect extiende Sprite private var _pictureArray: Array; función pública WindEffect ($ url: String) loadPicture ($ url); 
 función privada loadPicture ($ url: String): void var loader: Loader = new Loader; loader.contentLoaderInfo.addEventListener (Event.COMPLETE, onLoadComplete); loader.load (nueva URLRequest ($ url));  función privada onLoadComplete (e: Evento): void createEffect (e.target.content); 

Paso 15: Configurar las variables

El método createEffect () tomará el parámetro de imagen que es esencialmente un mapa de bits y lo dividirá en 1250 piezas.

Primero calculamos las posiciones x e y para centrar la imagen en el escenario. Los guardamos en variables locales llamadas centerWidth y centerHeight.

Dado que el tamaño de la imagen que estamos usando es de 300x100, decidí dividir la imagen 50 veces horizontalmente y 25 veces verticalmente. Estos valores dieron un resultado bastante decente con un rendimiento óptimo. Los guardamos en variables locales, que denominé "numberOfColumns" y "numberOfRows".

Guardamos el resultado de dividir el ancho de la imagen por numberOfColumns en "sizeWidth" y el resultado de dividir la altura de la imagen por numberOfRows en "sizeHeight".

La variable "numberOfBoxes" contiene numberOfColumns multiplicado por numberOfRows.

A continuación, creamos una instancia de _pictureArray para que podamos comenzar a poner pequeños sprites en ella. Agregue las siguientes líneas de código después del método onLoadComplete ():

Función privada createEffect ($ bitmap: Bitmap): void // centrar la imagen horizontalmente. var centerWidth: Number = (stage.stageWidth - $ bitmap.width) * .5; // Centrar la imagen verticalmente. var centerHeight: Number = (stage.stageHeight - $ bitmap.height) * .5; var numberOfColumns: uint = 50; var numberOfRows: uint = 25; var sizeWidth: uint = $ bitmap.width / numberOfColumns; var sizeHeight: uint = $ bitmap.height / numberOfRows; var numberOfBoxes: uint = numberOfColumns * numberOfRows; _pictureArray = []; 

Paso 16: Bucles anidados

Después de crear una instancia de _pictureArray, agregaremos dos bucles, uno dentro del otro. El primer bucle manejará el movimiento en la posición x y recorrerá todas las columnas, mientras que el segundo bucle se moverá en la posición y recorrerá todas las filas.

Agregue las siguientes líneas de código dentro del método createEffect () justo después de crear una instancia de _pictureArray, luego guarde el archivo:

para (var i: uint = 0; i < numberOfColumns; i++)  //these loops are what splits the image into 1250 pieces. for (var j:uint = 0; j < numberOfRows; j++)  //let's see what it does. trace ('i:' + i, 'j:' + j);  

Paso 17: Prueba Dos

Prueba la película presionando CTRL + Enter.

Como puedes ver, por cada yo hay un bucle completo de j. Esto se llama "bucles de anidamiento". Esto significa que yo que representa el eje x permanece en un valor mientras que el segundo bucle se repite para el eje y.

En pocas palabras, comenzamos con x = 0, y = 0; entonces la siguiente iteración es x = 0, y = 1; entonces x = 0, y = 2, y así sucesivamente.

Cuando y llega al final, el primer bucle se incrementa en 1 y luego nuevamente pasa por el segundo bucle: x = 1, y = 0; x = 1, y = 1, x = 1, y = 2, etc. Esto continúa hasta que se completa el primer ciclo.

Verá qué hace esto cuando lo apliquemos a una manipulación de mapas de bits en las siguientes líneas.


Paso 18: Dividiendo la imagen

Desde el segundo bucle, siga adelante y elimine la función de rastreo que utilizamos para las pruebas. Cada vez que hacemos un bucle necesitamos crear una imagen pequeña que tenga el ancho de "sizeWidth" y la altura de "sizeHeight".

Esta pequeña imagen tomará una instantánea de una pequeña parte de la imagen comenzando desde la esquina superior izquierda y moviéndose hacia la parte inferior derecha. El "tempBitmapData" es donde dibujaremos la pequeña parte de la imagen. El "sourceRect" es el rectángulo que usaremos para especificar qué parte de la imagen se copiará.

Agregue las siguientes líneas dentro del segundo ciclo y guarde el archivo:

// 1 bitmapdata temporal var tempBitmapData: BitmapData = new BitmapData (sizeWidth, sizeHeight); // 1 rectángulo temporal (x, y, ancho, alto) // pasamos i * sizeWidth para el parámetro x & i * sizeHeight para el parámetro y // y el tamañoWidth y sizeHeight para los parámetros de ancho y alto. var sourceRect: Rectangle = new Rectangle (i * sizeWidth, j * sizeHeight, sizeWidth, sizeHeight); trace (sourceRect); // para prueba

Paso 19: Aún más pruebas

Pon a prueba la película. Lo que hace ahora es crear un rectángulo que ajusta sus posiciones x e y en cada iteración..

Como puede ver, el primer ejemplo muestra x = 0, y = 0 y el siguiente es x = 0, y = 4. Esto es lo que usamos para los límites de la instantánea tomada de la imagen de origen. Elimine la función de prueba cuando esté listo para continuar.


Paso 20: BitmapData.copyPixels ()

Luego usamos el método BitmapData.copyPixels () para copiar una pequeña parte de la imagen basada en el sourceRect. Los parámetros para este método son la imagen de mapa de bits para copiar, el área del rectángulo para copiar y el punto de destino donde lo copiaremos..

Agregue la siguiente línea de código debajo de la declaración de sourceRect.

tempBitmapData.copyPixels ($ bitmap.bitmapData, sourceRect, nuevo punto);

Luego creamos un Bitmap temporal para alojar el BitmapData que acabamos de copiar y un Sprite temporal para alojar ese Bitmap..

Luego empujamos una referencia de cada Sprite en _pictureArray para acceder más tarde. Después de esto, agregamos el Sprite al escenario con la misma coordenada desde donde lo copiamos, recreando así la imagen original..

A continuación, desplazamos la imagen por centerWidth y centerHeight para centrarla correctamente en el escenario..

Agregue las siguientes líneas de código y, una vez más, guarde el archivo:

// luego creamos 1 mapa de bits temporal para alojar los datos de mapas de bits que acabamos de copiar. var tempBitmap: Bitmap = nuevo Bitmap (tempBitmapData); // y 1 sprite temporal para alojar el mapa de bits para habilitar la interactividad. var tempSprite: Sprite = nuevo Sprite; // solo agregamos cada cuadro dentro de su propio sprite para habilitar la interactividad ya que los mapas de bits por sí mismos no son interactivos. tempSprite.addChild (tempBitmap); // cada sprite se agrega a la matriz _pictureArray para acceder más tarde. _pictureArray.push (tempSprite); // luego posiciona cada uno de ellos en el escenario. // Añadimos el ancho del centro y la altura del centro para que la imagen se centre en el escenario. tempSprite.x = i * sizeWidth + centerWidth; tempSprite.y = j * sizeHeight + centerHeight; addChild (tempSprite);

Paso 21: Prueba Tres

Sigue adelante y prueba de nuevo. Deberías ver la imagen correctamente colocada en el escenario. Ni siquiera se verá como si estuviera separado en 1250 piezas..

Justo después del corchete de cierre del segundo bucle, antes de cerrar el método, agregue la siguiente línea de código:

stage.addEventListener (MouseEvent.CLICK, blowWind);

Agregamos un detector de eventos al escenario para escuchar un MouseEvent.CLICK. Esto activará la animación ejecutando la función blowWind (), que crearemos en el siguiente paso..

Tu clase de WindEffect debería verse algo como esto:

paquete import com.greensock.easing.Strong; import com.greensock.TweenLite; importar flash.display.Bitmap; importar flash.display.BitmapData; importar flash.display.Loader; importar flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; importar flash.geom.Point; import flash.geom.Rectangle; importar flash.net.URLRequest; la clase pública WindEffect extiende Sprite private var _pictureArray: Array; función pública WindEffect ($ url: String) loadPicture ($ url);  función privada loadPicture ($ url: String): void var loader: Loader = new Loader; loader.contentLoaderInfo.addEventListener (Event.COMPLETE, onLoadComplete); loader.load (nueva URLRequest ($ url));  función privada onLoadComplete (e: Evento): void createEffect (e.target.content);  La función privada createEffect ($ bitmap: Bitmap): void var centerWidth: Number = (stage.stageWidth - $ bitmap.width) * .5; var centerHeight: Number = (stage.stageHeight - $ bitmap.height) * .5; var numberOfColumns: uint = 50; var numberOfRows: uint = 25; var sizeWidth: uint = $ bitmap.width / numberOfColumns; var sizeHeight: uint = $ bitmap.height / numberOfRows; var numberOfBoxes: uint = numberOfColumns * numberOfRows; _pictureArray = []; para (var i: uint = 0; i < numberOfColumns; i++)  for (var j:uint = 0; j < numberOfRows; j++)  var tempBitmapData:BitmapData = new BitmapData (sizeWidth, sizeHeight); var sourceRect:Rectangle = new Rectangle (i * sizeWidth, j * sizeHeight, sizeWidth, sizeHeight); tempBitmapData.copyPixels ($bitmap.bitmapData, sourceRect, new Point); var tempBitmap:Bitmap = new Bitmap (tempBitmapData); var tempSprite:Sprite = new Sprite; tempSprite.addChild (tempBitmap); _pictureArray.push (tempSprite); tempSprite.x = i * sizeWidth + centerWidth; tempSprite.y = j * sizeHeight + centerHeight; addChild (tempSprite);   stage.addEventListener (MouseEvent.CLICK, blowWind);   

Paso 22: Creando el efecto del viento

Comience por eliminar el detector de eventos MouseEvent.CLICK ya que necesitamos que suceda solo una vez. Agregue las siguientes líneas de código después del método createEffect ():

función privada blowWind (e: MouseEvent): void stage.removeEventListener (MouseEvent.CLICK, blowWind); 

Necesitamos revisar todos los sprites que asignamos en _pictureArray y animarlos individualmente.

TweenLite se aplica para animar todas las piezas hacia la derecha como si el viento soplara en ellas..

Los parámetros son: el objetivo de la interpolación, la duración de la interpolación, un objeto variable que contiene todas las propiedades, junto con los valores a los que desea aplicar la interpolación..

Por ejemplo: TweenLite.to (objetivo, duración, x: 100, y: 100, rotación: 30, facilidad: Strong.easeIn, onComplete: trace, onCompleteParams: ['hello']).

Los últimos dos parámetros del ejemplo anterior se utilizan para cuando finaliza la interpolación. El parámetro onComplete llama a la función de rastreo y el parámetro onCompleteParams envía una matriz que contiene la cadena 'hello' a la función de rastreo.

Agregue las siguientes líneas de código justo después de eliminar el detector de eventos:

para (var i: uint = 0; i < _pictureArray.length; i++)  TweenLite.to ( _pictureArray[i], getRandomInRange (.25, 2, false),  x: stage.stageWidth + 100, y:_pictureArray[i].y + getRandomInRange (-100, 100, false),// rotation: getRandomInRange (-90, 90), ease:Strong.easeIn, onComplete:removeSprite, onCompleteParams:[_pictureArray[i]]  ); 

En la implementación real, cuando llamamos a TweenLite desde dentro del bucle, asignamos el objetivo como _pictureArray [iteración actual].

Para la duración, asignamos un valor para la duración de la interpolación a un tiempo aleatorio entre .25 segundos y 2 segundos..

El objeto variable tiene 5 propiedades:

  • x: stage.stageWidth + 100 que animará la propiedad x del sprite.
  • y: _pictureArray [i] .y + getRandomRange (-100,100, falso) que obtendrá la posición y del sprite actual y agregará un número aleatorio entre -100 y 100 para dar a la animación un efecto de expansión..
  • rotación: getRandomRange (-90,90) rota el sprite actual a cualquier lugar entre -90 y 90 grados.
  • facilidad: fuerte. lo que hace que la interpolación comience lentamente y de repente acelere.
  • onComplete: removeSprite que llama al método removeSprite una vez que la interpolación ha terminado y el Sprite está fuera de la pantalla.
  • onCompleteParams que envía la matriz [_pictureArray [iteración actual]] como el parámetro para removeSprite.

Paso 23: método removeSprite ()

Este método se llama desde TweenLite cuando la animación para una interpolación particular ha terminado. Simplemente eliminamos el Sprite de la lista de visualización para que no haya desorden. Agregue las siguientes líneas de código después del método blowWind ():

función privada removeSprite ($ sprite: Sprite): void removeChild ($ sprite); 

Paso 24: Método getRandomInRange ()

Estoy seguro de que está familiarizado con este (de lo contrario, Carlos Yanez ha escrito un Consejo rápido sobre el tema). Mi versión tiene la opción de devolver números enteros (int, uint) o flotadores (fracciones).

Agregue las siguientes líneas de código. Si está utilizando FlashDevelop, puede guardarlo como un fragmento personalizado para que se pueda agregar fácilmente a cualquier clase / proyecto. Lo he declarado como un método estático público para la accesibilidad total.

función estática pública getRandomInRange ($ min: Number, $ max: Number, $ rounded: Boolean = true): Number if ($ rounded) return Math.round (Math.random () * ($ max - $ min) + $ min); si no, devuelve Math.random () * ($ max - $ min) + $ min; 

¡Eso es! Ejecutar la película. Si hay algo incorrecto, compare su código con la clase WindEffect que he incluido en la descarga de origen.


Conclusión

La clave para crear efectos geniales es aprender y dominar tanto la manipulación de imágenes como las clases de interpolación de animación como TweenLite. Por favor, siéntase libre de dejar una nota para cualquier comentario, inquietud o sugerencia. Gracias por leer!