Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
This code shows how you can use clipping in JavaFX in combination with the alpha channel to implement fade in / out effects (not animations).
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.Node;
import javafx.scene.control.Control;
import javafx.scene.control.Skin;
public class MaskedView extends Control {
public MaskedView(Node content) {
setContent(content);
}
@Override
protected Skin<?> createDefaultSkin() {
return new MaskedViewSkin(this);
}
private final SimpleObjectProperty<Node> content = new SimpleObjectProperty<>(this, "content");
public final Node getContent() {
return content.get();
}
public final SimpleObjectProperty<Node> contentProperty() {
return content;
}
public final void setContent(Node content) {
this.content.set(content);
}
private final DoubleProperty fadingSize = new SimpleDoubleProperty(this, "fadingSize", 120);
public final double getFadingSize() {
return fadingSize.get();
}
public final DoubleProperty fadingSizeProperty() {
return fadingSize;
}
public final void setFadingSize(double fadingSize) {
this.fadingSize.set(fadingSize);
}
}
import javafx.beans.InvalidationListener;
import javafx.beans.WeakInvalidationListener;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.control.SkinBase;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.paint.CycleMethod;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Stop;
import javafx.scene.shape.Rectangle;
public class MaskedViewSkin extends SkinBase<MaskedView> {
private final Rectangle leftClip;
private final Rectangle rightClip;
private final Rectangle centerClip;
private final Group group;
private final StackPane stackPane;
public MaskedViewSkin(MaskedView view) {
super(view);
leftClip = new Rectangle();
rightClip = new Rectangle();
centerClip = new Rectangle();
centerClip.setFill(Color.BLACK);
leftClip.setManaged(false);
centerClip.setManaged(false);
rightClip.setManaged(false);
group = new Group(leftClip, centerClip, rightClip);
stackPane = new StackPane();
stackPane.setManaged(false);
stackPane.setClip(group);
getChildren().add(stackPane);
view.contentProperty().addListener((observable, oldContent, newContent) -> buildView(oldContent, newContent));
buildView(null, view.getContent());
view.widthProperty().addListener(it -> updateClip());
view.fadingSizeProperty().addListener(it -> updateClip());
}
private final InvalidationListener translateXListener = it -> updateClip();
private final WeakInvalidationListener weakTranslateXListener = new WeakInvalidationListener(translateXListener);
private void buildView(Node oldContent, Node newContent) {
if (oldContent != null) {
stackPane.getChildren().clear();
oldContent.translateXProperty().removeListener(weakTranslateXListener);
}
if (newContent != null) {
stackPane.getChildren().setAll(newContent);
newContent.translateXProperty().addListener(weakTranslateXListener);
}
updateClip();
}
private void updateClip() {
final MaskedView view = getSkinnable();
Node content = view.getContent();
if (content != null) {
final double fadingSize = view.getFadingSize();
if (content.getTranslateX() < 0) {
leftClip.setFill(new LinearGradient(0, 0, fadingSize, 0, false, CycleMethod.NO_CYCLE, new Stop(0, Color.TRANSPARENT), new Stop(1, Color.BLACK)));
} else {
leftClip.setFill(Color.BLACK);
}
if (content.getTranslateX() + content.prefWidth(-1) > view.getWidth()) {
rightClip.setFill(new LinearGradient(0, 0, fadingSize, 0, false, CycleMethod.NO_CYCLE, new Stop(0, Color.BLACK), new Stop(1, Color.TRANSPARENT)));
} else {
rightClip.setFill(Color.BLACK);
}
}
view.requestLayout();
}
@Override
protected void layoutChildren(double contentX, double contentY, double contentWidth, double contentHeight) {
final double fadingSize = Math.min(contentWidth / 2, getSkinnable().getFadingSize());
stackPane.resizeRelocate(snapPosition(contentX), snapPosition(contentY), snapSpace(contentWidth), snapSpace(contentHeight));
resizeRelocate(leftClip, snapPosition(contentX), snapPosition(contentY), snapSpace(fadingSize), snapSpace(contentHeight));
resizeRelocate(centerClip, snapPosition(contentX + fadingSize), snapPosition(contentY), snapSpace(contentWidth - 2 * fadingSize), snapSpace(contentHeight));
resizeRelocate(rightClip, snapPosition(contentX + contentWidth - fadingSize), snapPosition(contentY), snapSpace(fadingSize), snapSpace(contentHeight));
}
private void resizeRelocate(Rectangle rect, double x, double y, double w, double h) {
rect.setLayoutX(x);
rect.setLayoutY(y);
rect.setWidth(w);
rect.setHeight(h);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.