This sounds like the perfect situation for using the viewOrder
property of Node
added in Java 9. The viewOrder
controls how Node
s are drawn in relation to other Node
s of the same Parent
without changing the order of the Node
s in the child list. Here's the Javadoc:
Defines the rendering and picking order of this Node within its parent.
This property is used to alter the rendering and picking order of a
node within its parent without reordering the parent's children list.
For example, this can be used as a more efficient way to implement
transparency sorting. To do this, an application can assign the
viewOrder value of each node to the computed distance between that
node and the viewer.
The parent will traverse its children in decreasing viewOrder order.
This means that a child with a lower viewOrder will be in front of a
child with a higher viewOrder. If two children have the same
viewOrder, the parent will traverse them in the order they appear in
the parent's children list.
However, viewOrder does not alter the layout and focus traversal order
of this Node within its parent. A parent always traverses its children
list in order when doing layout or focus traversal.
Here's an example using this property:
import javafx.animation.ScaleTransition;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.util.ArrayList;
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
var box = new HBox(createRectangles(Color.DARKBLUE, Color.FIREBRICK, 25));
box.setAlignment(Pos.CENTER);
box.setPadding(new Insets(50, 20, 50, 20));
primaryStage.setScene(new Scene(box));
primaryStage.show();
}
private Rectangle[] createRectangles(Color start, Color end, int count) {
var list = new ArrayList<Rectangle>(count);
for (double i = 0; i < count; i++) {
var rect = new Rectangle(30, 60, start.interpolate(end, i / count));
var scaleTrans = new ScaleTransition(Duration.millis(250), rect);
scaleTrans.setFromX(1.0);
scaleTrans.setFromY(1.0);
scaleTrans.setToX(1.2);
scaleTrans.setToY(1.2);
rect.setOnMouseEntered(e -> {
scaleTrans.stop(); // <--- doesn't seem necessary*
scaleTrans.setRate(1.0);
rect.setViewOrder(-1.0);
scaleTrans.play();
});
rect.setOnMouseExited(e -> {
scaleTrans.stop(); // <--- doesn't seem necessary*
scaleTrans.setRate(-1.0);
rect.setViewOrder(0.0);
scaleTrans.play();
});
// *the "stop()"'s don't seem to be necessary. When I commented
// them out the animation still worked. In fact, the animation
// actually seems smoother in the situation where you move the
// mouse over and then away quickly (before the zoom-in completes).
list.add(rect);
}
return list.toArray(new Rectangle[0]);
}
}
It uses Rectangle
s instead of ImageView
s but the concept is the same. When the mouse hovers over a Rectangle
it sets the view order to be lower than the others and then plays a ScaleTransition
to make it bigger. When the mouse exits it resets the view order back to 0
and then reverses the ScaleTransition
.
Note: I used the var
keyword which was added in Java 10.
And here is a GIF of the example in action:

Edit: Since you brought up CSS I went and checked if the view order could be set from a stylesheet. And it appears it can. Looking at the CSS Reference Guide there is a CSS property defined for Node
named -fx-view-order
.