Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. ActionScript

Recrear el efecto de flujo de cobertura con Flash y AS3

by
Read Time:94 minsLanguages:

Spanish (Español) translation by Luis Chiabrera (you can also view the original English article)

Sin dudas, has visto la vista "Flujo de cobertura" en efecto. Está por todas partes en las cosas de Apple. Y probablemente también haya visto una serie de implementaciones de Cover Flow en Flash. En este tutorial, obtendrá uno más. Aprovecharemos las capacidades 3D integradas de Flash Player 10 (anterior a Stage3D) y crearemos nuestra propia versión de Cover Flow basada en XML.

Nota: Este tutorial se publicó originalmente en abril de 2011, antes de que se lanzara Stage3D, como regalo de promoción para los suscriptores de nuestro boletín. Dado que Activetuts + ahora se ha cerrado, lo hacemos libre para todos los lectores.

Vista previa del resultado final

Mira la demostración para ver para qué estamos trabajando.



Haga clic para ver la demostración.


Paso 1: crea un archivo Flash ActionScript 3

Lo primero que debemos hacer es crear un archivo Flash en el que trabajemos. Abra Flash CS4 o CS5 y elija Archivo> Nuevo y seleccione Archivo Flash (ActionScript 3.0) y presione OK. Guarde este archivo en una carpeta que estará dedicada a este proyecto.

Flash version settingsFlash version settingsFlash version settings

Estableceré el tamaño del escenario a 600 x 400. Siéntase libre de usar el tamaño que desee, pero recomendaría 600 x 400 como mínimo, teniendo en cuenta que el efecto Coverflow es mejor cuando tiene suficiente espacio para mostrarse. Además, elija una velocidad de fotogramas bastante alta, como la predeterminada 24. Esto hará que las animaciones sean más suaves.

Stage size and frame rateStage size and frame rateStage size and frame rate

Guarde este archivo como CoverFlow.fla en una carpeta que pueda dedicar a este proyecto.


Paso 2: crea la clase de documento de prueba

Nuestro objetivo será crear una clase CoverFlow reutilizable, pero para desarrollarla necesitamos un lugar donde vivir. Utilizaremos el archivo Flash que acabamos de crear para que sirva de campo de pruebas para la clase CoverFlow a medida que lo desarrollemos, por lo que necesitaremos una clase de documento para ir con el archivo Flash. Esta clase funcionará para proporcionar un lugar para instanciar y probar la clase CoverFlow. Como tal, proporcionará un ejemplo de cómo usar la clase CoverFlow una vez que esté completa.

En el editor de texto de su elección, cree un nuevo archivo y guárdelo como CoverFlowTest.as en la misma carpeta que su archivo Flash.

Your project folder

Apagar una clase simple:

Esto no hace mucho, pero rastreará un mensaje si está conectado correctamente, lo cual haremos a continuación.


Paso 3: conecte la clase de documento a la FLA

En el archivo Flash, haga clic en algún lugar en el escenario vacío y abra el panel Propiedades (Comando / Control-F3, o Ventana> Propiedades). En el campo Clase, escriba CoverFlowTest para asignar la clase del documento.

Pruebe su película ahora, y debería ver el rastro antes mencionado.

The Output Panel when running this FLAThe Output Panel when running this FLAThe Output Panel when running this FLA

Paso 4: Crea la clase CoverFlow

A continuación crearemos el archivo para la clase CoverFlow. Esto vivirá en un paquete, así que primero crea la estructura de la carpeta. Comenzando en la carpeta del proyecto (al mismo nivel que su FLA), cree una carpeta llamada com. Dentro de eso, crea otra carpeta llamada tutsplus. Dentro de esto, crea una carpeta más llamada Coverflow.

Ahora crea otro archivo de texto llamado CoverFlow.as dentro de la carpeta coverflow.

The project folder with the CoverFlow classThe project folder with the CoverFlow classThe project folder with the CoverFlow class

Agregue la siguiente plantilla repetitiva:

Esto es muy similar a la clase de documento, solo anticipamos la necesidad de más clases, por lo que hay más instrucciones de importación. Esta clase también extenderá Sprite, para que podamos tratar CoverFlow como un objeto de visualización.


Paso 5: crea una instancia de CoverFlow

Ahora, para asegurarnos de que podemos crear y trabajar con CoverFlow en nuestra clase de documento. De vuelta en CoverFlowTest.as, importe la clase CoverFlow. Después de las otras importaciones:

Ahora necesitamos una propiedad para almacenar la instancia de CoverFlow. Antes del constructor:

Y ahora para crear la instancia y agregarla a la lista de visualización. En el constructor, elimine la traza y reemplácela por:

Pruébelo ahora, y ahora debería ver el trazo "CoverFlow" en el panel de Salida. Si es así, todo está bien. Ahora podemos comenzar a construir CoverFlow.

Aquí está el código de clase del documento completo, para referencia:


Paso 6: crea una clase Cover

Una clase Cover será un elemento único en todo el "flujo". Comenzaremos por perfeccionar la clase en un solo elemento, luego nos preocuparemos de cargar los datos y generar el "flujo" una vez que se haya solucionado.

De nuevo, en su editor de texto, cree un nuevo archivo de clase. Guárdelo como Cover.as en la misma carpeta com / tutsplus / coverflow que la clase CoverFlow (a diferencia de la clase CoverFlowTest ... sí, tenemos algunos nombres potencialmente confusos. Trataré de ayudarte a mantener las cosas en orden a lo largo de este tutorial).

The project folder structureThe project folder structureThe project folder structure

Agregue el siguiente texto repetitivo (ves un patrón?):

Esto es en realidad idéntico a la repetición de CoverFlow, excepto por el uso de la palabra "Cover" en lugar de "CoverFlow". Esto terminará siendo bastante diferente, no te preocupes, solo me estoy asegurando de tener los archivos en su lugar antes de ir demasiado lejos en el agujero de conejo de codificación.


Paso 7: crea una instancia de portada

Entonces, haremos que el objeto CoverFlow cree y use un objeto Cover. Al final, CoverFlow será responsable de crear y administrar varios objetos de portada, pero por ahora, a medida que construimos la clase Cover, haremos que cree y muestre un solo objeto Cover.

En CoverFlow, en lugar de rastrear "CoverFlow", hagamos que haga esta creación de objetos.

Si lo prueba ahora, debería ver que se trazó "Cover" (recuerde, ese es un único objeto de portada, no los rastros de prueba previos que hemos estado usando).

Tracing "Cover"Tracing "Cover"Tracing "Cover"

Sin embargo, demos un paso más y tratamos de mostrar algo en la pantalla. En el archivo Cover, elimine la traza y agregue esto:

Ahora, en lugar de buscar en el panel de Salida, debería ver aparecer un cuadrado rojo en la esquina superior izquierda de su película. Si eso sucede, estamos listos para seguir trabajando. Lo que esto significa es que no solo el código se está ejecutando hasta llegar al objeto de portada individual, sino que también hemos agregado elementos al escenario para que podamos crear objetos visuales.

Actually seeing something in the movieActually seeing something in the movieActually seeing something in the movie

Paso 8: poner cubiertas en un contenedor

Acabamos de agregar nuestro objeto Cover directamente a la lista de visualización de CoverFlow. Por razones que pueden no estar claras en este momento, eventualmente las necesitaremos en un contenedor adicional; es decir, CoverFlow contendrá un Sprite, que contendrá todas las instancias Cover. A medida que avanzamos en este tutorial, agregaremos otros objetos de visualización que no sean Cover, y será muy conveniente asegurarse de que todas las Covers se administren fácilmente.

Primero, en CoverFlow, declare una propiedad de Sprite que hará referencia a nuestro contenedor.

Y en el constructor, cree ese Sprite y agréguelo a la lista de visualización. Además, en lugar de agregar el objeto Cover de prueba a la lista de visualización de CoverFlow, agréguelo al _coversContainer Sprite:

Si prueba ahora, debería ver exactamente lo mismo que antes, lo cual es bueno. No queremos que se vea diferente, pero queremos una funcionalidad diferente bajo el capó.


Paso 9: establece el tamaño de CoverFlow

Hay algunas cosas de las que debemos ocuparnos ahora. El tamaño del contenedor que contiene la pantalla CoverFlow es uno de ellos, ya que el ancho y la altura se usarán en cualquier otro lugar. Esto será tan simple como unas pocas propiedades y setters y getters coincidentes.

Primero, en CoverFlow.as, agregue dos propiedades al comienzo de la clase:

Y después del constructor, agregue los siguientes setters y getters:

Como CoverFlow es una subclase de Sprite, ya hay definidores de ancho y alto y getters. Entonces, debemos asegurarnos de anularlos. No queremos el comportamiento predeterminado de estiramiento, por lo que querremos controlar eso por nuestra cuenta. Volveremos a estos setters en unos pocos pasos.

Sin embargo, tener un tamaño para el objeto CoverFlow es lo suficientemente importante como para requerir estos parámetros en el constructor. El ancho y la altura determinan gran parte del diseño final, por lo que agregaremos algunos parámetros al constructor y luego estableceremos de inmediato nuestras propiedades con ellos:

Y, por supuesto, necesitamos proporcionar algunos argumentos a esto de CoverFlowTest. En ese archivo, actualice la línea que crea un nuevo CoverFlow a esto:

No hay mucho que probar ahora, pero si lo desea, puede publicar la película y ver si aparece algún error de compilación. Si hay algún error, cuídalos ahora. Sabrá que los errores se relacionan de alguna manera con el código que acaba de tipear, así que comience allí.


Paso 10: establece el color de fondo

Otra propiedad que se usará más tarde será el color de fondo de todo el "flujo". Apple hace el suyo negro, pero no hay razón para quedarse con eso, ya que será bastante simple cambiarlo. Para que esto suceda, sin embargo, necesitaremos un objeto Shape que se encuentra en la parte inferior de la pila de visualización del objeto CoverFlow, y tendremos que dibujar un rectángulo del color elegido en esa forma.

Primero, agregue dos propiedades a CoverFlow, una para contener la instancia de Shape y otra para almacenar el color de fondo:

Tenga en cuenta que le damos a _backgroundColor un valor predeterminado, de modo que si nunca lo establece el usuario de esta clase, de todos modos tendremos un fondo negro. El número 0 es el código de color para negro ...

A continuación, escriba en setter y getter para backgroundColor (no necesitamos que Shape sea accesible fuera de esta clase, solo el color):

Notarás que estamos llamando a un método todavía inexistente llamado drawBackground. Esto hará lo que dice en la etiqueta. Vamos a escribirlo ahora:

