Mostrando entradas con la etiqueta timeline. Mostrar todas las entradas
Mostrando entradas con la etiqueta timeline. Mostrar todas las entradas

domingo, 29 de marzo de 2009

Por qué incluir canSkip:true en los timelines


Este video muestra la diferencia en el rendimiento de las animaciones gráficas con y sin canSkip:true. Es recomendable incluir este parametro en los KeyFrame que alteran valores (values:) asociados a animaciones y efectos (cambio de posiciones, variación de sombras, difuminado, etc... cualquier efecto o movimiento gradual).

Una persona del grupo JavafxProgramming creo una aplicación Flex que luego replicó en JavaFX. Su problema era el rendimiento de JavaFX, que hacia su aplicación inutilizable. Pidió consejos al foro, pero nada funcionó... hasta que incluyó el canSkip:true en los timelines... recién ahí la aplicación funcionó como debía.

Quizás sería bueno que Sun incluyera el canSkip con true como valor por defecto. Esto puede terminar desprestigiando la plataforma.

En todo caso, sería bueno que todos comparta este detalle o lo tengan muy presente al momento de usar los timelines o de tener problemas con el rendimiento.

jueves, 19 de marzo de 2009

Timelines de duración variable y el at(..)


Uno de los inconvenientes de trabajar en un ambientea tan nuevo como el JavaFX script, es que no abundan las fuentes de información.

Una de las cosas que no tenía claras (seguramente porque no investigué lo suficiente, o no miré mejor los ejemplos), era cómo se hace que un timeline tenga duración variable.

Bueno, ahora descubrí que diablos es el at(..) en los timeline y cómo se hace para que la duración sea variable.

Primera cosa, cuando uno usa at(), en realidad está usando una "abreviatura" en la manera de definir un timeline. La idea es que sólo sea una opción breve para escribir el código, pero por lo mismo, no es la forma más "potente".

Tomemos este ejemplo:

var x:Number;
Timeline {
repeatCount: 1
keyFrames: [
at (0s) { x => 0.0}
at (3s) { x => 100.0}
]
}.play();

En él, definimos una variable x, que al segundo 0s -de iniciado el timeline- asume un valor de 0.0, luego, x asumirá gradualmente los valores entre 0.0 y 100.0, alcanzando el máximo valor (100.0) en 3s segundos.

El asunto que me faltaba comprender era... ¿como cambiaba el tiempo en at(3s)?, porque no acepta ni variables ni bind dentro de los paréntesis.

Pues resulta que con at(..) no lo vas a conseguir (por lo menos eso entiendo en este momento), la forma correcta y potente de usar las lineas de tiempo es esta:


var dur=3s;
var x:Number;
Timeline {
repeatCount: 1
keyFrames : [
KeyFrame {
time : 0s
values: [x=>0]
},
KeyFrame {
time: bind dur
values: [x=>100]
}
]
}.play();

De hecho, esta forma de definir un timeline permite anidar timelines o agregar un action: en cada KeyFrame, de manera que una acción sea ejecutada cada vez que una etapa de la animación o transición se completa.


var dur=3s;
var dur2=5s;
var x:Number;
Timeline {
repeatCount: 1
keyFrames : [
KeyFrame {
time : 0s
values: [x=>0]
},
KeyFrame {
time: bind dur
canSkip: true
values: [x=>50]
action: function() {
println("completo etapa 1");
};
},
KeyFrame {
time: bind dur2
canSkip: true
values: [x=>200]
action: function() {
println("completo etapa 2");
};
}
]
}.play();

martes, 17 de marzo de 2009

Un pequeño bug en el Timeline y una solución


Estaba haciendo unas pruebas y de vez en cuando notaba un comportamiento extraño, finalmente descubrí que el problema se origina en la reutilización de un Timeline, que estaba definido como:

var timeline= Timeline {
repeatCount: 1
keyFrames: [
at (0s) { currentX => startX }
at (0.5s) { currentX => posX }
]
};

