Security News
Research
Data Theft Repackaged: A Case Study in Malicious Wrapper Packages on npm
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
github.com/schlegel11/jfxanimation
CSS keyframe animation for JavaFX.
If you are using JFoenix JFXAnimation is included (currently not in release versions)
For details see: JavaDoc
The JFXAnimation project provides the JFXAnimationTemplate classes, which acts as a builder for JavaFX animations.
The building structure of a JFXAnimationTemplate is based on CSS keyframe animations, which have some advantages:
CSS TADA animation from animate.css | ||
---|---|---|
Implementation with Keyframe objects |
Implementation with JFXAnimationTemplate |
Implementation with pure CSS |
TimelineBuilder.create() .keyFrames( new KeyFrame(Duration.millis(0), new KeyValue(node.scaleXProperty(), 1, WEB_EASE), new KeyValue(node.scaleYProperty(), 1, WEB_EASE), new KeyValue(node.rotateProperty(), 0, WEB_EASE) ), new KeyFrame(Duration.millis(100), new KeyValue(node.scaleXProperty(), 0.9, WEB_EASE), new KeyValue(node.scaleYProperty(), 0.9, WEB_EASE), new KeyValue(node.rotateProperty(), -3, WEB_EASE) ), new KeyFrame(Duration.millis(200), new KeyValue(node.scaleXProperty(), 0.9, WEB_EASE), new KeyValue(node.scaleYProperty(), 0.9, WEB_EASE), new KeyValue(node.rotateProperty(), -3, WEB_EASE) ), new KeyFrame(Duration.millis(300), new KeyValue(node.scaleXProperty(), 1.1, WEB_EASE), new KeyValue(node.scaleYProperty(), 1.1, WEB_EASE), new KeyValue(node.rotateProperty(), 3, WEB_EASE) ), new KeyFrame(Duration.millis(400), new KeyValue(node.scaleXProperty(), 1.1, WEB_EASE), new KeyValue(node.scaleYProperty(), 1.1, WEB_EASE), new KeyValue(node.rotateProperty(), -3, WEB_EASE) ), new KeyFrame(Duration.millis(500), new KeyValue(node.scaleXProperty(), 1.1, WEB_EASE), new KeyValue(node.scaleYProperty(), 1.1, WEB_EASE), new KeyValue(node.rotateProperty(), 3, WEB_EASE) ), new KeyFrame(Duration.millis(600), new KeyValue(node.scaleXProperty(), 1.1, WEB_EASE), new KeyValue(node.scaleYProperty(), 1.1, WEB_EASE), new KeyValue(node.rotateProperty(), -3, WEB_EASE) ), new KeyFrame(Duration.millis(700), new KeyValue(node.scaleXProperty(), 1.1, WEB_EASE), new KeyValue(node.scaleYProperty(), 1.1, WEB_EASE), new KeyValue(node.rotateProperty(), 3, WEB_EASE) ), new KeyFrame(Duration.millis(800), new KeyValue(node.scaleXProperty(), 1.1, WEB_EASE), new KeyValue(node.scaleYProperty(), 1.1, WEB_EASE), new KeyValue(node.rotateProperty(), -3, WEB_EASE) ), new KeyFrame(Duration.millis(900), new KeyValue(node.scaleXProperty(), 1.1, WEB_EASE), new KeyValue(node.scaleYProperty(), 1.1, WEB_EASE), new KeyValue(node.rotateProperty(), 3, WEB_EASE) ), new KeyFrame(Duration.millis(1000), new KeyValue(node.scaleXProperty(), 1, WEB_EASE), new KeyValue(node.scaleYProperty(), 1, WEB_EASE), new KeyValue(node.rotateProperty(), 0, WEB_EASE) ) ) |
JFXAnimationTemplate.create() .from() .action(b -> b.target(Node::scaleXProperty, Node::scaleYProperty).endValue(1)) .percent(10) .percent(20) .action(b -> b.target(Node::scaleXProperty, Node::scaleYProperty).endValue(0.9)) .action(b -> b.target(Node::rotateProperty).endValue(-3)) .percent(30, 50, 70, 90) .action(b -> b.target(Node::scaleXProperty, Node::scaleYProperty).endValue(1.1)) .action(b -> b.target(Node::rotateProperty).endValue(3)) .percent(40, 60, 80) .action(b -> b.target(Node::scaleXProperty, Node::scaleYProperty).endValue(1.1)) .action(b -> b.target(Node::rotateProperty).endValue(-3)) .to() .action(b -> b.target(Node::scaleXProperty, Node::scaleYProperty).endValue(1)) .action(b -> b.target(Node::rotateProperty).endValue(0)) .config(b -> b.duration(Duration.seconds(1)).interpolator(WEB_EASE)); |
@keyframes tada { from { transform: scale3d(1, 1, 1); } 10%, 20% { transform: scale3d(0.9, 0.9, 0.9) rotate3d(0, 0, 1, -3deg); } 30%, 50%, 70%, 90% { transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg); } 40%, 60%, 80% { transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg); } to { transform: scale3d(1, 1, 1); } } |
As you can see, the default approach with KeyFrame objects has more lines of code.
Furthermore, there is a repetitive number of KeyFrames (each for a specific animation section) which causes a lot of duplicated KeyValue objects.
The JFXAnimationTemplate approach can handel one specified KeyFrame (action) on more animation sections due to different percentage values.
Moreover, you can specify more target observers for one specific end value.
The created type of JFXTemplate is use case specific. Mostly the JFXTemplateBuilder is used as final type, where T is the default animation object type.
A JFXTemplate can be created like that:
...
JFXAnimationTemplate.create()
...
in this case the default animation object type T is a Node type.
It's also possible to set a own default animation object type:
...
JFXAnimationTemplate.create(MyType.class)
...
After the init creation you have to specify a animation interval like you do in CSS.
You can use:
...
JFXAnimationTemplate.create()
.from()
...
which means 0%,
...
JFXAnimationTemplate.create()
.to()
...
which means 100% or
...
JFXAnimationTemplate.create()
.percent(10)
...
which is the percentage value. Furthermore you can specify multiple percentage values:
...
JFXAnimationTemplate.create()
.percent(10)
.percent(20)
.percent(30)
...
or via varargs:
...
JFXAnimationTemplate.create()
.percent(10, 20, 30)
...
Now you have to implement the specific action or actions like:
...
JFXAnimationTemplate.create()
.percent(10, 20, 30)
.action(...)
.action(...)
...
The action method can be called by value or in a lazy way. If you use the non lazy method you have to create a JFXAnimationTemplateAction.Builder manually like:
...
JFXAnimationTemplateAction.builder()
...
The more comfortable possibility is to use the lazy approach where such a builder is ready to use like:
...
JFXAnimationTemplate.create()
.percent(10, 20, 30)
.action(builder -> builder...)
...
The next step is to define the specific animation values like:
...
JFXAnimationTemplate.create()
.percent(10, 20, 30)
.action(b -> b.target(Node::scaleXProperty).endValue(1))
...
For example you can use multiple target properties via varargs:
...
JFXAnimationTemplate.create()
.percent(10, 20, 30)
.action(b -> b.target(Node::scaleXProperty, Node::scaleYProperty).endValue(1))
...
There are a lot more functions and also lazy method representations, which also provides access to the actual animation object (like the target method in this example).
Lets assume we have our animation action defined like this:
...
JFXAnimationTemplate.create()
.from()
.action(b -> b.target(Node::scaleXProperty, Node::scaleYProperty).endValue(1))
.percent(14)
.action(b -> b.target(Node::scaleXProperty, Node::scaleYProperty).endValue(1.3))
.percent(28)
.action(b -> b.target(Node::scaleXProperty, Node::scaleYProperty).endValue(1))
.percent(42)
.action(b -> b.target(Node::scaleXProperty, Node::scaleYProperty).endValue(1.3))
.percent(70)
.action(b -> b.target(Node::scaleXProperty, Node::scaleYProperty).endValue(1))
...
We can finalize the animation by calling the config(...) method after the last action(...) method.
Again there are the same possibilities as for the JFXAnimationTemplateAction.Builder.
So we can use the non lazy version and create a JFXAnimationTemplateConfig.Builder manually like:
...
JFXAnimationTemplateConfig.builder()
...
or the more comfortable possibility with the lazy approach like:
...
JFXAnimationTemplate.create()
.from()
.action(b -> b.target(Node::scaleXProperty, Node::scaleYProperty).endValue(1))
.percent(14)
.action(b -> b.target(Node::scaleXProperty, Node::scaleYProperty).endValue(1.3))
.percent(28)
.action(b -> b.target(Node::scaleXProperty, Node::scaleYProperty).endValue(1))
.percent(42)
.action(b -> b.target(Node::scaleXProperty, Node::scaleYProperty).endValue(1.3))
.percent(70)
.action(b -> b.target(Node::scaleXProperty, Node::scaleYProperty).endValue(1))
.config(builder -> builder...);
...
Now we have to define some config values like:
...
JFXAnimationTemplate.create()
.from()
.action(b -> b.target(Node::scaleXProperty, Node::scaleYProperty).endValue(1))
.percent(14)
.action(b -> b.target(Node::scaleXProperty, Node::scaleYProperty).endValue(1.3))
.percent(28)
.action(b -> b.target(Node::scaleXProperty, Node::scaleYProperty).endValue(1))
.percent(42)
.action(b -> b.target(Node::scaleXProperty, Node::scaleYProperty).endValue(1.3))
.percent(70)
.action(b -> b.target(Node::scaleXProperty, Node::scaleYProperty).endValue(1))
.config(b -> b.duration(Duration.seconds(1.3)).interpolator(Interpolator.EASE_BOTH));
...
We have to define the total duration of the animation and we can also define a interpolator which is used by all actions.
There are a lot more functions and also lazy method representations.
For more see the JFXAnimationTemplateConfig class.
After defining the actions and the config method, the last step is to build the final animation.
There are different approaches to build an animation.
Lets assume we have defined our animation for later use like this:
...
private static final JFXTemplateBuilder<Node> HEART_BEAT =
JFXAnimationTemplate.create()
.percent(0)
.action(b -> b.target(Node::scaleXProperty, Node::scaleYProperty).endValue(1))
.percent(14)
.action(b -> b.target(Node::scaleXProperty, Node::scaleYProperty).endValue(1.3))
.percent(28)
.action(b -> b.target(Node::scaleXProperty, Node::scaleYProperty).endValue(1))
.percent(42)
.action(b -> b.target(Node::scaleXProperty, Node::scaleYProperty).endValue(1.3))
.percent(70)
.action(b -> b.target(Node::scaleXProperty, Node::scaleYProperty).endValue(1))
.config(b -> b.duration(Duration.seconds(1.3)).interpolator(Interpolator.EASE_BOTH));
...
Now we want e.g. a Button to animate. We would build the animation like:
...
Button button = new Button("Button");
Timeline timeline = HEART_BEAT.build(button);
...
So the button is the default animation object and needs to be passed to the build(...) method.
The default return value is the ready to use Timeline.
There is also the possibility to use multiple default animation objects and also named animation objects.
For this purpose we have to use again a Builder in our build(...) method.
And again we can do it in a non lazy:
...
JFXTemplateBuilder.JFXAnimationObjectMapBuilder.builder()
...
or lazy way:
...
Button button = new Button("Button");
Timeline timeline = HEART_BEAT.build(builder -> builder...);
...
So to use multiple default animation objects via varargs we have to do the following:
...
Button button = new Button("Button");
Button button2 = new Button("Button2");
...
Timeline timeline = HEART_BEAT.build(b -> b.defaultObject(button, button2));
...
Now the animation for both buttons are contained in the timeline object.
Lets assume we have defined our animation for later use like this:
...
private static final JFXTemplateBuilder<Node> HEART_BEAT =
JFXAnimationTemplate.create()
.percent(0)
.action(b -> b.target(Node::scaleXProperty, Node::scaleYProperty).endValue(1))
.percent(14)
.action(b -> b.target(Node::scaleXProperty, Node::scaleYProperty).endValue(1.3))
.percent(28)
.action(b -> b.target(Node::scaleXProperty, Node::scaleYProperty).endValue(1))
.action(b -> b.withAnimationObject("SpecialNode").target(Node::translateYProperty).endValue(20))
.percent(42)
.action(b -> b.target(Node::scaleXProperty, Node::scaleYProperty).endValue(1.3))
.percent(70)
.action(b -> b.target(Node::scaleXProperty, Node::scaleYProperty).endValue(1))
.config(b -> b.duration(Duration.seconds(1.3)).interpolator(Interpolator.EASE_BOTH));
...
Here we are using a named animation object "SpecialNode".
So this animation object will be used in exactly this action method.
There is also the possibility to define another type for the named animation object like:
...
.action(b -> b.withAnimationObject(Button.class, "SpecialNode")...)
...
So a type of Button can be used in the defined animation values.
If we don't define a type a Node type will be used.
It's also possible to define multiple named animation objects via varargs for a specific type:
...
.action(b -> b.withAnimationObject("SpecialNode", "SpecialNode2", "SpecialNode3")...)
...
To build our animation for e.g. a Button and our special Node we have to do the following:
...
Button button = new Button("Button");
Label specialNode = new Label("Label");
Timeline timeline = HEART_BEAT.build(b -> b.defaultObject(button).namedObject("SpecialNode", specialNode));
...
We can also use multiple named animation objects via varargs for one name like:
...
Button button = new Button("Button");
Label specialNode = new Label("Label");
Label specialNode2 = new Label("Label2");
Timeline timeline = HEART_BEAT.build(b -> b.defaultObject(button).namedObject("SpecialNode", specialNode, specialNode2));
...
The default animation container for a JFXAnimation is Timeline.
It can explicitly set at build time like:
...
Button button = new Button("Button");
Timeline timeline = myAnimation.build(JFXAnimationTemplates::buildTimeline, button);
...
Where buildTimeline is just a method which accepts a type of JFXAnimationTemplate, which contains all animation values and configs.
To use another animation container except Timeline, just write your own implementation, which handles the JFXAnimationTemplate instance.
For orientation the implementation of buildTimeline in JFXAnimationTemplates class can be used and copied.
A full blown example of animations can be found in the demo project/package.
The demo uses JFoenix and is also included in the JFoenix demo package.
It also requires java 8 or 9.
Run the demo with:
./gradlew demo:animationDemo
If you are using JFoenix, you don't have to use this dependency (it's already included in JFoenix).
If you use this dependency and switch later to JFoenix you can remove this dependency.
Furthermore, you have to change the package names from de.schlegel11.jfxanimation.* to com.jfoenix.transitions.template.*.
compile 'de.schlegel11:jfxanimation:1.0.0'
<dependency>
<groupId>de.schlegel11</groupId>
<artifactId>jfxanimation</artifactId>
<version>1.0.0</version>
</dependency>
FAQs
Unknown package
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Research
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Research
Security News
Attackers used a malicious npm package typosquatting a popular ESLint plugin to steal sensitive data, execute commands, and exploit developer systems.
Security News
The Ultralytics' PyPI Package was compromised four times in one weekend through GitHub Actions cache poisoning and failure to rotate previously compromised API tokens.