Esto simplemente borra cualquier dibujo gráfico anterior en la forma de fondo, establece el color de relleno al valor actual de la propiedad y luego dibuja un rectángulo.

Finalmente, tenemos que configurar la forma inicialmente. En el constructor, agregue esto al final:

¡Y adelante, pruébalo! Debería ver un fondo negro detrás de nuestro cuadrado rojo.

The published SWF, with a black backgroundThe published SWF, with a black backgroundThe published SWF, with a black background

Si lo desea, puede cambiar el tamaño de la ventana del reproductor, y verá que efectivamente tenemos un rectángulo negro sobre la base blanca de la película.

Como referencia, aquí está la clase completa de CoverFlow en este punto. Los cambios realizados en este paso están resaltados.


Paso 11: Crea la máscara

Es posible que hayamos creado una forma de fondo que responda al tamaño de CoverFlow, pero cualquier otra cosa que agreguemos al objeto, como Covers individuales, puede no respetar el tamaño deseado. Lo que necesitamos es una máscara para todo el objeto de visualización CoverFlow que se establece en el mismo tamaño.

Podríamos usar una vieja máscara normal para esta tarea, pero debido a que estamos esperando una máscara rectangular, tenemos un enfoque aún más fácil. La propiedad scrollRect de DisplayObjects proporciona una funcionalidad similar a la de las máscaras, aunque existen diferencias. Una de las ventajas que tenemos con scrollRect es una optimización del rendimiento. No sé detalles, pero al utilizar scrollRect le dice a Flash que represente solo los píxeles contenidos dentro del rectángulo, a diferencia de las máscaras regulares, que aún representan todos los píxeles involucrados en el contenido enmascarado.

Configurarlo es tan simple como esto, en el constructor de CoverFlow:

No hay mucho que probar ahora, pero puedes compilar para asegurarte de que no cometiste ningún error tipográfico.


Paso 12: Ajuste el ancho y la altura

Ahora, necesitamos implementar nuestra propia lógica de tamaño. En los ajustadores de widthy height de CoverFlow, agregue estas líneas:

Podemos probar esto hasta cierto punto añadiendo un cambio de tamaño al objeto coverFlow en CoverFlowTest:

Deberías ver lo que has estado viendo, solo enmascarado.

The size-adjusted CoverFlow. The scrollRect is in effect.The size-adjusted CoverFlow. The scrollRect is in effect.The size-adjusted CoverFlow. The scrollRect is in effect.

Es difícil decir que scrollRect está funcionando en este momento, pero al menos espera resultados por ahora. Elimina las dos líneas que acabas de agregar; querremos un CoverFlow de tamaño completo para continuar con el desarrollo.


Paso 13: agregue propiedades para cubrir

Volveremos a CoverFlow.as en un futuro cercano, pero por ahora nos enfocaremos en hacer que una instancia individual de Cover sea más rápida.

Pensemos en lo que la Cover deberá hacer. Tendrá que cargar y mostrar una imagen. Tendrá que mostrar un título. Necesitará ser posicionado. También deberá mostrar un reflejo debajo de la imagen. Cuando lleguemos a los datos XML, haremos que cada tienda de portada sea el nodo XML relacionado con esa instancia, de modo que podamos almacenar información adicional relacionada con la Cover asociada. Y deberá enviar algunos eventos, para el progreso de la carga, la carga completa, seleccionar (para cuando la cubierta llegue a la posición central) y hacer clic. La Cover sin duda podría hacer más, pero por ahora, estas capacidades coinciden bastante con lo que iTunes implementa con Cover Flow, y ayudarán a mantener nuestro tutorial a un alcance razonable. Para el código, este conjunto de características significa:

Cargar imagen Necesitaremos un cargador para cargar la imagen, junto con la especificación de la URL de la imagen para cargar.

Mostrar imagen Tendremos que agregar el cargador a la lista de visualización.

Mostrar título Tendremos que poder establecer el título, colocarlo en un campo de texto y mostrar el campo de texto. Tenga en cuenta que esto abre la lata de gusanos que es el estilo de texto, y la cuestión de cuánto control del estilo para abrir fuera de la clase. Para nuestros propósitos, nos quedaremos con un estilo estándar. Si desea una implementación que permita estilos definibles por el usuario, es un ejercicio para más adelante.

Sin embargo, tenga en cuenta que en la implementación de referencia de Cover Flow, el título no está adjunto a la imagen de portada, es un área fija en el centro de la parte inferior de toda el área de visualización. Lo que necesitaremos no es un TextField para cada objeto Cover, sino un solo TextField para todo el sistema CoverFlow. Todo lo que el objeto Cover necesita hacer es almacenar su título. Tendremos CoverFlow y luego sacaremos esa información de cada portada a medida que esté enfocada y manejemos su pantalla. Entonces, todo lo que realmente necesitamos en este momento es un mecanismo de almacenamiento para un título.

Mostrar reflejo de imagen Esto requerirá un objeto de mapa de bits y cierta sofisticación de BitmapData, pero no es difícil. Sin embargo, requiere que conozcamos el color de fondo general de toda la pantalla de Cover Flow, porque la manera más fácil de manejar la transparencia de los reflejos es no ser transparente en realidad (si lo fueran, tendríamos reflejos superpuestos a través de cada otro). Por lo tanto, solicitaremos que el color de fondo se transfiera a nuestro constructor desde CoverFlow.

Además, querremos colgar en el mapa de bits, pero BitmapData puede ser un objeto de una sola vez usado para crear el reflejo en primer lugar.

Posicionamiento Dado que estamos subclasificando Sprite, todo lo que mostramos dentro de ese Sprite se posiciona automáticamente como una unidad por las propiedades de posición de Sprite. No tendremos que hacer nada para obtener esta funcionalidad, salvo asegurarnos de agregar los objetos de visualización apropiados como elementos secundarios de la instancia de Cover.

Datos XML Solo necesitaremos una propiedad para almacenar un nodo XML arbitrario y una manera de volver a salir del objeto.

Eventos De nuevo, dado que somos una subclase de Sprite, también tenemos automáticamente la capacidad de enviar eventos. De hecho, el evento click ya está definido por el Sprite. El progreso de carga y finalización solo se reenviará a eventos desde LoaderInfo del Loader que usamos para cargar la imagen. Y seleccionar realmente será manejado por CoverFlow, ya que sabe cómo administrar una colección de Covers. ¡Entonces, hemos terminado con esto también! 

Agreguemos las propiedades que necesitamos, junto con los adaptadores y captadores cuando corresponda. Seguiremos la convención de hacer que las propiedades reales sean privadas, y proporcionar acceso público a través de instaladores y captadores.

En Cover.as, agregue algunas propiedades al archivo de clase. Me gusta mantenerlos agrupados, en la parte superior de la definición de la clase:

Escribe los setters y getters:

¿Por qué solo tener estos? Bueno, otros objetos realmente no necesitarán acceso al Bitmap o al Loader, y para los subtítulos y los datos, operaremos bajo la suposición de que una vez que se establezcan esos valores, no necesitarán cambiar. Nos ocuparemos de eso en el siguiente paso.

Adelante, prueba esto. No habrá cambios para tomar nota; aún deberías ver un cuadrado rojo. Pero al probar la película, ejecutamos las cosas a través del compilador, lo que nos ayuda a encontrar errores en caso de que se introduzca alguno. Si todo fue bien, la película se ejecutará y verá el cuadrado rojo. Aquí está la clase Cover completa a partir de ahora:


Paso 14: establece las propiedades

Como se mencionó, estableceremos las propiedades como leyenda, datos y color de fondo a través del constructor. Modifique el constructor de Cover.as para que tome esos tres valores y luego transfiera esos valores a las propiedades apropiadas. Los cambios al constructor a continuación están resaltados:

Ahora, de nuevo en CoverFlow.as, necesitamos proporcionar valores cuando creamos nuestra prueba Cover o de lo contrario obtendremos errores. En el constructor:

Obviamente estamos usando datos ficticios en este momento, pero esto debería compilarse de manera segura. Nuevamente, no veremos ningún cambio, pero pruebe la película para asegurarse de que no ha introducido errores. Sin embargo, podemos escribir una prueba rápida de lo que escribimos al rastrear los valores de los captadores. Aún en el constructor CoverFlow:

Debería ver lo siguiente en su panel de Salida:

The results of tracing our new gettersThe results of tracing our new gettersThe results of tracing our new getters

Esto verifica que estamos configurando correctamente y obteniendo al menos las propiedades caption, data XML y backgroundColor.


Paso 15: carga una imagen

Para este paso, necesitaremos una imagen para cargar. Hay varios en el paquete de descarga para este tutorial, ya recortados y clasificados como cuadrados. Vamos a elegir uno para cargar. Usaré "best.jpg".

Primero, coloque la carpeta de imágenes en la misma carpeta de proyecto que ha estado usando. Debería estar en el mismo lugar que su CoverFlowTest.swf. Deje caer la carpeta de imágenes de la descarga aquí, o cree su propia carpeta de imágenes aquí y coloque las imágenes que desea cargar en esa carpeta.

The current project folder structureThe current project folder structureThe current project folder structure

A continuación, en el constructor de Cover.as, elimine las líneas que dibujan el cuadrado rojo.

Ahora, crea un método público llamado load. Recibirá un URL de cadena como parámetro, lo almacena en la propiedad _src y luego carga la imagen desde esa URL.

Esto es material estándar de Loader. En última instancia, dependerá de otro objeto (como CoverFlow) alimentar este valor al Cover.

Hemos agregado tres oyentes de eventos, todos los precios estándar para la carga. Vamos a escribir algunas funciones de oyente de stub para que podamos probar. Agrega este código a tu clase. Estos son métodos nuevos, no código dentro de otro método:

Terminaremos haciendo algo más útil con estos, pero por ahora dispararemos un rastro solo para asegurarnos de que las funciones se llamen en respuesta a los eventos. Como referencia, toda la clase debería tener un aspecto similar (las adiciones de este paso están resaltadas):

Ahora, para probar esto, tenemos que volver a CoverFlow.as y en algún punto del constructor, llamar al método load y pasar una ruta válida a una imagen:

Si prueba la película ahora, no solo verá una imagen en lugar del cuadrado rojo:

A Cover loading an imageA Cover loading an imageA Cover loading an image

... pero también debería ver algunos rastros en el panel de Salida que confirman que el progreso y los eventos completos se dispararon (los eventos de progreso reales pueden variar):

Events tracing to the Output panelEvents tracing to the Output panelEvents tracing to the Output panel

Obviamente, la carga se completó porque apareció la imagen, pero es bueno asegurarse de que conectamos el evento correctamente. Si desea probar el evento de error, simplemente cambie el valor de la ruta pasada al constructor del Cover a algo que no funcionará, y debería ver un rastro diferente.


