JavaFX. Comment reprendre?


J'écris une chronologie pour le texte:

Utilisation:

Text text = new Text();
......
group.getChildren().addAll(text);
    root.getChildren().addAll(group);

tl.play();

Cela fonctionne bien. Si je veux mettre en pause et continuer l'animation, tl.pause(); et tl.play(); peuvent le faire.

Maintenant, je veux faire de l'animation recommencer depuis le début et je l'utilise tl.stop(); et tl.playFromStart();, Mais l'effet de cette combinaison est que comme l'effet de tl.pause(); & tl.play();.

Ma question est, pourquoi tl.playFromStart (); ne fonctionne pas correctement et comment reprendre l'animation?

Author: SuperDelta, 2015-05-27

1 answers

Comment fonctionne Timeline

Un Timeline représente une période de temps au cours de laquelle une animation est exécutée. Le Timeline se compose d'une collection de KeyFrames. Chaque KeyFrame

  • doit spécifier un point dans le temps sur la Timeline (la Duration objet que vous transmettez)
  • peut éventuellement spécifier également une collection de KeyValues , qui comprendre WritableValue s (par exemple, Property s) et la cible les valeurs de ces WritableValue s à ce moment
  • peut éventuellement spécifier une action à effectuer, sous la forme EventHandler<ActionEvent>

Le Timeline a un currentTime propriété, qui (bien sûr) progresse à mesure que le temps s'écoule pendant la lecture du Timeline. pause() arrêtera la progression du currentTime, le laissant fixé à sa valeur actuelle. stop() arrête la progression du currentTime et remet le currentTime à zéro.

Si le Timeline a KeyFrames qui spécifient KeyValues, alors que la currentTime change, la WritableValues spécifié dans le KeyValues avoir des valeurs en fonction de la currentTime. (Plus précisément, si les WritableValue sont interpolables, la valeur sera interpolée entre deux KeyFramesadjacents spécifiant KeyValue s pour ce WritableValue. Sinon, la valeur sera simplement définie sur le" plus récent " KeyFrame en spécifiant une valeur pour ce WritableValue.)

Si le Timeline a KeyFrame s qui spécifient des actions (EventHandler<ActionEvent> s), alors que le currentTime progresse au-delà du temps spécifié par que KeyFrame, l'action est invoquée.

Pourquoi ton code ne fonctionne pas avec stop() ou playFromStart()

Dans votre cas, votre KeyFrame spécifie une action, qui ajoute de nouvelles transformations à la liste des transformations du nœud. Notez que cela ne dépend pas du currentTime du tout, sauf que chaque fois que le currentTime atteint 0.04 seconds, une nouvelle transformation est ajoutée (de plus, quelle que soit la méthode shiftAndScale dont vous n'avez pas montré l'implémentation). Ainsi, si vous stop() la timeline, le currentTime est remis à zéro, mais rien n'arrive au nœud à cause de cela. (En effet, le currentTime ne varie qu'entre 0 et 0.04 secondes de toute façon.)

D'Autres problèmes avec votre code

Il y a un problème avec votre code, en ce sens que vous avez une fuite de mémoire. Un Node maintient un ObservableListde Transform s. Vous ajoutez à cette liste (assez fréquemment), mais ne supprimez jamais rien. Le Node est assez intelligent: il conserve une matrice cachée qui est l'effet net de toutes les transformations; lorsque vous ajoutez un nouvelle transformation il le stocke dans la liste, puis met à jour la matrice "net" avec une simple multiplication matricielle. Par conséquent, vous ne verrez aucun problème de performance de calcul ici: il évolue bien de ce point de vue. Cependant, il stocke toutes les transformations individuelles (car, par exemple, il prend en charge leur suppression plus tard), et donc si vous laissez cela fonctionner assez longtemps, vous finirez par manquer de mémoire.

Le seul autre problème (peut-être mineur) avec votre code est que vous faites un beaucoup d'arithmétique à virgule flottante lorsque vous combinez tous ces transformations. Toute erreur d'arrondi finira par s'accumuler. Vous devriez essayer de trouver une technique qui évite l'accumulation des erreurs d'arrondi.

Des Moyens pour corriger votre code

Pour résoudre ce problème, vous avez deux options:

