Search code examples
javafxscene

How I can hide the part of element that is outside of Scene? JavaFX


This is a situation very hard to explain , i'm going to do the best.

I need to do a circular Pane or a Scene that hide the part of the element (node) that is outside of the area of the Circle. I mean, I don't want to HIDE the FULL ELEMENT only the zone outside. Like a ".setClip". I cant use this last method, because the project is an animation and when I move the node the clip move too.

This is what I want to make, when the text come up.

Example

Here is a image from my project. The red's marks show the problem.

enter image description here

Thanks!

PD: Here is the DocumentController Link


Solution

  • What's wrong with setClip()? If the following doesn't give you the answer you need, please provide a full code example which shows the problem.

    Clip the image (ie without text)

    import javafx.application.Application;
    import javafx.scene.Group;
    import javafx.scene.Node;
    import javafx.scene.Scene;
    import javafx.scene.SnapshotParameters;
    import javafx.scene.control.Label;
    import javafx.scene.effect.DropShadow;
    import javafx.scene.image.ImageView;
    import javafx.scene.image.WritableImage;
    import javafx.scene.layout.Pane;
    import javafx.scene.paint.Color;
    import javafx.scene.shape.Circle;
    import javafx.scene.text.Font;
    import javafx.stage.Stage;
    
    public class ClipImage extends Application {
    
        @Override
        public void start(Stage primaryStage) {
    
            Group root = new Group();
    
            // background image
            ImageView imageView = new ImageView( "http://upload.wikimedia.org/wikipedia/commons/thumb/4/41/Siberischer_tiger_de_edit02.jpg/800px-Siberischer_tiger_de_edit02.jpg");
    
            // some text
            Label label = new Label( "This is a Tiger. Tigers are awesome!");
            label.relocate(20, 400);
            label.setTextFill(Color.RED);
            label.setFont(new Font("Tahoma", 48));
    
            root.getChildren().addAll( imageView, label);
    
            Scene scene = new Scene( root, 1024, 768);
    
            primaryStage.setScene( scene);
            primaryStage.show();
    
            // pane with clipped area
            CirclePane circlePane = new CirclePane();
            makeDraggable( circlePane);
    
            root.getChildren().addAll( circlePane);
    
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    
    
        private class CirclePane extends Pane {
    
            public CirclePane() {
    
                // load image
    //          ImageView imageView = new ImageView( getClass().getResource("tiger.jpg").toExternalForm());
                ImageView imageView = new ImageView( "http://upload.wikimedia.org/wikipedia/commons/thumb/4/41/Siberischer_tiger_de_edit02.jpg/800px-Siberischer_tiger_de_edit02.jpg");
    
                // create circle
                Circle circle = new Circle( 200);
                circle.relocate(200, 100);
    
                // clip image by circle
                imageView.setClip(circle);
    
                // non-clip area should be transparent
                SnapshotParameters parameters = new SnapshotParameters();
                parameters.setFill(Color.TRANSPARENT); 
    
                // new image from clipped image
                WritableImage wim = null;
                wim = imageView.snapshot(parameters, wim);
    
                // new imageview
                ImageView clippedView = new ImageView( wim);
    
                // some shadow
                clippedView.setEffect(new DropShadow(15, Color.BLACK));
    
                clippedView.relocate( 200, 100);
    
                getChildren().addAll( clippedView);
    
            }
    
        }
    
        // make node draggable
        class DragContext { 
            double x;
            double y; 
        } 
    
        public void makeDraggable( Node node) {
    
            final DragContext dragDelta = new DragContext();
    
            node.setOnMousePressed(mouseEvent -> {
    
                dragDelta.x = node.getBoundsInParent().getMinX() - mouseEvent.getScreenX();
                dragDelta.y = node.getBoundsInParent().getMinY() - mouseEvent.getScreenY();
    
            });
    
            node.setOnMouseDragged(mouseEvent -> node.relocate( mouseEvent.getScreenX() + dragDelta.x, mouseEvent.getScreenY() + dragDelta.y));
    
        }
    
    
    }
    

    enter image description here

    Just click on the face and drag it around.

    Or you snapshot the entire scene and clip it, so you have everything in the new image:

    import javafx.application.Application;
    import javafx.scene.Group;
    import javafx.scene.Node;
    import javafx.scene.Scene;
    import javafx.scene.SnapshotParameters;
    import javafx.scene.control.Label;
    import javafx.scene.effect.DropShadow;
    import javafx.scene.image.ImageView;
    import javafx.scene.image.WritableImage;
    import javafx.scene.layout.Pane;
    import javafx.scene.paint.Color;
    import javafx.scene.shape.Circle;
    import javafx.scene.text.Font;
    import javafx.stage.Stage;
    
    public class ClipScene extends Application {
    
        Stage primaryStage;
    
        @Override
        public void start(Stage primaryStage) {
    
            this.primaryStage = primaryStage;
    
            Group root = new Group();
    
            // background image
    //      ImageView imageView = new ImageView( getClass().getResource("tiger.jpg").toExternalForm());
            ImageView imageView = new ImageView( "http://upload.wikimedia.org/wikipedia/commons/thumb/4/41/Siberischer_tiger_de_edit02.jpg/800px-Siberischer_tiger_de_edit02.jpg");
    
            // some text
            Label label = new Label( "This is a Tiger. Tigers are awesome!");
            label.relocate(20, 400);
            label.setTextFill(Color.RED);
            label.setFont(new Font("Tahoma", 48));
    
            root.getChildren().addAll( imageView, label);
    
            Scene scene = new Scene( root, 1024, 768);
    
            primaryStage.setScene( scene);
            primaryStage.show();
    
            // pane with clipped area
            CirclePane circlePane = new CirclePane();
            makeDraggable( circlePane);
    
            root.getChildren().addAll( circlePane);
    
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    
    
        private class CirclePane extends Pane {
    
            public CirclePane() {
    
                WritableImage wim = null;
    
                // load image
                wim = primaryStage.getScene().snapshot( wim);
    
                // create imageview
                ImageView imageView = new ImageView( wim);
    
                // create circle
                Circle circle = new Circle( 200);
                circle.relocate(200, 100);
    
                // clip image by circle
                imageView.setClip(circle);
    
                // non-clip area should be transparent
                SnapshotParameters parameters = new SnapshotParameters();
                parameters.setFill(Color.TRANSPARENT); 
    
                // new image from clipped image
                wim = imageView.snapshot(parameters, wim);
    
                // new imageview
                ImageView clippedView = new ImageView( wim);
    
                // some shadow
                clippedView.setEffect(new DropShadow(15, Color.BLACK));
    
                clippedView.relocate( 200, 100);
    
                getChildren().addAll( clippedView);
    
            }
    
        }
    
        // make node draggable
        class DragContext { 
            double x;
            double y; 
        } 
    
        public void makeDraggable( Node node) {
    
            final DragContext dragDelta = new DragContext();
    
            node.setOnMousePressed(mouseEvent -> {
    
                dragDelta.x = node.getBoundsInParent().getMinX() - mouseEvent.getScreenX();
                dragDelta.y = node.getBoundsInParent().getMinY() - mouseEvent.getScreenY();
    
            });
    
            node.setOnMouseDragged(mouseEvent -> node.relocate( mouseEvent.getScreenX() + dragDelta.x, mouseEvent.getScreenY() + dragDelta.y));
    
        }
    
    
    }
    

    enter image description here

    Here's the animated version where the background scrolls vertically:

    import javafx.animation.AnimationTimer;
    import javafx.application.Application;
    import javafx.scene.Group;
    import javafx.scene.Node;
    import javafx.scene.Scene;
    import javafx.scene.SnapshotParameters;
    import javafx.scene.effect.DropShadow;
    import javafx.scene.image.ImageView;
    import javafx.scene.image.WritableImage;
    import javafx.scene.layout.Pane;
    import javafx.scene.paint.Color;
    import javafx.scene.shape.Circle;
    import javafx.stage.Stage;
    
    public class ClipImage extends Application {
    
        Stage primaryStage;
    
        @Override
        public void start(Stage primaryStage) {
    
            this.primaryStage = primaryStage;
    
            Group root = new Group();
    
            // background image
            ImageView imageView = new ImageView( "http://upload.wikimedia.org/wikipedia/commons/thumb/5/58/Sunset_2007-1.jpg/640px-Sunset_2007-1.jpg");
    
            root.getChildren().addAll( imageView);
    
            Scene scene = new Scene( root, 1024, 768);
    
            primaryStage.setScene( scene);
            primaryStage.show();
    
            // pane with clipped area
            CirclePane circlePane = new CirclePane();
            makeDraggable( circlePane);
    
            root.getChildren().addAll( circlePane);
    
            AnimationTimer timer = new AnimationTimer() {
    
                @Override
                public void handle(long now) {
                    circlePane.scroll();                
                }
    
            };
            timer.start();
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    
    
        private class CirclePane extends Pane {
    
            double y = 0;
            ImageView clippedView;
            ImageView imageView;
            Circle circle;
            SnapshotParameters parameters;
            WritableImage wim = null;
    
            boolean directionForward = true;
    
            public CirclePane() {
    
                imageView = new ImageView( "http://upload.wikimedia.org/wikipedia/commons/thumb/5/58/Sunset_2007-1.jpg/640px-Sunset_2007-1.jpg");
    
                // create circle
                circle = new Circle( 100);
                circle.relocate(200, 100);
    
                // clip image by circle
                imageView.setClip(circle);
    
                // non-clip area should be transparent
                parameters = new SnapshotParameters();
                parameters.setFill(Color.TRANSPARENT); 
    
                // new image from clipped image
                wim = null;
                wim = imageView.snapshot(parameters, wim);
    
                // new imageview
                clippedView = new ImageView( wim);
    
                // some shadow
                clippedView.setEffect(new DropShadow(15, Color.BLACK));
    
                clippedView.relocate( 150, 100);
    
                getChildren().addAll( clippedView);
    
            }
    
            public void scroll() {
    
                if( directionForward) {
                    y++;
                    if( y > 100) {
                        directionForward = false;
                    }
                } else {
                    y--;
                    if( y < 0) {
                        directionForward = true;
                    }
                }
    
                circle.relocate(150, 100 + y);
                imageView.setClip(circle);
                wim = imageView.snapshot(parameters, wim);
                clippedView.setImage( wim);
            }
    
        }
    
        // make node draggable
        class DragContext { 
            double x;
            double y; 
        } 
    
        public void makeDraggable( Node node) {
    
            final DragContext dragDelta = new DragContext();
    
            node.setOnMousePressed(mouseEvent -> {
    
                dragDelta.x = node.getBoundsInParent().getMinX() - mouseEvent.getScreenX();
                dragDelta.y = node.getBoundsInParent().getMinY() - mouseEvent.getScreenY();
    
            });
    
            node.setOnMouseDragged(mouseEvent -> node.relocate( mouseEvent.getScreenX() + dragDelta.x, mouseEvent.getScreenY() + dragDelta.y));
    
        }
    
    
    }
    

    enter image description here

    And the same with text, in which the scene is used to create a combined snapshot of imageview and label.

    import javafx.animation.AnimationTimer;
    import javafx.application.Application;
    import javafx.scene.Group;
    import javafx.scene.Node;
    import javafx.scene.Scene;
    import javafx.scene.SnapshotParameters;
    import javafx.scene.control.Label;
    import javafx.scene.effect.DropShadow;
    import javafx.scene.image.ImageView;
    import javafx.scene.image.WritableImage;
    import javafx.scene.layout.Pane;
    import javafx.scene.paint.Color;
    import javafx.scene.shape.Circle;
    import javafx.scene.text.Font;
    import javafx.stage.Stage;
    
    public class ClipScene extends Application {
    
        Stage primaryStage;
    
        @Override
        public void start(Stage primaryStage) {
    
            this.primaryStage = primaryStage;
    
            Group root = new Group();
    
            // background image
            ImageView imageView = new ImageView( "http://upload.wikimedia.org/wikipedia/commons/thumb/5/58/Sunset_2007-1.jpg/640px-Sunset_2007-1.jpg");
    
            // some text
            Label label = new Label( "Let the sun shine!");
            label.relocate(180, 220);
            label.setTextFill(Color.YELLOW);
            label.setFont(new Font("Tahoma", 18));
    
            root.getChildren().addAll( imageView, label);
    
            Scene scene = new Scene( root, 1024, 768);
    
            primaryStage.setScene( scene);
            primaryStage.show();
    
            // pane with clipped area
            CirclePane circlePane = new CirclePane();
            makeDraggable( circlePane);
    
            root.getChildren().addAll( circlePane);
    
    //      label.setVisible(false);
    //      imageView.setVisible(false);
    
            AnimationTimer timer = new AnimationTimer() {
    
                @Override
                public void handle(long now) {
                    circlePane.scroll();                
                }
    
            };
            timer.start();
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    
    
        private class CirclePane extends Pane {
    
            double y = 0;
            ImageView clippedView;
            ImageView imageView;
            Circle circle;
            SnapshotParameters parameters;
            WritableImage wim = null;
    
            boolean direction = true;
    
            public CirclePane() {
    
                WritableImage wim = null;
    
                // load image
                wim = primaryStage.getScene().snapshot( wim);
    
                imageView = new ImageView( wim);
    
                // create circle
                circle = new Circle( 100);
                circle.relocate(200, 100);
    
                // clip image by circle
                imageView.setClip(circle);
    
                // non-clip area should be transparent
                parameters = new SnapshotParameters();
                parameters.setFill(Color.TRANSPARENT); 
    
                // new image from clipped image
                wim = null;
                wim = imageView.snapshot(parameters, wim);
    
                // new imageview
                clippedView = new ImageView( wim);
    
                // some shadow
                clippedView.setEffect(new DropShadow(15, Color.BLACK));
    
                clippedView.relocate( 150, 100);
    
                getChildren().addAll( clippedView);
    
            }
    
            public void scroll() {
    
                if( direction) {
                    y++;
                    if( y > 100) {
                        direction = false;
                    }
                } else {
                    y--;
                    if( y < 0) {
                        direction = true;
                    }
                }
    
                circle.relocate(150, 100 + y);
                imageView.setClip(circle);
                wim = imageView.snapshot(parameters, wim);
                clippedView.setImage( wim);
            }
    
        }
    
        // make node draggable
        class DragContext { 
            double x;
            double y; 
        } 
    
        public void makeDraggable( Node node) {
    
            final DragContext dragDelta = new DragContext();
    
            node.setOnMousePressed(mouseEvent -> {
    
                dragDelta.x = node.getBoundsInParent().getMinX() - mouseEvent.getScreenX();
                dragDelta.y = node.getBoundsInParent().getMinY() - mouseEvent.getScreenY();
    
            });
    
            node.setOnMouseDragged(mouseEvent -> node.relocate( mouseEvent.getScreenX() + dragDelta.x, mouseEvent.getScreenY() + dragDelta.y));
    
        }
    
    
    }
    

    enter image description here

    And here's all combined, where the animated sunset is "rendered" offscreen, ie we use a Pane for the snapshot, not the Scene:

    import javafx.animation.AnimationTimer;
    import javafx.application.Application;
    import javafx.scene.Group;
    import javafx.scene.Node;
    import javafx.scene.Scene;
    import javafx.scene.SnapshotParameters;
    import javafx.scene.control.Label;
    import javafx.scene.effect.DropShadow;
    import javafx.scene.image.ImageView;
    import javafx.scene.image.WritableImage;
    import javafx.scene.layout.Pane;
    import javafx.scene.paint.Color;
    import javafx.scene.shape.Circle;
    import javafx.scene.text.Font;
    import javafx.scene.text.Text;
    import javafx.stage.Stage;
    
    public class ClipOffScreen extends Application {
    
        @Override
        public void start(Stage primaryStage) {
    
            Group root = new Group();
    
            // background image
            ImageView imageView = new ImageView( "http://upload.wikimedia.org/wikipedia/commons/thumb/4/41/Siberischer_tiger_de_edit02.jpg/800px-Siberischer_tiger_de_edit02.jpg");
    
            // some text
            Label label = new Label( "This is a Tiger. Tigers are awesome!");
            label.relocate(20, 400);
            label.setTextFill(Color.RED);
            label.setFont(new Font("Tahoma", 48));
    
            root.getChildren().addAll( imageView, label);
    
            Scene scene = new Scene( root, 1024, 768);
    
            primaryStage.setScene( scene);
            primaryStage.show();
    
            // pane with clipped area
            CirclePane circlePane = new CirclePane();
            makeDraggable( circlePane);
    
            root.getChildren().addAll( circlePane);
    
            AnimationTimer timer = new AnimationTimer() {
    
                @Override
                public void handle(long now) {
                    circlePane.scroll();                
                }
    
            };
            timer.start();
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    
    
        private class CirclePane extends Pane {
    
            double y = 0;
            ImageView clippedView;
            ImageView imageView;
            Circle circle;
            SnapshotParameters parameters;
            WritableImage wim = null;
    
            boolean direction = true;
    
            public CirclePane() {
    
                Pane offScreenPane = new Pane();
    
                // background image
                ImageView imageView2 = new ImageView( "http://upload.wikimedia.org/wikipedia/commons/thumb/5/58/Sunset_2007-1.jpg/640px-Sunset_2007-1.jpg");
    
                // some text
                Text text = new Text( "Let the sun shine!");
                text.relocate(180, 220);
                text.setFill(Color.YELLOW);
                text.setFont(new Font("Tahoma", 18));
    
                offScreenPane.getChildren().addAll( imageView2, text);
    
                // non-clip area should be transparent
                parameters = new SnapshotParameters();
                parameters.setFill(Color.TRANSPARENT); 
    
                WritableImage wim = null;
    
                // load image
                wim = offScreenPane.snapshot( parameters, wim);
    
                imageView = new ImageView( wim);
    
                // create circle
                circle = new Circle( 100);
                circle.relocate(200, 100);
    
                // clip image by circle
                imageView.setClip(circle);
    
    
    
                // new image from clipped image
                wim = null;
                wim = imageView.snapshot(parameters, wim);
    
                // new imageview
                clippedView = new ImageView( wim);
    
                // some shadow
                clippedView.setEffect(new DropShadow(15, Color.BLACK));
    
                clippedView.relocate( 150, 100);
    
                getChildren().addAll( clippedView);
    
            }
    
            public void scroll() {
    
                if( direction) {
                    y++;
                    if( y > 100) {
                        direction = false;
                    }
                } else {
                    y--;
                    if( y < 0) {
                        direction = true;
                    }
                }
    
                circle.relocate(150, 100 + y);
                imageView.setClip(circle);
                wim = imageView.snapshot(parameters, wim);
                clippedView.setImage( wim);
            }
    
        }
    
        // make node draggable
        class DragContext { 
            double x;
            double y; 
        } 
    
        public void makeDraggable( Node node) {
    
            final DragContext dragDelta = new DragContext();
    
            node.setOnMousePressed(mouseEvent -> {
    
                dragDelta.x = node.getBoundsInParent().getMinX() - mouseEvent.getScreenX();
                dragDelta.y = node.getBoundsInParent().getMinY() - mouseEvent.getScreenY();
    
            });
    
            node.setOnMouseDragged(mouseEvent -> node.relocate( mouseEvent.getScreenX() + dragDelta.x, mouseEvent.getScreenY() + dragDelta.y));
    
        }
    
    
    }
    

    The trick is to use Text instead of Label, because Label will be hidden if it isn't visible. I guess that's some kind of JavaFX internal optimization.

    enter image description here