Paso 16: Centre la imagen

El movimiento dentro del "flujo" tiene dos requisitos. En primer lugar, los elementos deben girar alrededor de un eje vertical centrado horizontalmente en la imagen. En segundo lugar, la parte inferior de la imagen debe estar alineada con un "suelo" para que las imágenes de diferentes alturas estén todas "sentadas" en el mismo plano.

Para facilitar la rotación vertical, podemos centrar la imagen horizontalmente en relación con el Sprite que lo contiene. Del mismo modo, para facilitar la alineación del fondo, podemos colocar la imagen de modo que su borde inferior esté en el eje x del Sprite que lo contiene. Para lograr esto, necesitamos saber el tamaño de la imagen que acabamos de cargar. Para determinar eso, debemos asegurarnos de que la imagen esté cargada antes de intentar trabajar con su tamaño. Entonces, todo nuestro código de centrado debe estar en el controlador completo.

Agregue esto al método onLoadComplete de Cover.as (y elimine el rastreo que está actualmente allí):

Si prueba esto ahora, probablemente no verá ninguna imagen, porque el sprite Cover se posiciona en (0, 0) en el escenario, pero todo su contenido visible está por encima del punto de registro del sprite Cover. Por lo tanto, antes de realizar pruebas, agregue algunas líneas de código para colocar el objeto Cover en CoverFlow.as, justo después de crear el objeto Cover de prueba:

Pruébalo ahora, y deberías ver la imagen en algún lugar en el medio del escenario.
Si lo desea, puede volver atrás y volver a probar la configuración del ancho y la altura de CoverFlow (consulte el Paso 12) para ver si el scrollRect funciona para enmascarar el contenido de CoverFlow. Debería ver un objeto Cover parcial.


Paso 17: Refleja la imagen

Ahora entramos en cosas más desafiantes. Vamos a tomar la imagen que acabamos de cargar y crear una copia que se voltee verticalmente (es decir, se giró alrededor del borde inferior). Usaremos BitmapData para clonar el aspecto de la imagen. Al igual que antes, porque necesitamos la imagen real antes de que podamos hacer esto, iniciaremos el dibujo de la reflexión en el método onLoadComplete:

Es un montón de código que puede no ser familiar para usted, según cuánto haya trabajado con BitmapData.

BitmapData, en primer lugar, es una clase que le permite trabajar con los píxeles de un mapa de bits. La primera línea crea una, especificando un ancho y alto, transparencia y un color de relleno predeterminado.

Lo siguiente que haremos es generalmente una operación directa, donde dibujamos los gráficos de otro objeto de visualización en una representación de mapa de bits renderizado. Sin embargo, no queremos un clon directo, queremos voltear la imagen. Podemos hacer eso pasando un objeto Matrix al segundo parámetro del método draw. Un objeto Matrix representa una transformación geométrica, bidimensional, que incluye escala, rotación y traducción (o posición). Entonces, antes de hacer la operación de dibujar, creamos un nuevo objeto Matrix. Luego usamos el método de la escala en la Matriz para voltearlo verticalmente (los dos argumentos son escala horizontal y escala vertical; 1 no es cambio y -1 es inversión). A continuación, como la operación de escala funciona de forma similar a cómo funcionan los objetos de visualización, de hecho escalamos la imagen para que esté "apuntando" en la otra dirección. Entonces tenemos que reposicionarlo para volver a colocarlo en el lienzo del mapa de bits. El método translate lo hace, reposicionando la imagen escalada por la altura de la imagen.

Con la imagen volteada representada en un objeto BitmapData, necesitamos mostrarla realmente en un objeto Bitmap. BitmapData es una representación pura de datos y no es, en sí misma, visualizable. Sin embargo, el objeto Bitmap se puede visualizar y, con bastante facilidad, toma un objeto BitmapData como parámetro para su constructor, por lo que creamos un mapa de bits, lo agregamos a la lista de visualización y finalmente lo posicionamos horizontalmente para que esté alineado con la imagen original.

Prueba esto, y deberías ver doble.

The image showing off a reflectionThe image showing off a reflectionThe image showing off a reflection

Las operaciones de BitmapData pueden ser confusas, así que si no están claras para usted en este punto, lo aliento a que busque más información al respecto. BitmapData abre algunas posibilidades muy interesantes, por lo que vale la pena aprender más sobre ello.


Paso 18: atenúa la reflexión

Ahora, para hacer que el reflejo se desvanezca, necesitamos atenuar ese color de fondo y mantener el reflejo opaco. Dará la apariencia de ser transparente, pero si lo mantenemos opaco, un reflejo puede superponerse sobre otro sin que se vea el más bajo. Continúa y mira la vista previa de nuevo si necesitas visualizar esto:

Faded reflectionsFaded reflectionsFaded reflections

Agregaremos un montón de código de dibujo para dibujar Reflection (en Cover.as). Aquí está todo el método, con los cambios resaltados:

Sí, eso es un montón de código para masticar. En resumen, crea otro objeto Shape, en el que dibujamos un degradado lineal. El gradiente se establece para ser del mismo color que el fondo principal, con cambios alfa que van de la mitad de transparente a completamente opaco, de arriba a abajo. Este gradiente luego se fusiona en el BitmapData que ya contiene el reflejo volteado de la imagen, de modo que el efecto final es un reflejo que se desvanece al color de fondo.

Adelante, y prueba esto, debes sentirte bien al obtener estos resultados:

A nicely faded reflectionA nicely faded reflectionA nicely faded reflection

Sin embargo, si prueba más esto e intenta configurar la instancia de coverFlow en CoverFlowTest con un color de fondo que no sea negro, verá algunos resultados impredecibles:

A not-so-nicely faded reflectionA not-so-nicely faded reflectionA not-so-nicely faded reflection

Nos ocuparemos de esto a continuación.


Paso 19: realizar un seguimiento de las instancias de cobertura

Actualmente estamos utilizando la instancia de CoverFlow como un lugar para probar una sola instancia de portada. Y a pesar de que eventualmente nos desharemos de esa instancia de prueba, ahora debemos almacenarla en una lista oficial de todas las instancias. Crearemos una matriz de instancias de Cobertura, y todos los que obtengan creaciones quedarán escondidos en la Matriz. En realidad, dado que estamos apuntando a Flash 10, podemos convertirlo en un Vector, que ofrecerá un pequeño aumento en el rendimiento.

Primero, en CoverFlow.as, declare Vector, junto con el resto de las propiedades:

Luego, cree una instancia de ese Vector casi de inmediato. En el constructor, coloca esta línea justo después de configurar _width y _height:

Y después de haber creado el objeto de portada "prueba" (aún en el constructor), agréguelo al Vector:

La parte superior de su clase CoverFlow debe verse así (las adiciones resaltadas):


Paso 20: establece el color de fondo de las cubiertas

Ahora, en el backgroundColor setter, no solo necesitamos dibujar el fondo principal, sino que debemos informar a todas nuestras instancias de Cover que el color de fondo ha cambiado. El método completo se verá así (el nuevo código está resaltado):

Luego, en la clase Cover, actualice su setter de BackgroundColor para que vuelva a dibujar el reflejo:

Por último, tenemos que hacer algunas comprobaciones de errores. En el caso de prueba que hemos configurado en este momento, el backgroundColor se configura antes de que se cargue la imagen (y esta es una acción razonable, normalmente configurará CoverFlow y le dará un backgroundColor de inmediato, a medida que se carguen las imágenes). Debido a esto, el cargador tiene un ancho y una altura de 0, lo que hace que la primera línea de drawReflection produzca un error. Si configura el backgroundColor  del objeto CoverFlow en este momento y prueba la película, verá esto:

Runtime error from trying to set the background color too soonRuntime error from trying to set the background color too soonRuntime error from trying to set the background color too soon

Esto es bastante fácil de manejar. Si el cargador tiene un ancho y / o alto de 0, podemos suponer con seguridad que el cargador no ha terminado de cargarse todavía. Entonces, las primeras líneas de drawReflection pueden verificar esto:

Simplemente salga del método, y todo estará bien. No se preocupe, también llamamos a drawReflection desde onLoadComplete, por lo tanto, en ese punto, la propiedad _backgroundColor se establecerá con el valor correcto y el cargador se cargará para que podamos dibujar. En el caso de que deseemos cambiar el color de fondo después de que se carguen las imágenes, esto todavía funciona, porque el Loader tendrá dimensiones que no sean cero, y drawReflection se ejecutará desde el backgroundColor setter.

Pruébelo: vuelva a CoverFlow Test y configure el color de fondo de la instancia de CoverFlow:

Y tendrás una gloriosa cosa naranja pasando:

Orange background and orange reflectionsOrange background and orange reflectionsOrange background and orange reflections

Siéntase libre de eliminar esa línea una vez que esté satisfecho de que el color de fondo y el reflejo funcionan (¿a qué no le gusta el naranja?).


Paso 21: Escribe un origen de datos XML

Pasaremos a proporcionar un conjunto real de datos para conducir esta pieza. Nuestra imagen de prueba puede desaparecer y comenzaremos a mostrar un conjunto de imágenes.

Supondremos que para proporcionar datos al objeto CoverFlow, lo más probable es que se proporcione como un archivo XML externo. Esto facilita los cambios en el contenido en vivo, pero también nos permite cargar más de un archivo XML para reutilizar el mismo módulo CoverFlow con contenido diferente en la misma película.

Antes de escribir el código ActionScript para manejar el XML, escribamos nuestro archivo XML. Cree un nuevo archivo de texto llamado coverFlowImages.xml en la misma carpeta que su archivo CoverFlow.fla. Habrá un nodo raíz, por supuesto, y dentro de eso, simplemente habrá una lista de nodos de imagen, uno para que cada elemento aparezca en el flujo. El formato general para un nodo de imagen se verá así:

Se supone que la imagen tendrá una ruta de imagen, y probablemente un título. Sin embargo, es posible que deseemos asociar más datos con cada imagen, que podría ser solo texto simple, o incluso podríamos proporcionar una estructura XML anidada con datos complejos dentro de, por ejemplo:

El punto aquí es que dentro del nodo de imagen, podemos poner lo que queramos (desde datos XML extensos y complejos hasta nada). Esta información estará disponible a través de la clase CoverFlow, como el nodo XML que es.

 Aquí está nuestro documento final completo (utilicé otras imágenes y enlaces durante la construcción), una lista de imágenes y datos seleccionados de active.tutsplus.com:

Parece mucho, pero en realidad es solo una lista de nodos de imágenes con dos atributos cada uno, el src y el title, y un nodo < link> dentro del nodo < image> , que alberga la URL del artículo asociado con la imagen.


