• Home
  • Events
  • About
  • JavaFX recipes "Dynamic shadows"

    This is the first blogpost of a series about JavaFX related tips and tricks that I came across.

    Today I would like to explain how to add a dynamic shadow to a node. So first of all let me tell you what I mean with dynamic shadow.

    If you take a dial with some kind of pointer it will definitely look better when the pointer has a drop shadow. As an example let me show you two radial gauges.

     

    120705 0002

     

    As you could see on the left dial the pointer has no dropshadow which looks unrealistic whereas the pointer on the right dial has a drop shadow.

    Because the shadow is a result of the light that shines from the top of the dial, it should point always to the bottom of the dial because the position of the light won’t change.

     

    Adding a drop shadow

    Adding a DropShadow to a node in JavaFX is as easy as the following code snippet shows…

     

        Shape shape = new Rectangle(10, 100);

        shape.setEffect(new DropShadow());

     

    This will work but to make it more realistic we will also need to define the lighting that leads to the drop shadow and this could be done like follows…

     

        Light.Distant light = new Light.Distant();

        light.setAzimuth(270);

        Lighting lighting = new Lighting();

        lighting.setLight(light);

        DropShadow shadow = new DropShadow();

        shadow.setInput(lighting);

        shadow.setOffsetX(5);

        shadow.setOffsetY(5);

        shadow.setRadius(5);

        shadow.setBlurType(BlurType.GAUSSIAN);

        shadow.setColor(Color.color(0, 0, 0, 0.45);

        Shape shape = new Rectangle(10, 100);

        shape.setEffect(shadow);

     

    With this code we are able to create a drop shadow that is a result of a semitransparent white light that is placed over the dial.

    So we now know how to create a drop shadow in JavaFX and apply it to a node (e.g. a shape).

     

    Adding a dynamic drop shadow

    The pointer in the dial above is part of a group which will be rotated dependend on the current value of the dial. This could be done by apply a Rotate to the Transform of the pointer group. 

    Let’s take a look at some code to explain the problem…

     

         // create the lighting

        Light.Distant light = new Light.Distant();

        light.setAzimuth(270);

        light.setElevation(50);

        Lighting lighting = new Lighting();

        lighting.setLight(light);


        // create the drop shadow

        DropShadow dropShadow = new DropShadow();

        dropShadow.setInput(lighting);

        dropShadow.setOffsetX(5);

        dropShadow.setOffsetY(5);

        dropShadow.setRadius(5);

        dropShadow.setBlurType(BlurType.GAUSSIAN);

        dropShadow.setColor(Color.color(0, 0, 0, 0.45));


        // define the Rotate transformation

        rotate.setPivotX(100);

        rotate.setPivotY(100);

        rotate.setAngle(90);


        // create the pointer group with the pointer rectangle

        Group pointerGroup = new Group();

        Rectangle iBounds = new Rectangle(200, 200);

        iBounds.setOpacity(0.0);

        Rectangle pointer = new Rectangle(96, 20, 8, 80);

        pointer.setFill(new LinearGradient(96, 0, 104, 0,

                                           false, CycleMethod.NO_CYCLE,

                                           new Stop(0.0, Color.rgb(100, 0, 0)),

                                           new Stop(0.5, Color.rgb(255, 0, 0)),

                                           new Stop(1.0, Color.rgb(100, 0, 0))));

        pointer.setStroke(null);

        pointer.setEffect(dropShadow);

        pointerGroup.getChildren().addAll(iBounds, pointer);

        pointerGroup.getTransforms().add(rotate);

       

        // add the pointer group to a pane and add the pane to the scene

        StackPane pane = new StackPane();

        pane.setPadding(new Insets(5, 5, 5, 5));

        pane.getChildren().add(pointerGroup);

        final Scene scene = new Scene(pane, 200, 200, Color.rgb(250, 250, 250));

        stage.setTitle(“JavaFX recipes”);

        stage.setScene(scene);

        stage.show();

     

    The code above will create a simple rectangle (the pointer) which will be rotated around the center by 90 degrees which will look like this…

    120704 0009

    So far this looks ok, the shadow is at least below the pointer, but if we apply a rotation around 270 degrees you will see the following result…

    120704 0010

    So now it’s clear…the shadow will be rotated in the same way as the pointer which is wrong. The problem is that we apply a rotation to the transformation of the whole group which will also affect the drop shadow of the pointer.

    The solution is really easy, you just have to add another group that contains the pointer group and where we could apply the drop shadow to. You have to modify the code like follows…

     

        // create the lighting

        Light.Distant light = new Light.Distant();

        light.setAzimuth(270);

        light.setElevation(50);

        Lighting lighting = new Lighting();

        lighting.setLight(light);

       

        // create the drop shadow

        DropShadow dropShadow = new DropShadow();

        dropShadow.setInput(lighting);

        dropShadow.setOffsetX(5);

        dropShadow.setOffsetY(5);

        dropShadow.setRadius(5);

        dropShadow.setBlurType(BlurType.GAUSSIAN);

        dropShadow.setColor(Color.color(0, 0, 0, 0.45));

       

        // define the Rotate transformation

        rotate.setPivotX(100);

        rotate.setPivotY(100);

        rotate.setAngle(90);


        // create the pointer group with the pointer rectangle

        Group pointerGroup = new Group();

        Rectangle iBounds = new Rectangle(200, 200);

        iBounds.setOpacity(0.0);

        Rectangle pointer = new Rectangle(96, 20, 8, 80);

        pointer.setFill(new LinearGradient(96, 0, 104, 0,

                                           false, CycleMethod.NO_CYCLE,

                                           new Stop(0.0, Color.rgb(100, 0, 0)),

                                           new Stop(0.5, Color.rgb(255, 0, 0)),

                                           new Stop(1.0, Color.rgb(100, 0, 0))));

        pointer.setStroke(null);    

        pointerGroup.getChildren().addAll(iBounds, pointer);

        pointerGroup.getTransforms().add(rotate);

       

        // add an extra group for the pointer shadow

        Group pointerShadowGroup = new Group(pointerGroup);

        pointerShadowGroup.setEffect(dropShadow);


        // add the pointer shadow group to a pane and add the pane to the scene

        StackPane pane = new StackPane();

        pane.setPadding(new Insets(5, 5, 5, 5));

        pane.getChildren().add(pointerShadowGroup);

        final Scene scene = new Scene(pane, 200, 200, Color.rgb(250, 250, 250));

        stage.setTitle(“JavaFX recipes”);

        stage.setScene(scene);

        stage.show();


    As you could see we only removed the drop shadow from the pointer rectangle, added a new group that contains the pointerGroup and applied the drop shadow to this new group. Because the new group contains the pointer group already, we only have to add the new pointerShadowGroup to the pane.

    The result for 90 degrees now looks like this…

    120704 0011

    As you could see now it looks much better and for the 270 degree rotation it looks like this…

    120704 0012

    This might be a nothing special but if you have the ability to do it right…you should do it right.

    If you have questions or a special problem, please do not hesitate to contact me on gerrit DOT grunwald AT canoo DOT com

     

    I hope this post is useful for one or the other, so keep coding…

     

    Share and Enjoy: These icons link to social bookmarking sites where readers can share and discover new web pages.
    • DZone
    • Y!GG
    • Webnews
    • Digg
    • del.icio.us
    • DotNetKicks
    • Facebook
    • Google Bookmarks
    • Newsrider
    • Twitter
    • YahooBuzz

    Leave a Comment


    + six = 13

    css.php