Aunque no es el ideal que haya bugs, es algo habitual en una plataforma nueva (he leído que Adobe Flex tiene demasiados bugs, así que -aunque es consuelo de tontos- me voy a conformar con este pequeño error en JavaFX).

La solución fue tan simple como... no reutilizar el Timeline una y otra vez, sino que recrearlo cuando lo voy a usar:

var timeline:Timeline;

...

timeline = Timeline {
repeatCount: 1
keyFrames: [
at (0s) { currentX => startX }
at (0.5s) { currentX => posX }
]
};
timeline.playFromStart();

El problema específico de mi programa, era que a veces asignaba currentX igual a 0 en at (0s), aunque startX no tenia ese valor.

En todo caso, no es un problema que ocurra con frecuencia (supongo que debe ser un pequeño error el compilador), así que no es necesario hacer esto cada vez que utilicen un Timeline... pero si notan algo extraño como que un elemento asociado de alguna manera a un timeline salta de manera no esperada, intenten esto (por lo menos hasta que el bug sea corregido).

sábado, 17 de enero de 2009

JavaFX: Textos que giran y cambian de tamaño


Este ejercicio lo desarrollé para crear una clase que contiene sus propios timeline y que modifica el tamaño del font mientras gira el mensaje. Quizas lo más interesante de este ejercicio es la manera en que se modifica el tamaño del font desde el timeline. Otra cosa interesante es cómo el programa obtiene las dimensiones del texto para pintar el area que lo contiene (para ello es necesario que el texto sea definido en una variable y luego incluida en el contenido).




package experimenttext;

import javafx.stage.Stage;
import javafx.animation.*;
import javafx.scene.*;
import javafx.scene.text.*;
import javafx.scene.shape.Rectangle;
import javafx.scene.paint.Color;


class myObject extends CustomNode {

// texto en el mensaje
public var message = "Nada aun";
// color del mensaje
public var color = Color.BLACK;
// color borde
public var stroke = Color.BLACK;
// color fondo
public var fill = Color.WHITE;
// tamaño maximo del font
public var max_size = 64.0;

// angulo de la rotacion
var angle: Number;
// tamaño del font
var size: Number;
// font del texto
var varfont = function(size:Number):Font {
Font{
name:"Verdana"
oblique:true;
size:size;
}
};


// mensaje desplegado
var geomText = Text {
content: bind message
font : bind varfont(size)
fill: bind color
textOrigin: TextOrigin.TOP
};

// rotacion del mensaje (un giro en 2 segundos)
var rotatetimeline = Timeline {
repeatCount: Timeline.INDEFINITE
keyFrames: [
at(0s) { angle => 0 }
at(2s) { angle => 360 }
]
};

// variacion del tamaño del font (de 12 a MAX_SIZE en 1 segundos)
var fontsize = Timeline {
repeatCount: Timeline.INDEFINITE
autoReverse: true
keyFrames: [
at(0s) { size => 12 }
at(0.3s) { size => max_size tween Interpolator.EASEBOTH}
]
};


// mensaje desplegado (compuesto por texto y rectangulo a su alrededor)
override protected function create():Node {

// inicia los timelines de la instancia
rotatetimeline.play();
fontsize.play();

Group {
content: [
// rectangulo
Rectangle {
width: bind geomText.boundsInLocal.width
height: bind geomText.boundsInLocal.height
stroke: bind stroke
fill: bind fill
},
// mensaje
geomText
]
// rotacion del conjunto (rect+mensaje)
rotate:bind angle
}
};

};

// primera instancia de mensaje
var msg=myObject{
translateX: 200
translateY: 150
message: "Primer mensaje"
};
// segunda instancia de mensaje
var msg2=myObject{
translateX: 300
translateY: 250
message: "Segundo mensaje"
color: Color.RED
stroke: Color.GREEN
fill: Color.YELLOW
max_size: 40.0
};
// tercera instancia de mensaje
var msg3=myObject{
translateX: 100
translateY: 350
message: "Tercer mensaje"
color: Color.WHITE
stroke: Color.BLUE
fill: Color.BLUE
max_size: 90.0
};