Paso 22: carga un archivo XML

Para cargar XML, necesitaremos una propiedad para almacenar el XML y una propiedad para que un URLLoader cargue el XML. Necesitaremos un método público para iniciar una carga con una URL de cadena, algunos manejadores de eventos internos para que el URLLoader maneje los eventos de carga XML. Los elementos de este paso son una tarifa bastante estándar para cargar XML, así que simplemente apilaré todo en un solo paso y no pasaré mucho tiempo explicando las cosas.

Comience agregando dos propiedades, una para el urlLoader y otra para el xml, en la parte superior de la clase CoverFlow, con el resto de las propiedades.

En el constructor de CoverFlow, cree y configure el URLLoader (en realidad no importa en qué parte del constructor, pero estoy optando por el final):

Escriba las funciones de manejo de eventos de URLLoader, en algún lugar del cuerpo principal de su clase:

En este momento solo estamos rastreando cosas; nos aseguramos de convertir los datos de URLLoader a XML y luego escupirlos como están. También estamos solucionando un error en caso de que obtengamos una URL incorrecta para cargar. Podríamos hacer algo más elegante aquí, pero por ahora solo estamos evitando que el error detenga todo lo demás, mientras seguimos rastreando un mensaje.

Necesitamos un método de carga pública para iniciar la carga de XML. Podemos suponer que si ya hay algo cargado, primero debemos despejarlo y luego comenzar la nueva carga. Con ese fin, no solo iniciaremos la carga en el URLLoader, sino que también llamaremos a un método llamado clearContents para eliminar todo lo creado previamente en CoverFlow. Lo completaremos más tarde, pero lo planearemos y lo llamaremos, y crearemos un método vacío para albergarlo.

Por último, tenemos que actualizar nuestras pruebas. Necesitamos eliminar las líneas que crean una Cover de prueba del constructor de CoverFlow (las he comentado aquí para que pueda identificarlas, pero continúe y elimínelas). El próximo fragmento de código de esta función los eliminará):

Y luego tenemos que volver a CoverFlowTest.as y agregar una llamada para load. Pase el archivo coverFlowImages.xml que creamos en el último paso.

Debería ver nuestro documento XML trazado en el panel de Salida.


Paso 23: analizar el XML

A continuación, debemos analizar el XML y eventualmente hacer algo con los datos. Lo analizaremos en este paso y comenzaremos a hacer algo con él en el siguiente paso.

En el método onXMLLoad de CoverFlow, elimine el rastreo y reemplácelo con un bucle XML básico. Por ahora, rastrearemos los valores para asegurarnos de que los estamos analizando correctamente.

No hay nada demasiado especial pasando. Solo estamos seleccionando todos los nodos de imagen, lo repetimos y luego extraemos los atributos de ellos. Ahora, haremos algo con ellos.


Paso 24: Creación de objetos de portada

Tendremos el potencial de cargar bastantes imágenes. Será mejor controlar la carga cargando de a uno por vez. Esto maximizará el ancho de banda para cada imagen, permitiendo que la primera imagen aparezca lo más pronto posible, de modo que haya algo que ver en la pantalla antes de que pase demasiado tiempo. Además, la mayoría de los navegadores tienden a reducir el número de solicitudes simultáneas de todos modos, por lo que, en lugar de dejar que el navegador lo controle, podemos controlarlo en Flash.

Para trabajar en esto, tendremos que cargar algunas imágenes en varios objetos Cover, así que comenzaremos creando los objetos Cover e iniciando la carga. Analizaremos el proceso en los próximos pasos.

En el método CoverFlow onXMLLoad, eliminaremos los rastros y en su lugar crearemos objetos de portada:

Notarás que estamos haciendo lo que esperas: primero, creamos un nuevo objeto Cover, pasando los datos seleccionados del XML. Luego lo agregamos a la lista de visualización. Luego lo posicionamos; esto es temporal, pero nos permitirá ver resultados por ahora. El valor de x se basa en el número de iteraciones, de modo que obtenemos una ubicación de izquierda a derecha. Una vez más, revisaremos la lógica del diseño, esto es solo para ver algo en este momento.

Luego le decimos a la portada que cargue con el origen de la imagen que se encuentra en el XML, y finalmente almacenamos el objeto Cover en nuestro _covers Vector.

Esta llamada a cargar también es temporal; en este momento solo nos estamos asegurando de que estamos creando con éxito objetos Cover a partir de los datos XML. Pasaremos a cargar imágenes secuencialmente a continuación.

Si publica ahora, debería ver algo al menos un poco interesante:

Covers that load images from the XML documentCovers that load images from the XML documentCovers that load images from the XML document

No hay 3D todavía, pero eso viene. ¡Se paciente!


Paso 25: carga de imágenes en secuencia

Vamos a cambiar la lógica de cargar cada cubierta de inmediato para cargarlas progresivamente. Esto implicará hacer un seguimiento de un contador que apunta al Cover que se está cargando actualmente, y una función que carga el siguiente Cover. Tenemos algunas paradas en el camino, así que síguelo.

Primero, en Cover, queremos asegurarnos de volver a enviar el evento COMPLETE  una vez que se cargue la imagen. En onLoadComplete, agregue esta línea al final:

El resto de nuestros cambios ocurrirá en CoverFlow. Primero, agregue una propiedad para rastrear la carga actual por índice:

Luego, en onXMLLoad, eliminaremos var src: String = ... y cover.load (src) líneas, y establecer la propiedad _loadCounter en 0, así como llamar a un método que aún no hemos escrito:

Ahora vamos a escribir ese método loadNextCover. La idea es tomar el valor actual de _loadCounter y usarlo para apuntar a una portada y una imagen de origen.

Primero, obtenemos el objeto de portada creado previamente del _covers Vector. Luego obtenemos la URL de la imagen correspondiente volviendo a los datos XML y encontrando el nodo de imagen usando el mismo valor de _loadCounter. Luego simplemente le pedimos a la cubierta que cargue esa URL. Finalmente, agregamos un oyente COMPLETE, que debemos escribir a continuación:

Cada vez que una cubierta termina de cargarse, primero limpiemos y eliminemos el detector de eventos COMPLETE. Luego incrementará _loadCounter y luego verificará si todavía podemos tener Cover en el _covers Vector. Si lo hacemos, llamamos a loadNextCover () de nuevo, lo que vuelve a iniciar el proceso con una nueva portada e imagen. Si no, entonces debemos estar al final, para poder enviar un evento COMPLETE.

Para probar este evento, regrese a CoverFlowTest y agregue el siguiente código:

Esto debería ser sencillo: simplemente estamos agregando un oyente a ese evento COMPLETE que solo rastrea. Adelante, ejecuta la película ahora. Debería ver las imágenes aparecer secuencialmente y, una vez que todas las imágenes estén cargadas, debería ver el "Coverflow cargado y listo para funcionar". mensaje en el panel de Salida.

Proof that all images have loaded.Proof that all images have loaded.Proof that all images have loaded.

Paso 26: determinando el progreso

Para poder enviar eventos PROGRESS a medida que se cargan las imágenes, vamos a fingir un poco las cosas. Vamos a suponer que la primera imagen para cargar es, en promedio, representativa de todas las imágenes. Ciertamente habrá casos en que esto no sea cierto, pero para la mayoría de las aplicaciones trataremos con imágenes de dimensiones, calidad y contenido similares, y por lo tanto, la mayoría de las imágenes tendrán un tamaño de archivo similar.

Primero, necesitamos cambiar el método onLoadProgress en Cover para que podamos deshacernos de la traza y volver a enviar el evento PROGRESS:

Entonces necesitaremos dos propiedades más en CoverFlow para ayudar a rastrear el progreso general:

Ahora, lo siguiente es obtener el tamaño de la primera imagen una vez que comience a cargarse. Necesitamos asegurarnos de que estamos agregando un oyente de eventos PROGRESS cuando comenzamos la carga. En el método loadNextCover de CoverFlow:

Y agregue el método onCoverProgress:

Ahora, para probarlo, regrese a CoverFlowTest y agregue un oyente para el evento PROGRESS:

Y prueba la película. Debería ver una larga cadena de trazas de progreso, que termina en el mensaje "cargado", algo como lo siguiente:

You'll see quite a few numbers go by in the Output panelYou'll see quite a few numbers go by in the Output panelYou'll see quite a few numbers go by in the Output panel

Por supuesto, querrás mostrar algo informativo en el escenario, no trazar números. Sin embargo, esta tarea particular no corresponde realmente a CoverFlow; es suficiente que esté enviando los eventos apropiados. No profundizaré en la creación de una barra de progreso, y en su lugar, me referiré al trabajo más interesante de mostrar Covers en 3D.


Paso 27: Refactorizar la lógica

Veremos ese bucle que analiza el XML, crea los objetos Cover y los establece. Vamos a descargar la lógica de diseño a otro método. Elimine las líneas en el bucle que colocan el objeto Cover (es posible que desee copiarlas) y agregue una llamada a un método pendiente de escritura después del bucle.

Luego, escribe ese método de layout . Es otro ciclo, este sobre el vector _covers, e internamente hace la misma lógica de posicionamiento:

Adelante, pruébalo ahora; no deberías ver absolutamente ningún cambio. La única diferencia es la lógica puramente detrás de escena. La ventaja que hemos agregado es que podremos llamar a layout () en cualquier momento, independientemente de onXMLLoad ().


Paso 28: Diseño inicial

Vamos por una gran recompensa en este momento. Reescribiremos la lógica del layout para que eso comience a suceder en 3D. Retire la línea cover.x = ... y agregue las líneas resaltadas a continuación (aún en CoverFlow):

Esta lógica necesita mucho amor, pero sigue adelante y hazlo ahora, y deberías ver ... algo que no está bien.

It's in 3D, but clearly we're not finishedIt's in 3D, but clearly we're not finishedIt's in 3D, but clearly we're not finished

Debería ver el potencial allí, pero obviamente necesitamos gestionar la profundidad. Esto es una desventaja al uso de las capacidades 3D integradas de Flash: representa cada objeto DisplayObject correctamente, pero no una "escena" de múltiples DisplayObjects. Se aplican las reglas normales de apilamiento de profundidad 2D, incluso si la "z" de un objeto dado debe colocarlo de otro modo.

Sin embargo, este poco de lógica le da una idea de lo que vamos a hacer: Primero, determine en qué parte del "flujo" se encuentra una Cubierta determinada: centrada, a la derecha o (eventualmente) a la izquierda. Luego configure apropiadamente las propiedades x, zy rotationY de la Portada. La matemática involucrada en la posición x del lado derecho es un bocado, pero se puede leer así:

