animated
animated introduces implicit animations, a completely new concept in JavaFX strongly inspired by Flutter's animations and motion widgets.
Index
- Getting started
- Implicit animations
- Animated containers
- Animated switchers
- Animated theme switch
- Animated values
- Other examples
- FXML
- Kotlin extensions
Getting started
Maven:
<dependency>
<groupId>eu.iamgio</groupId>
<artifactId>animated</artifactId>
<version>1.2.0</version>
</dependency>
Gradle:
allprojects {
repositories {
mavenCentral()
}
}
dependencies {
implementation 'eu.iamgio:animated:1.2.0'
}
Note: v1.0.0 brought several important changes, including a different project structure,
that may cause compilation errors when upgrading from 0.x versions.
Please check the migration guide to see what changed and quickly learn how to fix those errors.
Implicit animations
Forget about timelines, explicit animations and other stuff that pollutes your code. This animation system will provide versatility to your code and interface.
Code
Animated animated = new Animated(child, AnimationProperty.of(child.opacityProperty()));
root.getChildren().add(animated);
child.setOpacity(0.5);
This approach instantiates an Animated
node that contains one child and is bound to a property.
Now that we have set an animated bound, we can see that child.setOpacity(someValue)
creates a transition between the initial and final value.
A single Animated
object can take multiple properties at once,
and they can also be added later by accessing Animated#getTargetProperties
.
Presets
Pre-made animation properties represent a concise and efficient
way to create animated bindings, rather than manually referencing to the
raw JavaFX property as we previously did.
AnimatedBlur
AnimatedColor
AnimatedDropShadow.Color
AnimatedDropShadow.Radius
AnimatedLayout
AnimatedOpacity
AnimatedPrefSize
AnimatedRotation
AnimatedScale
AnimatedTranslatePosition
When these properties are instantiated via their zero-arguments constructor,
the target node references to the Animated
's child.
new Animated(child,
AnimationProperty.of(child.prefWidthProperty()),
AnimationProperty.of(child.prefHeightProperty())
);
new Animated(child, new AnimatedPrefSize());
Independent animations
Animated
is a node that has to be added to the scene in order to work.
Here is a different approach that is independent from the scene:
AnimationProperty.of(node.opacityProperty()).register();
new AnimatedOpacity(node).register();
node.setOpacity(0.5);
Animations are not only visual:
you can also animate other changes such as audio volume!
Custom animations
The default animation is linear and lasts 1 second.
It can be customized by calling withSettings(AnimationSettings settings)
or custom(Function<AnimationSettings, AnimationSettings> settings)
,
both methods available on animated nodes and animation properties.
Examples:
Animated animated = new Animated(child, AnimationProperty.of(child.opacityProperty()));
.custom(settings -> settings.withDuration(Duration.seconds(.5)).withCurve(Curve.EASE_IN_OUT));
Animated animated = new Animated(child,
new AnimatedOpacity()
.custom(settings -> settings.withDuration(Duration.seconds(.8))),
new AnimatedRotation()
.custom(settings -> settings.withDuration(Duration.seconds(.5))),
).custom(settings -> settings.withCurve(Curve.EASE_OUT));
Animated containers
animated provides custom implementations of VBox
and HBox
that animate their content whenever their children are affected by a change.
This feature is based on animations from AnimateFX.
Code
Constructors:
Animation in, Animation out
wraps two AnimateFX
objects into customizable animated
objects;AnimationFX in, AnimationFX out
takes two raw AnimateFX animations that cannot be customized;AnimationPair animation
takes a pair of animations, mostly used with pre-made pairs (e.g. AnimationPair.fade()
).
pause()
and resume()
allow disabling/enabling animations so that you can switch back to the regular implementation and forth.
Example:
AnimatedVBox vBox = new AnimatedVBox(AnimationPair.fade());
vBox.getChildren().add(someNode);
vBox.getChildren().remove(someNode);
Animated switchers
The library also provides an AnimatedSwitcher
node that creates a transition whenever its child changes.
As for animated containers, this feature relies on AnimateFX.
Code
See animated containers for information about constructors.
Right after the instantiation, calling of(Node child)
will set the initial child without any animation played.
Example:
AnimatedSwitcher switcher = new AnimatedSwitcher(
new Animation(new FadeInDown()).setSpeed(2),
new Animation(new FadeOutDown()).setSpeed(1.5)
).of(firstChild);
root.getChildren().add(switcher);
switcher.setChild(secondChild);
Related: animated text
AnimatedLabel
uses a switcher to animate text.
Example:
AnimatedLabel label = new AnimatedLabel("Text", AnimationPair.fade());
label.setText("New text");
Animated theme switch
It is possible to create a transition whenever the stylesheets of the scene change via AnimatedThemeSwitcher
, based on AnimateFX.
Code
scene.getStylesheets().setAll("/light.css");
AnimatedThemeSwitcher themeSwitcher = new AnimatedThemeSwitcher(scene, new CircleClipOut());
themeSwitcher.init();
scene.getStylesheets().setAll("/dark.css");
Note that not every type of root can be animated properly, such as VBox
and HBox
.
Parents that allow overlapping children, i.e. Pane
, are suggested.
Animated values
The animated binding API provides a way to animate the content of a Label
every time its associated value changes.
Code
AnimatedValueLabel<Integer> label = new AnimatedValueLabel<>(0)
.custom(settings -> settings.withCurve(Curve.EASE_IN_OUT));
label.setTextMapper(value -> "The value is " + value);
label.setValue(10);
Other examples
Button color and border
Drop shadows + label
Root switch
Layout alignment (inspired by the Edge home page)
FXML
-
Animated
<?import eu.iamgio.animated.binding.Animated?>
<?import eu.iamgio.animated.binding.AnimationSettings?>
<?import eu.iamgio.animated.binding.presets.AnimatedOpacity?>
<Animated>
<child>
<MyNode/>
</child>
<targetProperties>
<AnimatedOpacity>
<settings>
<AnimationSettings duration="1500ms" curve="EASE_IN_OUT"/>
</settings>
</AnimatedOpacity>
</targetProperties>
</Animated>
-
AnimatedContainer
<?import eu.iamgio.animated.transition.container.AnimatedHBox?>
<?import eu.iamgio.animated.transition.Animation?>
<AnimatedHBox>
<in>
<Animation type="ZoomIn"/>
</in>
<out>
<Animation type="ZoomOut"/>
</out>
<MyNode1/>
<MyNode2/>
</AnimatedHBox>
-
AnimatedSwitcher
<?import eu.iamgio.animated.transition.AnimatedSwitcher?>
<?import eu.iamgio.animated.transition.Animation?>
<AnimatedSwitcher>
<in>
<Animation type="RectangleClipIn"/>
</in>
<out>
<Animation type="SlideOutUp" speed="0.5"/>
</out>
<child>
<InitialChild/>
</child>
</AnimatedSwitcher>
-
AnimatedLabel
<?import eu.iamgio.animated.transition.AnimatedLabel?>
<?import eu.iamgio.animated.transition.Animation?>
<AnimatedLabel text="Initial text">
<in>
<Animation type="BounceIn" delay="300ms"/>
</in>
<in>
<Animation type="BounceOut"/>
</in>
</AnimatedLabel>
-
AnimatedValueLabel
FXML has issues with generic types, so you will need to instantiate an
AnimatedIntValueLabel
or AnimatedDoubleValueLabel
.
<?import eu.iamgio.animated.binding.AnimationSettings?>
<?import eu.iamgio.animated.binding.label.AnimatedIntValueLabel?>
<AnimatedIntValueLabel value="0">
<settings>
<AnimationSettings duration="1500ms" curve="EASE_IN_OUT"/>
</settings>
</AnimatedIntValueLabel>
When instantiating an <Animation type="..."/>
,
the class name (case sensitive) is searched in the following packages:
Kotlin extensions
Extension functions make the library less verbose with Kotlin.
Example:
val animated: Animated = Animated(child, child.someProperty().animated())
val pair: AnimationPair = FadeIn().options(speed = 1.5) outTo FadeOut()