Si l'animation est "naturellement cyclique" (ce qui signifie qu'elle revient à son état de départ après un certain temps fixe, comme une rotation), alors définissez simplement le Timeline en termes de ce naturel durée. En utilisant simplement votre rotation comme exemple simple, vous pouvez faire:

double secondsPerCompleteCycle = (360.0 / 0.75) * 0.04 ;
Rotate rotation = new Rotate(0, new Point3D(1, 0, 0));
group.getTransforms().add(rotation);
Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(secondsPerCompleteCycle), 
    new KeyValue(rotation.angleProperty(), 360, Interpolator.LINEAR)));
timeline.setCycleCount(Animation.INDEFINITE);
timeline.play();

Maintenant, timeline.stop() mettra le currentTime à zéro, ce qui aura pour effet de remettre l'angle de rotation à sa valeur initiale (également zéro).

Si l'animation n'est pas naturellement répétitive, j'utiliserais un compteur (de type entier) pour garder une trace de la "trame actuelle" dans les unités de temps que vous choisissez, puis lier les valeurs de la transformation au compteur. En utilisant le même exemple, vous pourriez faire

double degreesPerFrame = 0.75 ;
LongProperty frameCount = new SimpleLongProperty();
Rotate rotation = new Rotate(0, new Point3D(1, 0, 0));
group.getTransforms().add(rotation);
rotation.angleProperty().bind(frameCount.multiply(degreesPerFrame));

Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(0.04), e -> 
    frameCount.set(frameCount.get() + 1)));
timeline.setCycleCount(Animation.INDEFINITE);
timeline.play();

// to reset to the beginning:
timeline.stop();
frameCount.set(0L);

Vous pouvez également envisager d'utiliser un AnimationTimer, selon vos besoins exacts. Je voudrais essayer l'une de ces techniques d'abord, cependant.

Dans votre cas, l'algèbre devient assez complexe (trop complexe, pour moi en tout cas). Chaque action ajoute trois transformations au nœud; une translation, une échelle et une rotation autour de l'axe des abscisses. Les représentations matricielles 4x4 de ceux-ci sont:

1 0 0 tx
0 1 0 ty
0 0 1 0
0 0 0 1

Pour la traduction,

sx  0 0 0 
 0 sy 0 0
 0  0 1 0
 0  0 0 1

Pour l'échelle, et

1      0       0 0
0 cos(t) -sin(t) 0
0 sin(t)  cos(t) 0
0      0       0 1

Pour la rotation.

Bien qu'il ne soit pas trop difficile de calculer l'effet net de ces trois (il suffit de les multiplier ensemble), calculer la matrice nette que vous obtenez en les appliquant un nombre arbitraire de fois est au-delà de moi (peut-être...). De plus, le montant que vous traduisez dans la direction x change, ce qui le rend à peu près impossible.

Donc, l'autre façon d'aborder cela est de définir une seule transformation et de l'appliquer au nœud, puis de la modifier sur chaque événement. Cela ressemblerait à

Affine transform = new Affine() ; // creates identity transform
node.getTransforms().add(transform);

Timeline timeline = new Timeline(Duration.seconds(0.04), event -> {
    double shiftX = ... ;
    double shiftY = ... ;
    double scaleX = ... ;
    double scaleY = ... ;
    double angle = 0.75 ;
    Affine change = new Affine();
    change.append(new Translate(shiftX, shiftY));
    change.append(new Scale(scaleX, scaleY));
    change.append(new Rotate(angle, new Point3D(1, 0, 0)));
    transform.append(change);
});

timeline.setCycleCount(Animation.INDEFINITE);
timeline.play();

Comme décrit ci-dessus, stop() et pause() auront (presque) le même effet. (La seule différence est le temps de la première nouvelle mise à jour lorsque vous jouez à nouveau, pour stop() il sera de 0,04 seconde, pour pause() il sera inférieur - tout ce qui est resté jusqu'à la prochaine mise à jour quand il a été mis en pause.) Mais pour "réinitialiser" l'animation, il suffit de faire

timeline.stop();
transform.setToIdentity(); // resets to beginning

Notez qu'en utilisant cette technique, le nœud n'a qu'une seule transformation appliquée; nous mettons simplement à jour cela transformer à mesure que nous progressons. Les erreurs d'arrondi s'accumulent toujours, mais au moins l'algèbre est viable :).

 2
Author: James_D, 2015-05-28 18:09:53