Comience en el centro (_background.width / 2), mueva 60 píxeles hacia la derecha para dejar espacio alrededor del elemento centrado (... + 60), luego, dependiendo de dónde estemos en el bucle, mueva la tapa más hacia la derecha  (… + (i + 30))


Paso 29: Administrar la profundidad

Vamos a abordar el problema de profundidad. Esto es lo que debe suceder: el artículo centrado debe ser el que más adelante. Después de eso, los elementos de cada lado deben disminuir en el índice de profundidad cuanto más lejos estén del centro. Afortunadamente, esto es relativamente simple de lograr. Ajuste la lógica en el método de layout de CoverFlow:

En el primer bloque if, estamos en la portada centrada, por lo que establecemos el índice de la portada en el índice más alto disponible. En el bloque else, cuanto mayor es i, menor es el número de índice que obtenemos. Eso funciona en relación con otros Covers en el lado derecho, pero tenemos que bajar el índice una más para acomodar el Cover central (más alto).

Pruébelo ahora ... ¡debería ver algunas imágenes prometedoras!

Proper depth sortingProper depth sortingProper depth sorting

Paso 30: el lado izquierdo

OK, hasta ahora hemos estado haciendo suposiciones que hacen que sea muy difícil establecer las cosas de verdad. La gran suposición es que estamos centrando la primera Cobertura (índice 0). Si intentas centrar, digamos, el cuarto Cover, obtendrás resultados que no se esperan del todo:

The fourth cover centeredThe fourth cover centeredThe fourth cover centered

No se ve mal a primera vista, pero piénselo. Si centramos la cuarta Cobertura, esperaríamos ver tres Cubiertas a la izquierda, la cuarta en el centro y el resto a la derecha. Hagamos que eso suceda.

Primero, creemos una propiedad nueva (aún en CoverFlow) que contendrá nuestro valor de índice actual. Llamémoslo _selectedIndex.

Y, solo para fines de prueba, estableceremos ese valor en 4 en la parte superior del método de diseño. Esto se eliminará en el siguiente paso. Luego, volveremos a trabajar la lógica dentro del ciclo de diseño para ser un poco más dinámico.

Es mucha lógica y matemática, pero en realidad es bastante repetitiva. El nuevo bloque está en el medio, pero es casi idéntico al otro bloque else if, solo unas pocas cosas se invierten. Pero adelante y prueba la película, debería verse más o menos así:

Proper depth sortingProper depth sortingProper depth sorting

El gran cambio fue el alejamiento de i como un factor directo en la colocación de Covers, pero en cambio determina la distancia de i del índice actual. Cuanto más lejos esté, más lejos estará la x del centro, y también la más atrás en profundidad tiene que ser. La matemática es solo una traducción de lo que estábamos haciendo antes a un enfoque más dinámico. Si lo desea, siga adelante e intente configurar _selectedIndex en otros valores, y asegúrese de obtener los resultados que espera.


Paso 31: Personalizando la Apariencia

Puede sentir que la distribución de Covers es un poco pequeña (sé que lo hago, pero tengo que trabajar dentro del límite de 600 píxeles que Activetuts + pone en mí). De hecho, hay bastantes parámetros de posicionamiento que podrían ajustarse para ajustar el aspecto general. Vamos a seguir adelante y crear un conjunto de propiedades, junto con los adaptadores y ejecutores asociados, para manejar esta personalización. Y por supuesto usaremos esas propiedades en lugar de números codificados en nuestro método de diseño.

Este será un paso largo, pero no temas, es bastante básico.

Primero, las propiedades:

Estas propiedades controlarán lo siguiente:

  • centerMargin controla la cantidad de espacio en cada lado de la cubierta central y las primeras cubiertas laterales. Debe ser un valor positivo.
  • horizontalSpacing controla la cantidad de espacio entre las Cubiertas en la "parte posterior". Es decir, toda el cover a la izquierda y a la derecha (pero sin contar el centro) tendrá la misma cantidad de espacio entre los covers adyacentes. la cantidad de espacio entre los Covers en la "parte posterior". Debe ser un valor positivo.
  • backRowDepth controla qué tan atrás está la última fila. Esto será en forma de un desplazamiento, a fin de evitar que la fila de atrás se encuentre frente al Cover central. Debe ser un valor positivo.
  • backRowAngle controla el ángulo al que girarán los Covers en la última fila. Esto es una especie de valor absoluto, y el valor es "reflejado" para el otro lado. Debe estar limitado entre 0 y 90.
  • verticalOffset controla la cantidad por la cual mover todo el conjunto de Covers hacia arriba o hacia abajo, compensar el valor y determinado automáticamente. Este puede ser cualquier número. Volveremos a este poco de lógica, pero también podemos escribir la propiedad, el setter y el getter mientras hacemos los otros cuatro.

Configuraremos constantes para contener los valores predeterminados de cada una de estas propiedades. Estos pueden ir con el resto de sus propiedades:

En el constructor, establezca cada una de las propiedades a estos valores predeterminados (si no le gustan los valores predeterminados, puede cambiarlos, esa es parte de la razón para juntarlos en un lugar fácil de encontrar). De esta forma, nos aseguraremos de que cada una de estas variables se configure con un valor adecuado, incluso si el usuario nunca especificó una.

Y, con los valores válidos en mente, escriba los setters y getters (voy a poner estos correctos después de nuestros setters y getters actuales, alrededor de la línea 89):

Y, por último, necesitamos reemplazar los números integrados en nuestra lógica de diseño con estas propiedades. En layout:

Puedes probar la película tal como está, y las cosas deberían funcionar exactamente como antes. Sin embargo, también puede probar estas propiedades configurándolas desde CoverFlowTest. Configúrelos como desee (de hecho, asegúrese de probar los valores ilegales, como -200 para centerMargin), por ejemplo:

Things are a little more spread out with these settingsThings are a little more spread out with these settingsThings are a little more spread out with these settings

Paso 32: diseño eficiente

En este momento, nuestro CoverFlow funciona y es algo personalizable, pero solo si establecemos los valores antes de que se cargue el archivo XML. Como está ahora, establecer las propiedades en cualquier punto después de eso dará como resultado ningún cambio.

Esto podría ser tan fácil como simplemente llamar a layout () desde cada uno de los setters. Sin embargo, esto es propenso a un uso ineficiente de los ciclos de CPU. Si necesita establecer las cinco propiedades de una sola vez, terminaría ejecutando la lógica de diseño 5 veces seguidas, las 4 primeras son inútiles ya que no terminó de configurar los valores para lo que necesitaba.

Debido a que Flash opera en el modelo de trama de renderizado, donde el código se ejecuta entre cada cuadro, y luego se actualiza la pantalla, necesitamos una forma de ejecutar solo ese método de diseño cuando el escenario esté por renderizarse para el siguiente cuadro. Esto podría permitirnos establecer propiedades tanto como nos plazca, pero solo redibujar CoverFlow una vez por cuadro.

Afortunadamente, hay una manera fácil de hacerlo, pero requiere algunos medios. Necesitamos utilizar el evento RENDER. El evento RENDER será enviado por un DisplayObject cuando el escenario esté a punto de ser renderizado, pero antes de que ocurra el renderizado. Como beneficio adicional, el evento no se despacha cuando se minimiza Flash Player, lo que significa que no deberíamos estar desperdiciando ciclos si ni siquiera puedes ver la película.

Implementar esto es un proceso de tres pasos: primero, tenemos que escuchar el evento. En el constructor de CoverFlow, agregue esta línea:

A continuación, agregue el método de escucha:

Sí, eso es todo ... llamaremos diseño cada vez que recibamos el evento.

Por último, necesitamos llamar a invalidate en el escenario cada vez que queremos que el evento RENDER se presente en nuestro camino. Esto significa que en cada uno de los setters, necesitamos agregar esta línea al final:

Si no hay etapa, no queremos llamar a un método y causar un error de referencia de objeto nulo. De todos modos, si no hay una etapa, entonces el objeto CoverFlow no está en la lista de visualización de todos modos, por lo que no tiene sentido pedir el evento de renderizado.

La llamada para invalidate, sin embargo, es cómo hacemos que el evento RENDER se active. Sin esa llamada, no obtendremos el evento, incluso con un oyente de eventos. Entonces, necesitamos esto en cada setter:

Entonces, si configuramos las 5 propiedades al mismo tiempo, podemos llamar invalidate al escenario más de una vez, pero está bien, eso no va a causar problemas. Deberíamos obtener un único evento RENDER como resultado, lo que significa que tendremos CoverFlow solo una vez por cuadro.

Para probar esto, podemos poner algunos rastros en nuestros métodos, y luego establecer estas propiedades después de que el XML se haya cargado. Primero, agregue un mensaje de seguimiento a onRender:

Luego, en CoverFlowTest, configure las 5 propiedades en onCoverFlowLoaded:

Debería ver que la película carga imágenes como antes, sin embargo, esta vez, las imágenes deberían saltar a un diseño ligeramente diferente una vez que todas las imágenes se hayan cargado. Esto demuestra que las cosas funcionan visualmente, pero más importante aún, verifica el panel de Salida. Debería ver un solo mensaje de "render". Llamamos a stage.invalidate de los 5 setters, pero eso resultó en un solo evento RENDER, que es lo que buscamos.

Siéntase libre de eliminar la traza y el código de diseño que acabamos de agregar.


Paso 33: Reagrupar

Ha pasado un tiempo desde que he enumerado el código de clase completo. Nuestro trabajo ha sido principalmente en CoverFlow. Aquí está el estado actual de las cosas con esa clase:


Paso 34: Título

Tenemos algunas cosas más que arreglar antes de tener animación e interactividad divertida. Tomaremos la etiqueta. Para que esto funcione, solo necesitamos un campo de texto. Configuraremos algunos estilos predeterminados y abriremos los estilos para personalizarlos en el siguiente paso.

Primero, cree un TextField y agréguelo al CoverFlow. En CoverFlow, junto con el resto de sus propiedades, cree una propiedad TextField:

Luego configúrelo en el constructor (cualquier lugar está bien, pero lo estoy colocando después de todo lo demás en el constructor):

Finalmente, en el layout, agregue una línea para establecer el texto de TextField:

La creación de TextField en el constructor es posiblemente un poco desalentadora. En su mayor parte, sin embargo, es una configuración de TextField bastante estándar, todo hecho a través del código. Si nunca lo has hecho antes, te sorprenderá la cantidad de líneas necesarias para configurar un TextField. Esto es típico; el TextField predeterminado es bastante insulso, y debemos asegurarnos de que el tamaño esté establecido, que sea un campo de texto de varias líneas y que tenga un formato básico.