// aplicacion
Stage {
title: "Application title"
width: 800
height: 600
scene: Scene {
content: [
msg,
msg2,
msg3
]
}
}

sábado, 6 de diciembre de 2008

JavaFX: La rueda de la fortuna


Este ejemplo genera una "rueda" de colores de distinta intensidad y que gira... el ejemplo original viene incluido con Netbeans 6.5, yo sólo le agregué el giro. Pueden modificar la velocidad del giro cambiando el tiempo en el Timeline (0.5s, o medio segundo para una vuelta completa).

Básicamente, lo único que agregué fue un rotate: bind angle; asociado a la rueda ya generada incluida en la escena (Scene) y una "linea de tiempo" (Timeline) que incrementa el ángulo entre 0º y 360º, en el tiempo especificado. Una vez que llega a 360º, comienza por el ángulo 0º. Así queda la impresión que la ruega gira indefinidamente.



package color;

import javafx.scene.Group;
import javafx.scene.CustomNode;
import javafx.scene.Node;
import javafx.scene.paint.Color;
import javafx.scene.shape.Arc;
import javafx.scene.shape.ArcType;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.animation.Timeline;
import javafx.lang.FX;

class ColorWheelNode extends CustomNode {

var segments : Number = 12;
var steps : Number = 6;
var radius : Number = 95;
var stripes : Arc[];

override function create() : Node {
return Group {
content: bind stripes
};
}

init {
// Set radius
var r = radius;
var rStep = radius / steps;
var colors : Color[];
for( i in [1..
segments + 1] ) {
insert Color.rgb( 255, 255, 0 ) into colors;
}
for( i in [0..
steps - 1] ) {
colors[1] = Color.rgb( 255 - ( 255 / steps * i ), 255 - ( 255 / steps * i ), 0 );
colors[2] = Color.rgb( 255 - ( 255 / steps ) * i, ( 255 / 1.5 ) - (( 255 / 1.5 ) / steps ) * i, 0 );
colors[3] = Color.rgb( 255 - ( 255 / steps ) * i, ( 255 / 2 ) - ( ( 255 / 2 ) / steps ) * i, 0 );
colors[4] = Color.rgb( 255 - ( 255 / steps ) * i, ( 255 / 2.5 ) - (( 255 / 2.5 ) / steps ) * i, 0 );
colors[5] = Color.rgb( 255 - ( 255 / steps ) * i, 0, 0 );
colors[6] = Color.rgb( 255 - ( 255 / steps ) * i, 0, ( 255 / 2 ) - (( 255 / 2 ) / steps ) * i );
colors[7] = Color.rgb( 255 - ( 255 / steps ) * i, 0, 255 - ( 255 / steps ) * i );
colors[8] = Color.rgb(( 255 / 2 ) - (( 255 / 2 ) / steps ) * i, 0, 255 - ( 255 / steps ) * i );
colors[9] = Color.rgb( 0, 0, 255 - ( 255 / steps ) * i );
colors[10] = Color.rgb( 0, 255 - ( 255 / steps ) * i, ( 255 / 2.5 ) - (( 255 / 2.5 ) / steps ) * i );
colors[11] = Color.rgb( 0 , 255 - ( 255 / steps ) * i, 0 );
colors[12] = Color.rgb(( 255 / 2 ) - (( 255 / 2 ) / steps ) * i, 255 - ( 255 / steps ) * i, 0 );
for( j in [1..segments] ) {
var c = colors[
j.intValue()];
insert Arc {
centerX: 100,
centerY: 100
radiusX: r ,
radiusY: r
startAngle: 360 / segments * ( segments - j - 0.5)
length: 360 / segments
fill: c
type: ArcType.ROUND
} into stripes;
}
r -= rStep;
}
}
}


var angle:Integer;

Timeline {
repeatCount: Timeline.INDEFINITE
autoReverse: false
keyFrames: [
at (0s) {angle => 0},
at (0.5s) {angle => 360},
]
}.play();



Stage {
scene: Scene {
fill: Color.GRAY
content: ColorWheelNode {
rotate: bind angle;
}
}

width: 216
height: 232
title: "Color Wheel"
}