Issue
I'm looking for an uncomplicated way to add an effect to an ImageView in JavaFX for only a brief period, but I haven't found an effective way yet.
It's a game and the players are shown as ImageViews on Planes. When a player attacks the other player, I want to show an effect on the attacked ImageView, like a color overlay or something like that.
But what is the best approach to do so? And is there a way around a timer or do i need one?
Thanks for your help!
Solution
General Information
Generic info on this task is in:
Doing something just once, is just a simpler version of doing it periodically.
Lighting example
A simple example that uses a lighting effect to make Smurfette turn green with envy when you click on her. A PauseTransition is used.
import javafx.animation.PauseTransition;
import javafx.application.Application;
import javafx.scene.*;
import javafx.scene.effect.*;
import javafx.scene.image.*;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.Duration;
public class EnvyApp extends Application {
@Override
public void start(Stage stage) throws Exception {
Image image = new Image(
"http://icons.iconarchive.com/icons/designbolts/smurfs-movie/128/smurfette-icon.png"
);
ImageView imageView = new ImageView(image);
PauseTransition hitAnimation = new PauseTransition(Duration.seconds(1));
hitAnimation.setOnFinished(e -> imageView.setEffect(null));
Light light = new Light.Distant();
light.setColor(Color.PALEGREEN);
Lighting lighting = new Lighting(light);
imageView.setOnMouseClicked(e -> {
imageView.setEffect(lighting);
hitAnimation.playFromStart();
});
stage.setScene(new Scene(new Group(imageView), Color.AQUA));
stage.show();
}
public static void main(String[] args) {
Application.launch();
}
}
Gradiated Color Change Example
Example of a fading temporary color overlay effect on clicking an ImageView. A timeline is used for the effect display.
The example for the color change is based on this answer:
The clicked image turns red, then gradually fades from red through gray and back to the original color. There are probably simpler ways of doing this, but this is what I came up with.
The timeline for the effect is stored in the properties of the affected node, but it could be stored in an instance variable (e.g. an attribute of a generic Sprite class) somewhere if preferred. The timeline is stored so that if you click on the image before the effect completes, then the existing timeline can be restarted from the start.
Applying different effects
The effect is just an example, you could adjust it as wished.
The general principle can be used to adjust any effect (or node) properties. You can adjust the actual specifics of the effect used to how you want. For example, on click in addition to the color adjustment, you could make a node bounce via translations or apply a glow effect or a drop shadow effect, etc.
Example Code
import javafx.animation.*;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.effect.*;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Blush extends Application {
@Override
public void start(Stage stage) throws Exception {
HitEffectGenerator effectGenerator = new HitEffectGenerator();
Image image = new Image(
"http://icons.iconarchive.com/icons/designbolts/smurfs-movie/128/smurfette-icon.png"
);
ImageView imageView = new ImageView(image);
// try with clip on and off to see the difference . . .
imageView.setClip(new ImageView(image));
imageView.setOnMouseClicked(e -> effectGenerator.apply(imageView));
stage.setScene(new Scene(new Group(imageView), Color.AQUA));
stage.show();
}
public static void main(String[] args) {
Application.launch();
}
}
class HitEffectGenerator {
public void apply(ImageView target) {
Timeline effectTimeline = (Timeline) target.getProperties().get(HitEffectGenerator.class);
if (effectTimeline == null) {
ColorAdjust monochrome = new ColorAdjust();
monochrome.setSaturation(-1.0);
ColorInput colorInput = new ColorInput(
0,
0,
target.getImage().getWidth(),
target.getImage().getHeight(),
Color.RED
);
Blend blush = new Blend(
BlendMode.MULTIPLY,
monochrome,
colorInput
);
target.setEffect(blush);
effectTimeline = new Timeline(
new KeyFrame(
Duration.ZERO,
new KeyValue(monochrome.saturationProperty(), -1.0),
new KeyValue(colorInput.paintProperty(), Color.RED)
),
new KeyFrame(
Duration.seconds(1),
new KeyValue(monochrome.saturationProperty(), 0.0),
new KeyValue(colorInput.paintProperty(), Color.TRANSPARENT)
)
);
effectTimeline.setOnFinished(e -> remove(target));
}
target.getProperties().put(HitEffectGenerator.class, effectTimeline);
effectTimeline.playFromStart();
}
public void remove(ImageView target) {
Timeline effectTimeline = (Timeline) target.getProperties().get(HitEffectGenerator.class);
if (effectTimeline != null) {
target.setEffect(null);
target.getProperties().remove(HitEffectGenerator.class);
effectTimeline.stop();
}
}
}
Various animation effects on click
See:
- FXExperience canned animations, with blog post and example app.
Those examples are old and use the deprecated builder API, but are easy to convert to modern code which uses constructors and setters rather than the builders.
Answered By - jewelsea
Answer Checked By - Robin (JavaFixing Admin)