Incluso si antes había escrito TextFields programáticos, el negocio "fontName" podría ser una sorpresa. La idea general es que estamos configurando un estilo predeterminado, basado en el uso de Apple de la fuente Lucida Grande como fuente del sistema. Pero no podemos estar seguros de que los usuarios tengan esa fuente (la mayoría, si no todos, los Mac que ejecutan OS X deberían hacerlo, pero no podemos estar 100% seguros y las PC son otra historia). Entonces obtenemos una matriz de fuentes del sistema, usando Font.enumerateFont (verdadero). El "verdadero" dice enumerateFont para enumerar las fuentes del sistema; de lo contrario, enumera las fuentes que se incluyen en el archivo SWF. Esto devuelve una matriz de objetos de fuente. Recorremos esa matriz, buscando una fuente con un nombre similar a "Lucida Grande". Esa es una expresión regular en la llamada de "búsqueda", que solo nos da cierta flexibilidad en caso de que el nombre de la fuente sea ligeramente diferente en un sistema en lugar de otro. Si encontramos una coincidencia, tome el nombre de la fuente y rompa el ciclo. Si lo hacemos a través del bucle sin una coincidencia, simplemente use "Verdana". Esto nos da Lucida Grande si el usuario lo tiene, y una fuente sans serif de respaldo si no lo tienen.

Adelante, pruébalo; debería ver su etiqueta aparecer debajo del elemento centrado.

The label showingThe label showingThe label showing

Además, es posible que notes un montón de rastros adicionales que aparecen en el panel de Salida. Esto es del bucle de fuente en el constructor. No necesitamos este largo plazo, pero pensé que sería útil ver estas fuentes enumeradas. Su salida será diferente, naturalmente, dependiendo de las fuentes instaladas en su sistema.

A sampling of the Output panel, showing fonts on my systemA sampling of the Output panel, showing fonts on my systemA sampling of the Output panel, showing fonts on my system

Después de que haya tenido suficiente para examinar los nombres de las fuentes, puede eliminar la trace(font.fontName); línea del constructor (línea 74 en el listado superior).


Paso 35: Habilitar el estilo de texto

Ahora, ¿qué pasa si quieres personalizar un poco el campo de título? Esa es una solicitud razonable, y tenemos algunas opciones. Una es que podríamos escribir setters y getters para los tipos de cosas que queremos exponer para el control externo. Esto podría incluir cosas como habilitar multilínea, ajustar el ancho, establecer el color del texto, establecer el tipo de alineación, configurar la fuente y establecer el tamaño de la fuente. La mejor parte de este enfoque es que mantiene las cosas encapsuladas y probablemente facilitará las cosas al programador que está utilizando este objeto CoverFlow. Si todo lo que necesita hacer es escribir "coverFlow.fontSize = 16" para hacer ese pequeño ajuste, entonces eso es lo más fácil posible. La desventaja es que tenemos que escoger y elegir qué está expuesto, y mientras más control ofrecemos, más código tenemos que escribir en CoverFlow para acomodar eso.

Otro enfoque es simplemente exponer TextField como una propiedad (de solo lectura) de CoverFlow. Esto tiene básicamente lo opuesto a pro / contra como la primera opción. Nos resulta fácil implementarlo ahora mismo y le damos un control total al usuario final; si puede hacerlo en un TextField, puede hacerlo con el título de CoverFlow. Sin embargo, ceder demasiado control podría tener efectos desastrosos. La mayoría de la gente no se molestaría en tocarlo, pero los accidentes ocurren. Peor aún, sin embargo (en mi opinión) es que para establecer el tamaño de letra de la leyenda, terminas con un texto bastante detallado (y completamente hipotético, ten en cuenta):

Sin mencionar que hay una diferencia entre defaultTextFormat en setTextFormat (), y será mejor que esté familiarizado con cuál usar en este escenario.

¿Entonces cual es mejor? En un mundo ideal, tendría que votar por el método de encapsulación donde se tienen en cuenta casi todas las opciones posibles. Sin embargo, para los propósitos de este tutorial, es un código repetitivo. Por lo tanto, con el interés de intentar demostrar esta técnica, y aun así permitir una amplia personalización, implementaré tres propiedades para el diseño de texto, junto con exponer el TextField para un estilo más detallado, que, como ocurre, es la opción número tres.

Si le gusta la idea de encapsulación y desea evitar la revelación de TextField a objetos externos, lo siguiente servirá de guía para escribir más setters.

Añadiremos propiedades para establecer y obtener el nombre de la fuente, el tamaño de la fuente y el color de la fuente. Además, agregaremos un getter (sin setter) para captionField. Las propiedades de estilo requerirán un aplazamiento de representación como lo hicieron las propiedades de diseño, así como la atención prestada a cómo aplicar objetos TextFormat. Para comenzar, declare una propiedad para contener el objeto TextFormat:

A continuación, crearemos ese objeto en el constructor cuando configuremos TextField:

Lo anterior simplemente reemplaza la variable de "formato" temporal de antes con esta propiedad.

A continuación podemos agregar los setters y getters (los colocaré después de todos los otros setters y getters que hemos agregado hasta ahora):

Todos son similares entre sí, la única diferencia son los tipos de datos y las propiedades reales de TextFormat que afectamos. Tenga en cuenta que establecemos el valor en el objeto TextFormat, luego volvemos a aplicar ese objeto como el formato predeterminado para el título TextField. Hacemos esto porque al establecer defaultTextFormat se crea una copia del objeto de formato, por lo tanto, aunque cambiemos nuestra referencia a este objeto, perderemos la referencia al que realmente establece los estilos. Para resumir, necesitamos restablecer el defaultTextFormat  cada vez que eso cambie.

Sin embargo, esto solo establece el estilo para el nuevo texto que ingresa al campo de texto. Para actualizar los estilos en el texto ya presente en el campo, necesitamos usar setTextFormat (). Esto, sin embargo, presenta un problema de representación similar al anterior. Si tuviéramos que configurar las tres propiedades de estilo al mismo tiempo, no necesariamente queremos volver a aplicar el formato tres veces seguidas. Entonces, en cambio, llamamos a stage.invalidate (), que, como recordarán, finalmente activará el evento RENDER para que podamos cambiar la visualización.

Ahora, aquí está la razón por la que queríamos un método onRender separado conectado al evento RENDER, en lugar de simplemente conectar el método de diseño directamente a él. El método de diseño actualizará el título, sin embargo, dado que hemos establecido un formato de texto predeterminado, no es necesario aplicar un formato de texto cada vez que se invoca el diseño. Solo queremos aplicar un formato de texto al texto existente, para que podamos mantenerlo fuera de diseño y ponerlo en onRender:

Por último, tenemos que exponer el campo de texto en sí. Escribe un último getter:

Y para probar todo esto, podemos actualizar el método CoverFlowTest onCoverFlowLoaded:

Ejecútelo, y una vez que todas las imágenes se carguen y el diseño cambie, también verá el cambio de estilo de título. Siéntase libre de eliminar estas líneas en este punto.

The (rather ugly but obvious) change to the caption stylesThe (rather ugly but obvious) change to the caption stylesThe (rather ugly but obvious) change to the caption styles

Paso 36: posición vertical

Habrás notado que el título no se mueve con las Covers cuando ajustan su desplazamiento vertical.

Como usted sabe, agregamos una propiedad vertical Offset unos pocos pasos atrás, y la agregamos al valor predeterminado de 250 al ubicar nuestros objetos Cover. ¿De dónde viene ese 250? Saqué ese número del aire como algo que funciona por ahora.

Ahora podemos aplicar un poco de lógica al posicionamiento vertical de la fila. Podemos simplemente bloquearlo en la parte inferior con un margen predefinido. Entonces, en el diseño, saque la línea que dice cover.y = 250; y reemplázalo con:

En Apple CoverFlow, la parte inferior de la fila de Cubiertas está bloqueada de manera similar en el borde inferior de la vista, pero también cambian el tamaño de las cubiertas en función de la altura de la vista. Vamos a saltear ese poco de lógica, pero sería un ejercicio interesante si tienes fiebre que solo se puede curar con más CoverFlow una vez que este tutorial haya terminado.

Sin embargo, aún no hemos terminado con nuestra lógica. Aplicaremos un margen de píxeles de texto entre el borde inferior del flujo y el título. Por lo tanto, es lógico que deseemos mover el campo de título al ejecutar el diseño, en caso de que la altura haya cambiado. Agregue esta línea después del bucle en el diseño:

Como referencia, todo el método debería verse así:

Pero hay una tarea restante. Si se cambia el ancho o la altura, debemos llamar al layout nuevamente. Esto será tan simple como hacer otra llamada a stage.invalidate en los setters de width height .

Para probar esto, continúe y elimine cualquier otro código de prueba enCoverFlowLoaded y agregue esta línea en su lugar, en CoverFlowTest:

Esto es importante: si no elimina al menos la línea coverFlow.verticalOffset = 50, en realidad negará los efectos del cambio de altura. Para ver más fácilmente el cambio de altura, sugiero que elimine las propiedades de diseño y el estilo de los subtítulos anteriores, si aún no lo ha hecho.

Pruebe la película, y debería ver las carátulas y el dibujo de los subtítulos como antes, y además toda la vista se hace más corta, a la vez que conserva el diseño relativo.


Paso 37: Agregar Drop Off

Para una apariencia más auténtica de CoverFlow, agregaremos un poco de zazz a Cover para que "deje caer" cuanto más lejos esté del centro. Tiene el efecto de un cambio de opacidad, pero, al igual que con los reflejos, no podemos ajustar el alfa, aunque es más fácil. Sin embargo, lograremos el efecto a través de una técnica bastante simple: simplemente usaremos una transformación de color para "teñir" el elemento al color de fondo.

Teñir con ActionScript puro implica un poco de engaño matemático. Es un poco mucho para explicar completamente aquí, pero aquí está el código. Agregue un método set dropOff en la Cover:

En pocas palabras, este código toma un valor numérico de 0 a 1, y lo convertirá en una cantidad para aplicar el tinte: 0 significa que no hay tinte en absoluto, 1 significa un tinte de color sólido completo.

Para hacer eso, tomaremos el color de fondo y lo dividiremos en valores de componentes rojo, verde y azul. Esto utiliza operadores bit a bit, y si son extraños para usted, entonces su tarea es leer sobre ellos. Puede comenzar con Wikipedia para obtener una descripción general y pasar al sitio de Colin Moock y al artículo devnet de Adobe para discusiones específicas de ActionScript.

