Module 1 Task 3: Adding an Expansion Effect


Launch JavaFX Media Browser Application Download NetBeans Project

Introduction

This task scales all thumbnails proportionately and adds a mouseover effect to individual thumbnails. As shown in Figure 1, the thumbnail beneath the cursor is 25% larger:

Expansion effect on mouseover Figure 1

Adding the expansion effect on the thumbnails presents two challenges.

  • Centering the thumbnail after it has been scaled to fit the fitWidth and fitHeight constraints

  • Handling the expansion such that it doesn't cause the wall to resize (resizing causes the wall to be recentered, which causes the wall to "jump").

Running the Project

  1. Download the Module 1 Task 3 NetBeans project.

  2. Run the project.

    In Module 1 Task 2 all the thumbnails were the same size. In Task 3 the thumbnails are sized proportionately.

  3. Run the cursor across the wall. As the mouse passes over a thumbnail it is enlarged. As the mouse exits it shrinks to normal size.

Architecture

Because you are only adding an additional mouse behavior, you add new functions to Thumbnail.fx and edit Wall.fx. As seen in Figure 2, there are no new files in the project.

Architecture for Module 1 Task 3 Figure 2

Creating a Group of Thumbnails

In Thumbnail.fx, you size the image to fit the thumbnail's dimensions while preserving the image ratio. The expansion effect scales the thumbnail, enlarging it by 25 percent as the mouse passes over, and scaling it to 100 percent on exit.

In the previous task the height of the wall was the height of the thumbnails (THUMB_HEIGHT) plus the THUMB_VERTICAL_SPACING between thumbnail rows and between thumbnails and the frame. Now the expansion effect causes the overall size of the wall to change when a thumbnail in the top or bottom row is expanded. If the expansion is not taken into account, the wall will appear to jump around as it resizes.

In this task the problem is already solved, but taking a step back in the development process is a good way to understand both the problem and the solution. Follow these steps:

  1. Edit Thumbnail.fx.

  2. Use // to comment out passages that refer to Rectangle:

    • Lines 119-123: def boundingRect = Rectangle { ... }
    • Lines 133 and 134: translateX: bind ... and translateY: bind ...
    • Line 172: boundingRect

  3. Run the project.

  4. Mouse over thumbnails in the top or bottom rows. As you mouse over images the wall "jumps". Compare Figure 3 below to Figure 1. Note how both the landscape images on the bottom are shifted to the right instead of being centered in the column.

    Expansion without x and y translate Figure 3

  5. Remove the comments you made in Step 2.

Create a Bounding Rectangle

Because you determine the wall dimensions based on the thumbnail dimensions, you need to accommodate the expansion effect. As defined below, the rectangle is a container for the thumbnail. Multiplying by EXPANDED_THUMB_SCALE ensures the wall size will be correct, and eliminates the "jumping" you saw earlier.

Source Code
 def boundingRect = Rectangle {
       width: Constants.THUMB_WIDTH * Constants.EXPANDED_THUMB_SCALE
       height: Constants.THUMB_HEIGHT * Constants.EXPANDED_THUMB_SCALE
       visible : false
}

Seeing the outline of the boundingRect (or any other invisible rectangle) can be a useful debugging tool. By initializing boundingRect's fill and stroke, and setting visible to true, the outline can be seen:

Source Code
       fill : null
       stroke : javafx.scene.paint.Color.YELLOW
       visible : true

Sizing the Thumbnail and Preserving Aspect Ratio

Now that the rectangle contains the thumbnail you need to size the thumbnail so that its aspect ratio is maintained, then center the thumbnail within the rectangle:

Source Code
 translateX: bind (boundingRect.boundsInLocal.width 
     - imageView.width) / 2.0
 translateY: bind (boundingRect.boundsInLocal.height
     -imageView.height) / 2.0
 fitWidth: Constants.THUMB_WIDTH
 fitHeight: Constants.THUMB_HEIGHT
 preserveRatio: true

Scaling With onMouseEntered and onMouseExited

Thumbnail.fx contains the new onMouseEntered and onMouseExited functions. These functions inherit scaleX and scaleY from javafx.scene.Node, so scaling is done from the center of the object. This is why the boundsInLocal values (used to calculate size) are important.

Source Code
        onMouseEntered: function(evt: MouseEvent) : Void {
            imageView.scaleX = Constants.EXPANDED_THUMB_SCALE;
            imageView.scaleY = Constants.EXPANDED_THUMB_SCALE;
        }

        // Scale the thumbnail back down for normal display when the mouse exits.
        onMouseExited: function(evt: MouseEvent) : Void {
            imageView.scaleX = 1;
            imageView.scaleY = 1;

Note that scaling applies to the imageView only (not the Rectangle you see in the code, or the Thumbnail CustomNode).

Creating a Group

Thumbnail.fx uses a Group in the create() function. Note the reflection effect, which was inside ImageView in Task 2, is now in effect before the group's content is formed. A Group is a node in the scene-graph that has a multiplicity of children. Nodes in a Group can be handled as a single Node. In the following code, the Rectangle is one Node in the Group and imageView is another. The reflection applies to the Group, so any Node in the Group has the reflection effect

Source Code
        Group {
            effect: if (reflect) then reflection else null;
            content: [
                boundingRect,
                imageView
            ]
        }

Try It

  • In Photo.fx, experiment with changing the look and feel.

    • Change the value of gap and observe how this affects the spacing throughout.
    • Change the frame attributes.

  • Move the reflection effect to imageView. What changes need to be made to the boundingRect? (Hint: How does the reflection affect the height of the imageView?)

Rate This Article
Discussion

We welcome your participation in our community. Please keep your comments civil and on point. You may optionally provide your email address to be notified of replies—your information is not used for any other purpose. By submitting a comment, you agree to these Terms of Use.

 

English
日本語
한국어
简体中文
русский
Português do Brasil