All weekends I was trying to find the way to pause animation at any(!) frame and get interpolated values of animated objects at this particular frame.
I've found the way to pause timeline but still I have terrible problem: I can't get correct interpolated values.
It's possible to pause at any time using newly inserted frame with action function which performs pause() on timeline.
But interpolated values will be returned for pausedFrame -1. For example you make pause at 1000ms and see that your interpolated values == 0. You make pause() at 2000 and get values for 1000ms. Strange thing.
This is a code:
***************************************************
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.shape.Rectangle;
import javafx.scene.paint.Color;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.animation.Interpolator;
import javafx.ext.swing.SwingTextField;
import javafx.ext.swing.SwingButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.HBox;
/**
* @author SSheipak
*/
//Movable rectangle
var xOff = 0.0;
var yOff = 0.0;
def rectangle: Rectangle = Rectangle{
x: bind 50.0 + xOff
y: bind 50.0 + yOff
width: 50
height: 50
fill: Color.RED
}
//Read frame value
def inputTimeField: SwingTextField = SwingTextField{
width: 100
height: 30
columns: 10
}
def button: SwingButton = SwingButton{
text: "Go to frame"
onMouseClicked: function(evt: MouseEvent): Void{
//get Duration
var time: Duration = Duration.valueOf( Float.valueOf(inputTimeField.text) );
//Prepare new KeyFrame with pause() action
var newKeyFrame: KeyFrame = createKeyFrameWithPause(time);//+1ms);
//find place for newKeyFrame
var i: Integer = 0;
for(keyFrame in timeline.keyFrames){
if(isAtTheEndOfSequence(i, timeline.keyFrames.size())
or isCanBeInserted(newKeyFrame, keyFrame, timeline.keyFrames[i+1])){
//<debug>
println("---------------------------------------------");
if( isAtTheEndOfSequence(i, timeline.keyFrames.size()) ){
println("insert {newKeyFrame.time} after {timeline.keyFrames[i].time} at the end of the sequence");
}else {
println("insert {newKeyFrame.time} after {timeline.keyFrames[i].time} and before {timeline.keyFrames[i+1].time}");
}
//</debug>
insert newKeyFrame after timeline.keyFrames[i];
break;
}//
i++;
}//for
timeline.time = time;
timeline.play();
println("At {newKeyFrame.time} rectangle has x={rectangle.x} and y={rectangle.y}, xOff={xOff},yOff={yOff}");
printKeyFramesTime();
println("");
}
}
//Container for Swing components
def hBox: HBox = HBox{
content: [inputTimeField, button]
}
//Frames for timeline
var keyFrames: KeyFrame[] = [
KeyFrame{
time: 0ms
values: [xOff => 0.0, yOff => 0.0]
},
KeyFrame{
time: 6000ms
values: [xOff => 400.0 tween Interpolator.LINEAR, yOff => 400.0 tween Interpolator.LINEAR]
}
];
//Timeline
def timeline: Timeline = Timeline{
time: 0ms
keyFrames: keyFrames
}
Stage {
title: "Halt Timeline"
width: 600
height: 600
scene: Scene {
content: [hBox, rectangle]
}
}
/**Checks is current index points to the last element of Sequence
@param position is an index to test
@param seqSize is a value of someSeq.size()
@return true if position points to the last element of the sequence else returns false
*/
function isAtTheEndOfSequence(position: Integer, seqSize: Integer): Boolean{
var isAt: Boolean = false;
if( (position + 1) >= seqSize){
isAt = true;
}
return isAt;
}
/**Find ordered place for KeyFrame in KeyFrame sequence using keyFrame.time
@param testing is a KeyFrame you want to insert into seq
@param current is a current KeyFrame of a seq
@param next is a next KeyFrame of q seq
@return true if testing.time >= current.time and <= next.time else returns false
*/
function isCanBeInserted(testing: KeyFrame, current: KeyFrame, next: KeyFrame): Boolean{
var isCan = false;
var testingTime = testing.time;
var currentTime = current.time;
var nextTime = next.time;
if(testingTime >= currentTime and testingTime <= nextTime){
isCan = true;
}
return isCan;
}
/**Print all frames of timeline in format [keyFrame.time]*/
function printKeyFramesTime():Void{
var str: String = "KeyFrames -> ";
for(kf in timeline.keyFrames){
str+="[{kf.time}] ";
}
println(str);
}
function createKeyFrameWithPause(time: Duration): KeyFrame{
var keyFrame: KeyFrame = KeyFrame{
time: time
action: function(): Void{
timeline.pause();
}
};
return keyFrame;
}
output:
*****************************************
---------------------------------------------
insert 3000ms after 0ms and before 6000ms
At 3000ms rectangle has x=50.0 and y=50.0, xOff=0.0,yOff=0.0
KeyFrames -> [0ms] [3000ms] [6000ms]
---------------------------------------------
insert 4000ms after 3000ms and before 6000ms
At 4000ms rectangle has x=250.0 and y=250.0, xOff=200.0,yOff=200.0
KeyFrames -> [0ms] [3000ms] [4000ms] [6000ms]
---------------------------------------------
insert 5000ms after 4000ms and before 6000ms
At 5000ms rectangle has x=316.6667 and y=316.6667, xOff=266.6667,yOff=266.6667
KeyFrames -> [0ms] [3000ms] [4000ms] [5000ms] [6000ms]
---------------------------------------------
insert 6000ms after 5000ms and before 6000ms
At 6000ms rectangle has x=383.3333 and y=383.3333, xOff=333.3333,yOff=333.3333
KeyFrames -> [0ms] [3000ms] [4000ms] [5000ms] [6000ms] [6000ms]
---------------------------------------------
insert 6000ms after 5000ms and before 6000ms
At 6000ms rectangle has x=50.0 and y=50.0, xOff=0.0,yOff=0.0
KeyFrames -> [0ms] [3000ms] [4000ms] [5000ms] [6000ms] [6000ms] [6000ms]
---------------------------------------------
insert 6000ms after 5000ms and before 6000ms
At 6000ms rectangle has x=50.0 and y=50.0, xOff=0.0,yOff=0.0
KeyFrames -> [0ms] [3000ms] [4000ms] [5000ms] [6000ms] [6000ms] [6000ms] [6000ms]
---------------------------------------------
insert 1000ms after 0ms and before 3000ms
At 1000ms rectangle has x=50.0 and y=50.0, xOff=0.0,yOff=0.0
KeyFrames -> [0ms] [1000ms] [3000ms] [4000ms] [5000ms] [6000ms] [6000ms] [6000ms] [6000ms]
---------------------------------------------
insert 1000ms after 0ms and before 1000ms
At 1000ms rectangle has x=116.66667 and y=116.66667, xOff=66.66667,yOff=66.66667
KeyFrames -> [0ms] [1000ms] [1000ms] [3000ms] [4000ms] [5000ms] [6000ms] [6000ms] [6000ms] [6000ms]
---------------------------------------------
insert 3000ms after 1000ms and before 3000ms
At 3000ms rectangle has x=117.53333 and y=117.53333, xOff=67.53333,yOff=67.53333
KeyFrames -> [0ms] [1000ms] [1000ms] [3000ms] [3000ms] [4000ms] [5000ms] [6000ms] [6000ms] [6000ms] [6000ms]
---------------------------------------------
insert 6000ms after 5000ms and before 6000ms
At 6000ms rectangle has x=250.0 and y=250.0, xOff=200.0,yOff=200.0
KeyFrames -> [0ms] [1000ms] [1000ms] [3000ms] [3000ms] [4000ms] [5000ms] [6000ms] [6000ms] [6000ms] [6000ms] [6000ms]
---------------------------------------------
insert 6001ms after 6000ms at the end of the sequence
At 6001ms rectangle has x=50.0 and y=50.0, xOff=0.0,yOff=0.0
KeyFrames -> [0ms] [1000ms] [1000ms] [3000ms] [3000ms] [4000ms] [5000ms] [6000ms] [6000ms] [6000ms] [6000ms] [6000ms] [6001ms]
---------------------------------------------
insert 6000ms after 5000ms and before 6000ms
At 6000ms rectangle has x=50.0 and y=50.0, xOff=0.0,yOff=0.0
KeyFrames -> [0ms] [1000ms] [1000ms] [3000ms] [3000ms] [4000ms] [5000ms] [6000ms] [6000ms] [6000ms] [6000ms] [6000ms] [6000ms] [6001ms]
---------------------------------------------
insert 6000ms after 5000ms and before 6000ms
At 6000ms rectangle has x=450.0 and y=450.0, xOff=400.0,yOff=400.0
KeyFrames -> [0ms] [1000ms] [1000ms] [3000ms] [3000ms] [4000ms] [5000ms] [6000ms] [6000ms] [6000ms] [6000ms] [6000ms] [6000ms] [6000ms] [6001ms]
I've found the way to pause timeline but still I have terrible problem: I can't get correct interpolated values.
It's possible to pause at any time using newly inserted frame with action function which performs pause() on timeline.
But interpolated values will be returned for pausedFrame -1. For example you make pause at 1000ms and see that your interpolated values == 0. You make pause() at 2000 and get values for 1000ms. Strange thing.
This is a code:
***************************************************
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.shape.Rectangle;
import javafx.scene.paint.Color;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.animation.Interpolator;
import javafx.ext.swing.SwingTextField;
import javafx.ext.swing.SwingButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.HBox;
/**
* @author SSheipak
*/
//Movable rectangle
var xOff = 0.0;
var yOff = 0.0;
def rectangle: Rectangle = Rectangle{
x: bind 50.0 + xOff
y: bind 50.0 + yOff
width: 50
height: 50
fill: Color.RED
}
//Read frame value
def inputTimeField: SwingTextField = SwingTextField{
width: 100
height: 30
columns: 10
}
def button: SwingButton = SwingButton{
text: "Go to frame"
onMouseClicked: function(evt: MouseEvent): Void{
//get Duration
var time: Duration = Duration.valueOf( Float.valueOf(inputTimeField.text) );
//Prepare new KeyFrame with pause() action
var newKeyFrame: KeyFrame = createKeyFrameWithPause(time);//+1ms);
//find place for newKeyFrame
var i: Integer = 0;
for(keyFrame in timeline.keyFrames){
if(isAtTheEndOfSequence(i, timeline.keyFrames.size())
or isCanBeInserted(newKeyFrame, keyFrame, timeline.keyFrames[i+1])){
//<debug>
println("---------------------------------------------");
if( isAtTheEndOfSequence(i, timeline.keyFrames.size()) ){
println("insert {newKeyFrame.time} after {timeline.keyFrames[i].time} at the end of the sequence");
}else {
println("insert {newKeyFrame.time} after {timeline.keyFrames[i].time} and before {timeline.keyFrames[i+1].time}");
}
//</debug>
insert newKeyFrame after timeline.keyFrames[i];
break;
}//
i++;
}//for
timeline.time = time;
timeline.play();
println("At {newKeyFrame.time} rectangle has x={rectangle.x} and y={rectangle.y}, xOff={xOff},yOff={yOff}");
printKeyFramesTime();
println("");
}
}
//Container for Swing components
def hBox: HBox = HBox{
content: [inputTimeField, button]
}
//Frames for timeline
var keyFrames: KeyFrame[] = [
KeyFrame{
time: 0ms
values: [xOff => 0.0, yOff => 0.0]
},
KeyFrame{
time: 6000ms
values: [xOff => 400.0 tween Interpolator.LINEAR, yOff => 400.0 tween Interpolator.LINEAR]
}
];
//Timeline
def timeline: Timeline = Timeline{
time: 0ms
keyFrames: keyFrames
}
Stage {
title: "Halt Timeline"
width: 600
height: 600
scene: Scene {
content: [hBox, rectangle]
}
}
/**Checks is current index points to the last element of Sequence
@param position is an index to test
@param seqSize is a value of someSeq.size()
@return true if position points to the last element of the sequence else returns false
*/
function isAtTheEndOfSequence(position: Integer, seqSize: Integer): Boolean{
var isAt: Boolean = false;
if( (position + 1) >= seqSize){
isAt = true;
}
return isAt;
}
/**Find ordered place for KeyFrame in KeyFrame sequence using keyFrame.time
@param testing is a KeyFrame you want to insert into seq
@param current is a current KeyFrame of a seq
@param next is a next KeyFrame of q seq
@return true if testing.time >= current.time and <= next.time else returns false
*/
function isCanBeInserted(testing: KeyFrame, current: KeyFrame, next: KeyFrame): Boolean{
var isCan = false;
var testingTime = testing.time;
var currentTime = current.time;
var nextTime = next.time;
if(testingTime >= currentTime and testingTime <= nextTime){
isCan = true;
}
return isCan;
}
/**Print all frames of timeline in format [keyFrame.time]*/
function printKeyFramesTime():Void{
var str: String = "KeyFrames -> ";
for(kf in timeline.keyFrames){
str+="[{kf.time}] ";
}
println(str);
}
function createKeyFrameWithPause(time: Duration): KeyFrame{
var keyFrame: KeyFrame = KeyFrame{
time: time
action: function(): Void{
timeline.pause();
}
};
return keyFrame;
}
output:
*****************************************
---------------------------------------------
insert 3000ms after 0ms and before 6000ms
At 3000ms rectangle has x=50.0 and y=50.0, xOff=0.0,yOff=0.0
KeyFrames -> [0ms] [3000ms] [6000ms]
---------------------------------------------
insert 4000ms after 3000ms and before 6000ms
At 4000ms rectangle has x=250.0 and y=250.0, xOff=200.0,yOff=200.0
KeyFrames -> [0ms] [3000ms] [4000ms] [6000ms]
---------------------------------------------
insert 5000ms after 4000ms and before 6000ms
At 5000ms rectangle has x=316.6667 and y=316.6667, xOff=266.6667,yOff=266.6667
KeyFrames -> [0ms] [3000ms] [4000ms] [5000ms] [6000ms]
---------------------------------------------
insert 6000ms after 5000ms and before 6000ms
At 6000ms rectangle has x=383.3333 and y=383.3333, xOff=333.3333,yOff=333.3333
KeyFrames -> [0ms] [3000ms] [4000ms] [5000ms] [6000ms] [6000ms]
---------------------------------------------
insert 6000ms after 5000ms and before 6000ms
At 6000ms rectangle has x=50.0 and y=50.0, xOff=0.0,yOff=0.0
KeyFrames -> [0ms] [3000ms] [4000ms] [5000ms] [6000ms] [6000ms] [6000ms]
---------------------------------------------
insert 6000ms after 5000ms and before 6000ms
At 6000ms rectangle has x=50.0 and y=50.0, xOff=0.0,yOff=0.0
KeyFrames -> [0ms] [3000ms] [4000ms] [5000ms] [6000ms] [6000ms] [6000ms] [6000ms]
---------------------------------------------
insert 1000ms after 0ms and before 3000ms
At 1000ms rectangle has x=50.0 and y=50.0, xOff=0.0,yOff=0.0
KeyFrames -> [0ms] [1000ms] [3000ms] [4000ms] [5000ms] [6000ms] [6000ms] [6000ms] [6000ms]
---------------------------------------------
insert 1000ms after 0ms and before 1000ms
At 1000ms rectangle has x=116.66667 and y=116.66667, xOff=66.66667,yOff=66.66667
KeyFrames -> [0ms] [1000ms] [1000ms] [3000ms] [4000ms] [5000ms] [6000ms] [6000ms] [6000ms] [6000ms]
---------------------------------------------
insert 3000ms after 1000ms and before 3000ms
At 3000ms rectangle has x=117.53333 and y=117.53333, xOff=67.53333,yOff=67.53333
KeyFrames -> [0ms] [1000ms] [1000ms] [3000ms] [3000ms] [4000ms] [5000ms] [6000ms] [6000ms] [6000ms] [6000ms]
---------------------------------------------
insert 6000ms after 5000ms and before 6000ms
At 6000ms rectangle has x=250.0 and y=250.0, xOff=200.0,yOff=200.0
KeyFrames -> [0ms] [1000ms] [1000ms] [3000ms] [3000ms] [4000ms] [5000ms] [6000ms] [6000ms] [6000ms] [6000ms] [6000ms]
---------------------------------------------
insert 6001ms after 6000ms at the end of the sequence
At 6001ms rectangle has x=50.0 and y=50.0, xOff=0.0,yOff=0.0
KeyFrames -> [0ms] [1000ms] [1000ms] [3000ms] [3000ms] [4000ms] [5000ms] [6000ms] [6000ms] [6000ms] [6000ms] [6000ms] [6001ms]
---------------------------------------------
insert 6000ms after 5000ms and before 6000ms
At 6000ms rectangle has x=50.0 and y=50.0, xOff=0.0,yOff=0.0
KeyFrames -> [0ms] [1000ms] [1000ms] [3000ms] [3000ms] [4000ms] [5000ms] [6000ms] [6000ms] [6000ms] [6000ms] [6000ms] [6000ms] [6001ms]
---------------------------------------------
insert 6000ms after 5000ms and before 6000ms
At 6000ms rectangle has x=450.0 and y=450.0, xOff=400.0,yOff=400.0
KeyFrames -> [0ms] [1000ms] [1000ms] [3000ms] [3000ms] [4000ms] [5000ms] [6000ms] [6000ms] [6000ms] [6000ms] [6000ms] [6000ms] [6000ms] [6001ms]