Crear ColorTransform es otro tema moderadamente complejo que dejaré como proyecto de investigación si lo necesitas. Estos son los valores necesarios para crear un efecto de tinte para el color elegido a la intensidad deseada. Una buena discusión de esta técnica se puede encontrar en flashandmath.com. Vale la pena señalar que otra opción (también discutida en el artículo de la última actualización) es importar la clase fl.motion.Color, que extiende ColorTransform, y usar eso en lugar de un objeto ColorTransform. La clase Color proporciona una conveniencia de tinte con un método setTint, que básicamente nos hace las matemáticas que estamos haciendo aquí. Opté por mostrar la versión de tuercas y tornillos, que por lo tanto no requiere las clases de componentes, que considero una ventaja (no tendrá fl.motion.Color disponible en un proyecto Flex).

Para probar esto, agregue algunas líneas (resaltadas) a nuestro método layout en CoverFlow:

Estas dos líneas adicionales son las mismas, y asegúrese de que si una cubierta no es la central, se aplica un tinte. Cuanto más lejos del centro, más lleno está el tinte. En el transcurso de 10 Covers, se vuelven progresivamente más teñidas (más oscuro, en el caso de un fondo negro), hasta que el décimo de distancia es invisible, quedando completamente teñido del color del fondo.

Continúa y prueba, y deberías ver algo como esto:

Drop-off appliedDrop-off appliedDrop-off applied

Compare eso con esta imagen antes de aplicar dropOff:

Before Drop-off appliedBefore Drop-off appliedBefore Drop-off applied

No voy a entrar en detalles aquí, ya que creo que ya podrá resolverlos usted mismo, pero el número 10 en la ecuación anterior sería un buen candidato para una propiedad para personalización. Trátelo como la propiedad distanceFromCenter y lo establecerá.


Paso 38: establecer el índice actual

Comenzaremos trabajando hacia una pieza interactiva que anime. Para que funcione, necesitaremos la posibilidad de establecer el índice actual. Esto debería ser un método público, ya que podría estar presente otra representación de la lista, y las dos pueden trabajar juntas para tener una selección sincronizada. Piense en cómo funciona iTunes, donde seleccionar una canción en la lista a continuación también enfoca el elemento apropiado en el CoverFlow anterior.

Primero, en el layout(de Cover Flow), elimine la línea que establece el selectedIdex para nosotros. Si recuerdas, fue solo una prueba temporal.

Si bien ya tenemos una propiedad privada para _selectedIndex, debemos agregar un setter y getter. Agregue lo siguiente a CoverFlow:

El getter es típico, pero el setter tiene una lógica extra de limpieza de entrada. En primer lugar, si no hay nada en _covers Vector, entonces probablemente aún no hayamos cargado nuestro documento XML, así que oculte el valor y finalice aquí. De lo contrario, podemos asegurarnos de que el valor no esté fuera de rango antes de almacenarlo en _selectedIndex. And then we can ask the display to redraw with a stage.invalidate().

Podemos probar yendo a CoverFlowTest y estableciendo la propiedad después de que se crea coverFlow.

Y verás que seleccionamos esa cubierta.

The 9th cover selected at startupThe 9th cover selected at startupThe 9th cover selected at startup

Paso 39: haz clic para seleccionar

Con la capacidad de seleccionar una Cover en su lugar, podemos agregar fácilmente la funcionalidad de clic esperada. Si haces clic en una Cover que está del lado, se convierte en la Cover enfocada.

Necesitamos agregar un detector de eventos a cada Cover a medida que los creamos. Aún en CoverFlow, agregue esta línea al bucle en XMLLoad:

Y luego agregue el método onCoverClick.

Es así de simple. Conocemos el índice que queremos seleccionar porque conocemos la Cobertura en la que se hizo clic (a través de currentTarget). Los vectores (y matrices) proporcionan el práctico método indexOf para que podamos obtener el índice de un objeto dado en el Vector. Solo necesitamos alimentar eso a nuestra propiedad selectedIndex, y ahora tenemos una interactividad rudimentaria.

Interactúa con este hito SWF para probarlo.



Haga clic para ver la demostración de hito.


Paso 40: agrega un Tween: el objeto CoverFlow

Pero para hacer que CoverFlow realmente, bueno, fluya, tenemos que intercalar las Covers de una posición a otra. Esto es, por supuesto, la pieza de resistencia y posiblemente por qué estás aquí en primer lugar. Reunir esto significará algunas actualizaciones importantes para nuestro código, pero valdrán la pena.

Podría pensar que lo mejor sería usar un paquete de interpolación de terceros. Y aunque sería más fácil en algunos aspectos, tenemos muchas cosas personalizadas para actualizar a medida que nuestras Cubiertas se animan. De hecho, lanzaremos nuestros propios preestablecidos, lo cual no es tan malo como probablemente crees que es.

Sin embargo, este será un maratón de un paso, así que agárrense.

Primero, en CoverFlow, necesitamos agregar algunas propiedades más. En la parte superior de la clase, junto con el resto de las propiedades, declare lo siguiente:

Sí, son bastantes. Aquí hay una descripción rápida de lo que harán:

  • _tweenDuration: cuánto tiempo, en milisegundos, la animación debería durar.
  • _startTime: cada vez que se inicia la animación, almacenaremos el resultado de getTimer () en esta propiedad.
  • _elapsado: cada vez que actualizamos la animación, almacenaremos el tiempo que ha transcurrido desde el comienzo de la interpolación aquí.
  • _coversLength: la cantidad de elementos en vector _covers
  • _iteración: la variable "i" en nuestros bucles.
  • _iterationCover: una propiedad para almacenar un objeto Cover individual a medida que iteramos sobre _covers.
  • _sortedCovers: una versión duplicada y luego ordenada del vector _covers. Lo usaremos para ordenar profundidades.
  • _unitsFromCenter: hasta qué punto, en número de covers, un objeto de cover dado está desde la posición central.
  • _distanceA y _distanceB: utilizado en la función de clasificación de profundidad, mantiene la distancia desde el centro (en píxeles) de dos objetos de cover dados.

Tenga en cuenta que en la mayoría de los casos, solo estamos declarando una propiedad para fines de optimización. Estamos a punto de escribir un montón de métodos que se llaman repetidamente, varias veces un marco, por lo que estamos tratando de minimizar la sobrecarga de la declaración de variables declarándolos una vez para el objeto, luego reutilizando la ranura cada vez que necesitamos la variable

A continuación, modificaremos el método set selectedIndex para eliminar stage.invalidate () línea, y agregue las siguientes líneas que inician la animación:

Aquí establecemos la propiedad _startTime usando getTimer (), que devuelve el número de milisegundos desde que la película Flash comenzó a reproducirse. Llamamos a una nueva función de diseño, usando _selectedIndex, y finalmente configuramos un listener ENTER_FRAME. Tenga en cuenta que también eliminamos el agente de escucha de eventos antes de agregarlo, en caso de que una animación siga en vigencia cuando se necesite iniciar otra.

A continuación, escribiremos el método determineLayout; puede ir a cualquier parte de la clase que tenga sentido.

Tenga en cuenta que esto es muy similar a nuestro método layout (), solo parece que estamos estableciendo propiedades en los objetos Cover que no existen ... todavía. Lo que estamos tratando de hacer es descubrir nuestros valores de destino basados en la nueva selección. Luego lo usaremos para interpolar nuestra posición a medida que progresa la interpolación. Aparte de eso, y el hecho de que hemos eliminado algunas cosas como el posicionamiento dropOff y _captionField, es la misma lógica que layout ().

Ahora vamos a escribir el método animate, el oyente ENTER_FRAME.

Aquí entramos en algunos de los aspectos prácticos de los tweens programáticos. Primero, encontramos el tiempo que ha transcurrido desde que comenzó la interpolación. Si es más larga que la duración del interpolado, entonces debemos detener la interpolación (eliminando el oyente del evento). De lo contrario, continuamos y recorremos los objetos Cover. En cada uno de ellos, llamamos a un método (que aún no se ha escrito) que realmente realizará las actualizaciones visuales en función de nuestra progresión a través de la interpolación.

La segunda mitad del método se asigna a la clasificación en profundidad. Esto se torna un poco más complicado que en el layout() antiguo simple, ya que no podemos confiar en que las covers estén en cualquier posición en cualquier momento. En su lugar, ordenaremos el Vector de Covers, utilizando el método de sort proporcionado por Vectores (y Matrices). El parámetro pasado a este método es una referencia de función, por lo que depthSort es algo que aún debemos escribir. Tenga en cuenta también que tenemos que copiar el Vector, de lo contrario terminamos jugando con el orden de Cubiertas de izquierda a derecha, y eso no es bueno.

Sin embargo, una vez que el Vector está ordenado, podemos recorrerlo y establecer las profundidades de las Covers con bastante facilidad.

A continuación, escribiremos la función de clasificación depthSort:

La idea detrás de los métodos de clasificación es que reciben dos objetos del vector que se está ordenando, y necesitan devolver un valor que le indique al vector cómo ordenar esos dos elementos. Si el valor de retorno es negativo, el objeto pasado al primer parámetro va primero. Si la devolución es positiva, entonces el segundo parámetro va primero. Si el retorno es 0, entonces no se realiza ningún cambio.

Nuestra lógica de clasificación implica comparar la distancia absoluta desde el centro. Básicamente, cuanto más lejos del centro, más atrás queremos que se establezca.

Eso es todo por los cambios en CoverFlow, pero ahora tenemos algo que hacer en Cover. Como tal, no se moleste en probar la película ahora, ya que obtendrá errores de compilación.


Paso 41: Agregar un Tween: los objetos de Cover

En la clase Cover, comencemos por declarar un grupo de propiedades.

Esto es lo que harán:

  • _startX, _startRotationY, _startZ: Almacena los valores actuales de x, rotationY y z en el momento en que se inicia una interpolación.
  • _endX, _endRotationY, _endZ: Almacena los valores finales calculados de x, rotationY y z cuando termina la interpolación.
  • _coverFlowParent: Almacena una referencia al CoverFlow que contiene esta cover.
  • _centerMargin: Almacena el valor de CoverFlow.centerMargin.
  • _distanceFromCenter: la distancia calculada desde el centro de CoverFlow.

A continuación, escribiremos los setters para endX, endRotationY y endZ.

Estos son los setters típicos, excepto que también almacenan el valor actual de la propiedad relacionada. Necesitaremos ese valor para actualizar la propiedad a medida que avanzamos, de modo que también podemos almacenarla cada vez que recibamos una instrucción y almacenar nuestro valor final calculado. También son inusuales en el sentido de que no son públicos, son internos. Esto solo restringe el acceso a objetos del mismo paquete, por lo que está un poco más protegido que el público, pero aún así el objeto CoverFlow los establece. Tampoco me molesté en escribir getters correspondientes. No los necesitamos, pero no hay daño en hacerlo si lo desea.

