SpringAnimation

Applicazione di JavaFX presa dagli esempi forniti con Netbeans 6.5 (noto per inciso che al momento la beta di Netbeans 6.7 non include il supporto per JavaFX e la cosa mi preoccupa un po', dato che da quando Sun é stata inglobata da Oracle girano strane voci sul futuro di questo linguaggio).

Lo scopo é quello di mostrare come estendere la classe SimpleInterpolator per ottenere animazioni custom.

Per far ciò si crea un applicazione in cui una palla viene fatta rimbalzare con un moto che simula quello tipico di una molla o il rimbalzo. Si può vedere il risultato in questa pagina.


Interpolatore

Il punto chiave del nostro interpolatore é che ridefinisce la funzione curve() per determinare il valore della curva risultante come fa più comodo a noi, vediamoci allora il codice del file SpringInterpolator.fx, sostanzialmente uguale a quello che si trova nell'esempio di Netbeans:
package custominterpolator;

import javafx.animation.SimpleInterpolator;
import java.lang.Math;

public class SpringInterpolator extends SimpleInterpolator {
public-init var amplitude = 1.0;
public-init var mass = 0.058;
public-init var stiffness = 12.0;
public-init var phase = 0.0;
public-init var bounce = false;

def pulsation = Math.sqrt(stiffness / mass);

override public function curve(t: Number) : Number {
var t2 = -Math.cos(pulsation*t+phase+Math.PI) * (1-t) * amplitude ;
return if(bounce) 1-Math.abs(t2) else 1-t2;
}
}

A parte i valori utilizzati, e la sinusoidale che non gode di gran successo presso il folto pubblico, il senso della cosa é abbastanza chiaro.

Timeline

Per rendermi il codice più leggibile, ho creato una classe intermedia, Animator, che si occupa di gestire le timeline per i due moti che vogliamo applicare alla nostra palla: un moto a molla, e uno a rimbalzo. Vediamo quindi il codice di Animator.fx:
package custominterpolator;

import javafx.animation.Timeline;

public class Animator {
public var height : Number = 0;
public var y : Number = 0;

def springInterpolator = SpringInterpolator { bounce: false };
def bounceInterpolator = SpringInterpolator { bounce: true };

var sTimeline = Timeline {
keyFrames: [
at(1s) { y => -120 },
at(2.5s) { y => height tween springInterpolator}
]
};

var bTimeline = Timeline {
keyFrames: [
at(1s) { y => -120 },
at(2.5s) { y => height tween bounceInterpolator}
]
};

public function playSpring() : Void {
sTimeline.time = 0s;
sTimeline.playFromStart();
}

public function playBounce() : Void {
bTimeline.time = 0s;
bTimeline.playFromStart();
}
}

Abbiamo due funzioni pubbliche, playSpring() e playBounce() che generano il moto dell'oggetto (stile molla e rimbalzo, rispettivamente) mettendo il risultato nella variabile y con lo scorrere del tempo nella timeline di competenza.

Quelle che deve fare la classe che usa l'Animator, é fornire l'altezza della finestra in input e utilizzare come output l'y risultante quando viene fatta partire l'animazione, per mostrare il movimento dell'oggetto.

Main

Il main.fx della nostra applicazione si occuperà quindi di mettere gli oggetti in scena e di animare la palla collegandola alla y della classe Animator:
package custominterpolator;

import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.scene.control.Button;
import javafx.scene.text.Text;
import javafx.scene.shape.Circle;
import javafx.scene.effect.DropShadow;

var width : Number = bind scene.width;
var height : Number = bind scene.height;

var description = Text {
content: "Spring vs. Bounce Interpolator"
fill: Color.WHITE
y: 20, translateX: bind (width-150)/2
}

def line = Line {
startX: 0, startY: bind height - 150,
endX: bind width, endY: bind height - 150
stroke: Color.GRAY
}

def btnSpring = Button {
translateX: 10, translateY: bind height - 40, width: 60
text: "Spring"
action: function() { animator.playSpring(); }
}

def btnBounce = Button {
translateX: 10, translateY: bind height - 70, width: 60
text: "Bounce"
action: function() { animator.playBounce(); }
}

def ball = Circle {
centerX: bind width/2, centerY: bind height - 150, radius: 30
translateY: bind animator.y;
fill: Color.GREEN;
effect: DropShadow { offsetX: 3, offsetY: 3 }
}

def animator = Animator {
height: height;
}

def scene : Scene = Scene {
width: 240, height: 320
fill: Color.PURPLE
content: [ description, line, btnSpring, btnBounce, ball ]
}

def stage = Stage {
title: "Spring Animation"
scene: scene
}

Nessun commento:

Posta un commento