Properties & animation
The Property
class is used for creating property controls on components. It can be used
directly as a field type, and is also the underlying mechanism for other field types such as
int
, double
, String
and boolean
. Properties can be created using the
@P
or @Inject
annotations.
Properties have some important extra features. Values are automatically saved (unless specified) as part of the component state. Properties with the same name maintain their value across code changes, unlike ordinary fields.
Additional annotations can be used to provide additional information about a property. This can include its type, range, allowed values, default value, etc. There are also annotations to mark a property as read-only or transient (value not saved), to control whether a port is created, or to call a method when the value changes.
Used as the field type Property
also supports animation with multiple keyframes and easing
modes. Animations also maintain their state across code changes. The Property
class
also supports Linkables for reacting and linking changing values.
Reading and writing values
The Property
object itself supports various methods for setting and retrieving the value.
Methods that set the value return the Property
for efficient method chaining. Get methods
provide an optional default to return if the value cannot be coerced to the required type.
Property set(Argument value);
Property set(double value);
Argument get();
double getDouble(double def);
double getDouble(); // equivalent to getDouble(0)
int getInt(int def);
int getInt(); // equivalent to getInt(0)
boolean getBoolean(boolean def);
boolean getBoolean(); // equivalent to getBoolean(false)
There are also some shorthand functions for working with properties.
Property prop;
double d = d(prop); // shorthand for prop.getDouble()
float f = f(prop); // shorthand for (float) prop.getDouble()
int i = i(prop); // shorthand for prop.getInt()
String s = s(prop); // shorthand for prop.get().toString()
boolean b = b(prop); // shorthand for prop.getBoolean()
Animation
Property
supports animation through a Property.Animator
object, with multiple keyframes
and a variety of easing modes available. Animations will continue running even when code is
changed - use the isAnimating()
to only start an animation if one is not already running.
Methods on Property
Property.Animator animator();
boolean isAnimating();
Property.Animator to(double ... values);
Property.Animator objects are created lazily when required. isAnimating()
is equivalent
to animator != null && animator().isAnimating()
so will not create an animator unless
required.
The to()
method is a shorthand for calling animator().to()
.
Note
Calling set(...)
will stop any animation.
Methods on Property.Animator
Most methods on Property.Animator
return the animator for fluid method chaining.
Property.Animator to(double ... values);
Property.Animator in(double ... seconds);
Property.Animator easing(Easing ... easings);
Property.Animator stop();
Property.Animator whenDone(Consumer<Property> action);
Property.Animator linear(); // shorthand for easing(Easing.linear)
Property.Animator ease(); shorthand for easing(Easing.ease)
Property.Animator easeIn(); shorthand for easing(Easing.easeIn)
Property.Animator easeOut(); shorthand for easing(Easing.easeOut)
Property.Animator easeInOut(); shorthand for easing(Easing.easeInOut)
The number of keyframes is defined by the number of values given to to()
. You can provide fewer values to in()
or easing()
and their values will be cycled. eg. to(1, 0, 0.5).in(5, 2.5);
will result in -
- animate from current value to 1 in 5 seconds
- animate from 1 to 0 in 2.5 seconds
- animate from 0 to 0.5 in 5 seconds
The whenDone(...)
method will run an action whenever animation is complete,
and also run the action if not currently animating. It's main use is creating continuous
animations. Restarting the animation within a whenDone
handler will cause the
property to remove any overrun of time from the first keyframe for accurate time handling.
Easing modes
The various easing modes and their definitions are listed here -
public final static Easing linear = new LinearEasing();
public final static Easing easeIn = new SplineEasing(0.42, 0, 1, 1);
public final static Easing easeOut = new SplineEasing(0, 0, 0.58, 1);
public final static Easing easeInOut = new SplineEasing(0.42, 0, 0.58, 1);
public final static Easing ease = new SplineEasing(0.25, 0.1, 0.25, 1);
public final static Easing expoIn = new SplineEasing(0.71,0.01,0.83,0);
public final static Easing expoOut = new SplineEasing(0.14,1,0.32,0.99);
public final static Easing expoInOut = new SplineEasing(0.85,0,0.15,1);
public final static Easing circIn = new SplineEasing(0.34,0,0.96,0.23);
public final static Easing circOut = new SplineEasing(0,0.5,0.37,0.98);
public final static Easing circInOut = new SplineEasing(0.88,0.1,0.12,0.9);
public final static Easing sineIn = new SplineEasing(0.22,0.04,0.36,0);
public final static Easing sineOut = new SplineEasing(0.04,0,0.5,1);
public final static Easing sineInOut = new SplineEasing(0.37,0.01,0.63,1);
public final static Easing quadIn = new SplineEasing(0.14,0.01,0.49,0);
public final static Easing quadOut = new SplineEasing(0.01,0,0.43,1);
public final static Easing quadInOut = new SplineEasing(0.47,0.04,0.53,0.96);
public final static Easing cubicIn = new SplineEasing(0.35,0,0.65,0);
public final static Easing cubicOut = new SplineEasing(0.09,0.25,0.24,1);
public final static Easing cubicInOut = new SplineEasing(0.66,0,0.34,1);
public final static Easing quartIn = new SplineEasing(0.69,0,0.76,0.17);
public final static Easing quartOut = new SplineEasing(0.26,0.96,0.44,1);
public final static Easing quartInOut = new SplineEasing(0.76,0,0.24,1);
public final static Easing quintIn = new SplineEasing(0.64,0,0.78,0);
public final static Easing quintOut = new SplineEasing(0.22,1,0.35,1);
public final static Easing quintInOut = new SplineEasing(0.9,0,0.1,1);
Examples
This simple example creates a property that counts seconds. It will continue running through code changes, and restart after 24 hours.
@P(1) Property seconds;
public void update() {
if (!seconds.isAnimating()) {
seconds.set(0).to(86400).in(86400);
}
}
This example animates a rectangle from the left to the middle of the screen and back when a trigger is received (eg. button clicked). It use @Inject
to create a hidden property purely for animation purposes (no port or control, and not saved).
@Inject Property x;
public void draw() {
rect(d(x) * width, (height / 2) - 25, 50, 50);
}
@T(1) void move() {
x.to(0.5, 0).in(2.25).easing(Easing.easeIn, Easing.cubicOut);
}
To continuously animate to a random value every 5 seconds using whenDone
.
@Inject Property x;
public void init() {
x.animator().whenDone(p -> p.to(random(0, 1).in(5).easeInOut());
}