Necesitamos escribir ese método updateTween a continuación. Aquí será donde realmente actualizaremos la posición y la rotación:

Las primeras tres líneas se basan en una ecuación de relajación que todavía tenemos que escribir. Veamos eso ahora:

Lo obtuve de las ecuaciones de facilitación de Penner, que se publican bajo una licencia BSD. Como tal, debemos asegurarnos de incluir también el aviso de copyright:

Puede encontrar más información sobre estas ecuaciones en el sitio de Robert Penner.

Entonces, en combinación, ¿qué hace esto? Las ecuaciones de aceleración están configuradas para tomar cuatro argumentos: el tiempo transcurrido de la interpolación, el valor inicial de la propiedad que se va a sumar, la distancia que se va a recorrer en el transcurso de la interpolación completa y finalmente la duración de la interpolación completa. Devuelven un número: el valor interpolado basado en los valores de entrada y la ecuación subyacente. Más allá de eso, me costaría mucho decirte cómo funcionan. Solo agradecer que Penner lanzó estas ecuaciones como de código abierto.

¿Por qué no usar las clases de facilitación como son? Bueno, de esta manera, no tenemos que preocuparnos de distribuir una clase de terceros con el paquete CoverFlow. Todo es autónomo. Será mucho más ordenado.

Para resumir, establecemos la corriente x, rotationY y z en función del tiempo a través de la interpolación y esta ecuación.

Después de eso, se trata de configurar el descenso. Primero nos aseguramos de tener una referencia al padre de CoverFlow. Luego descubrimos qué tan lejos estamos del centro. Luego descubrimos qué debe hacer DropOff en función de ese valor. Esta matemática se pone un poco complicada, pero esencialmente estamos interpolando el valor basado en el hecho de que si una cover está en un destino final, tendrá un drop off del 10% o 30% u 80%, o algún múltiplo de 10 por ciento. La idea es averiguar qué tan cerca estamos de una de esas posiciones.


Paso 42: cambio de tamaño sobre la marcha

Tenemos algo que necesitamos, pero la necesidad no es del todo evidente. Al agregar un tipo de cambio de tamaño de pantalla completa, ambos podemos probar la capacidad de cambiar el tamaño de CoverFlow sobre la marcha, y revelar un poco de comportamiento extraño que necesita abordarse.

Todo esto sucede en CoverFlowTest.as. En el constructor, agregue estas líneas al final:

Y luego agregue el detector de eventos onResize:

Estas pocas líneas son tarifas bastante estándar para crear un archivo SWF que cambia de tamaño de forma fluida con el cambio de tamaño del reproductor. Dentro de los productos de Apple, vemos una vista de CoverFlow dentro de una ventana más grande, pero se puede cambiar de tamaño, p. cambiando el tamaño de la ventana o arrastrando el borde inferior de CoverFlow para cambiar su tamaño dentro de la ventana. Básicamente, estamos simulando esta habilidad.

Probablemente notará que CoverFlow de Apple cambia el tamaño de las imágenes a medida que cambia el tamaño de la vista de CoverFlow. Esto es algo que no recrearemos, pero siéntase libre de hacerlo.


Paso 43: Corregir la perspectiva

A medida que cambia el tamaño de la ventana y mueve las cubiertas, puede notar algo más extraño que definitivamente debe ser atendido. Si estira la ventana bastante ancha, debería ver que las imágenes se alinean correctamente, pero parecen estar sesgadas de manera bastante extraña. Esta es esa cosa "no del todo aparente" de la que teníamos que ocuparnos.

Images don't seem to be rotated quite rightImages don't seem to be rotated quite rightImages don't seem to be rotated quite right

¿Por qué pasó esto? Bueno, de forma predeterminada, la representación 3D de Flash 10 ocurre a través de una única "cámara", y esa cámara está en una ubicación fija, en el centro del escenario. Sin embargo, esa cámara no se mueve si cambia el tamaño del escenario. Así que mientras espera que el punto de fuga permanezca en el centro de su rectángulo de CoverFlow, sin importar qué tan grande, Flash configure el punto de fuga en un punto determinado en función del rectángulo original del escenario, y luego lo deja allí.

Afortunadamente podemos resolverlo. Hay algo que llama un objeto PerspectiveProjection en el objeto de visualización raíz de cualquier película Flash. Puedes pensar en esto como la cámara virtual a través de la cual vemos la representación 3D. Y, naturalmente, podemos moverlo (si vienes de un modo de pensar Papervision3D o Away3D, esta no es una cámara, es la sensación de que tienes cámaras en esos paquetes. No te emociones demasiado; es bastante limitado).

Pero si comenzamos a moverlo, podemos estar afectando la proyección 3D de otros elementos 3D en la Película Flash, no solo nuestro CoverFlow. Bueno, afortunadamente, cada DisplayObject tiene un objeto PerspectiveProjection, que está desactivado por defecto. Pero si lo activamos, los objetos secundarios de DisplayObject se mostrarán de acuerdo con esa PerspectiveProjection, no con la raíz. Por lo tanto, podemos configurar un punto de fuga independiente para nuestro CoverFlow sin preocuparnos de cómo afectará a otras representaciones 3D en la película.

Para hacer esto, vuelva a CoverFlow y busque un lugar en el constructor para configurarlo. He agregado algunas líneas justo después de configurar scrollRect:

Primero tenemos que crear el objeto PerspectiveProjection en el objeto de visualización CoverFlow. Luego configuramos la propiedad projectionCenter en un nuevo punto. La clave es que establecemos esta propiedad en un Punto. No podemos establecer la xey del punto directamente, por alguna razón. Así que creamos un punto y la x se establece en la mitad del ancho de nuestro CoverFlow.

Así que eso lo configura correctamente la primera vez (lo que, por cierto, ayudaría si CoverFlow no fuera tan ancho como el escenario para empezar). A continuación, tenemos que volver a visitar los ajustadores de width height y actualizar el projectionCenter con nuevos valores:

Ahora, cada vez que se cambian las dimensiones de la instancia de CoverFlow, también lo es el punto de fuga. Sigue adelante e inténtalo; si ejecuta la película y cambia el tamaño del escenario, debería ver un buen comportamiento a partir de la representación 3D, sin importar cómo la califique.


Paso 44: Centrar el Título

Las cosas se ven bastante dulces, pero probablemente habrás notado que, al cambiar el tamaño del escenario, la leyenda se mueve verticalmente con las cubiertas, pero no horizontalmente. Esto es, afortunadamente, una solución bastante fácil. En el método de diseño de CoverFlow, necesitamos actualizar la propiedad x de _captionField.

Esto solo asegura que TextField esté centrado dentro del objeto CoverFlow.


Paso 45: habilite doble clic en la portada

Casi terminado. Ahora nos gustaría habilitar la capacidad de hacer clic en la portada y enviar un evento. Además, nos gustaría poder recuperar un XML de data de una portada dado, como lo proporciona el documento XML original.

Comenzaremos con la portada. Primero, habilitamos la acción de doble clic en Cover, en el constructor:

Estas dos nuevas líneas simplemente permiten la capacidad de recibir clics dobles del usuario. La línea mouseChildren no habilita necesariamente clics dobles, pero es necesario en nuestro caso, de lo contrario, el Loader interceptará los clics dobles.


Paso 46: Haga algo con doble clic en la clase de prueba

Para escuchar dos clics, vuelva a CoverFlowTest y escuche el evento DOUBLE_CLICK. Debido al burbujeo del evento, un evento enviado por un objeto Cover será escuchable desde CoverFlowTest:

Después de agregar el detector de eventos, necesitamos escribirlo:

Tenga en cuenta que e.target será el objeto de portada en el que se hizo doble clic. Esto nos da un acceso fácil a los datos que se almacenan dentro del objeto Cover en cuestión. En este caso particular, tenemos un <link> anidado (recuerde, los datos son solo el nodo XML para ese objeto Cover - y podemos poner todo lo que queramos dentro de ese nodo). Entonces, si ejecuta la película tal como está ahora, verá el nodo XML completo en el panel de Salida. Si queremos hacer algo más significativo, podemos hacer esto:

Recuerde que puede almacenar cualquier información que desee en el XML y acceder a ella nodo por nodo a través de la propiedad de datos de cada Cover. Hay más información sobre eso en mi tutorial XML.


Paso 47: Una última cosa ...

Puede haberlo olvidado, pero yo no. Rechazamos un método clearContents () hace un tiempo, pero nunca lo completamos. Bueno, ahora que ya hemos terminado, podemos completarlo. El objetivo de este método es solo preservar la instancia de CoverFlow y su configuración de diseño y estilo, y eliminar las Cubiertas y cualquier información asociada. Este también sería un lugar para asegurar que hemos limpiado a los oyentes de eventos perdidos. Entonces el objeto CoverFlow podría cargar un archivo XML diferente y comenzar de nuevo con nuevos datos. Ubique ese método vacío clearContents () en CoverFlow y agregue lo siguiente:

Estoy siendo un poco cauteloso, pero solo quiero asegurarme de que cualquier evento que alguna vez haya estado conectado se borre. Sin embargo, al final de la función, los objetos individuales de Cover se limpian, se eliminan de la pantalla y finalmente se eliminan junto con el manejador ENTER_FRAME, en caso de que hayamos llamado esto en el medio de una animación. Probémoslo agregando algún código a CoverFlowTest:

Esta es una prueba bastante inútil, pero fácil de ejecutar. Simplemente espera un segundo después de que se cargue CoverFlow, luego carga el archivo XML nuevamente. Esto en realidad entra en una secuencia interminable, pero debería mostrar que el método clearContents () funciona bien.


Tiempo de salida

Este no fue un proyecto pequeño. Dependiendo de su experiencia con Flash y ActionScript, probablemente haya aprendido una o dos cosas, aparte de cómo recrear la vista de CoverFlow en Flash. Algunos de los temas más avanzados incluyen BitmapData, interpolación manual, clasificación en profundidad 3D y gestión de perspectiva 3D.

Gracias por quedarte conmigo hasta el final. Espero haberle mostrado que con un poco de esfuerzo, puede crear contenido 3D atractivo en Flash 10 sin la necesidad de un paquete de terceros para manejar la representación 3D. El 3D nativo de Flash 10 no es adecuado para todo, pero para ciertos proyectos, a menudo es mucho más fácil trabajar con él, sin mencionar que es mucho más pequeño en términos de tamaño de archivo del archivo SWF.

Advertisement
Did you find this post useful?
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.