JavaFX Script Animation

June 22, 2009

Animating with Dynamic Timeline Values in JavaFX ...and a Scientific Fish Story from 1901

ScientificFishStory A scientific fish story, published in 1901, reports that pike fish living in a tank were separated from minnows by a glass plate.  After a while "a strike at the minnow had come to mean a bump on the nose... Instead of imputing their inability to the glass partition, they considered it some new and remarkable property of the minnows themselves."  Consequently the pike stopped trying to strike the minnows, even after the partition was removed.

I first heard that story when a speaker was illustrating the idea that we often get used to "the way things are", and consequently quit pursuing our passions and dreams.  But this isn't a motivational blog, so I'll stop the inspirational stuff right here and tie the fish story in with the subject of today's post :-)

If you've been developing in JavaFX for a while, you may have gotten used to the idea that dynamically changing values in Timeline key frames don't work as expected.  I was in the same boat (tank?), and instead used the action event handler of a KeyFrame when this kind of dynamic capability was required.  I was pleased to find out recently that this is no longer the case, by virtue of JIRA issue RT-2985 being addressed.

As shown in the screenshot below, I've modified the Metronome example to demonstrate this capability:

MetronomeDynVals

Taking a look at the code below, notice that clicking anywhere in the scene causes the values in the second KeyFrame of the Timeline to be assigned the coordinates of the mouse click.  The net effect is that one end of the blue line (which is bound to the variables that are interpolated by the timeline) move to where you clicked the mouse.  Here's the code listing for this example:

/*
* MetronomeDynVals.fx - A simple example of animation using a Timeline
* with dynamic KeyFrame time and values.
*
* Developed 2009 by James L. Weaver jim.weaver [at] javafxpert.com
* as a JavaFX Script SDK 1.2 example
*/
package projavafx.metronome1.ui;

import javafx.animation.KeyFrame;
import javafx.animation.Interpolator;
import javafx.animation.Timeline;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Slider;
import javafx.scene.input.MouseEvent;
import javafx.scene.shape.Line;
import javafx.scene.shape.Rectangle;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

var durMs:Number = 1000;
var xVal:Number = 100;
var yVal:Number = 50;
var endXVal:Number = 300;
var endYVal:Number = 50;
var anim = Timeline {
  keyFrames: [
    KeyFrame {
      time: 0ms
      values: [
        xVal => 100,
        yVal => 50
      ]
    },
    KeyFrame {
      time: bind 2000ms - (durMs * 1ms)
      values: [
        xVal => endXVal tween Interpolator.LINEAR,
        yVal => endYVal tween Interpolator.LINEAR
      ]
    },
    KeyFrame {
      time: bind (2000ms - (durMs * 1ms))*2
      values: [
        xVal => 100,
        yVal => 50
      ]
    }
  ]
  repeatCount: Timeline.INDEFINITE
};

Stage {
  title: "Metronome Dynamic Values"
  visible: true
  scene: Scene {
    width: 400
    height: 500
    content: [
      Rectangle {
        width: 400
        height: 500
        fill: Color.TRANSPARENT
        onMousePressed: function(me:MouseEvent):Void {
          endXVal = me.x;
          endYVal = me.y;
        }
      },
      Line {
        startX: bind xVal
        startY: bind yVal
        endX: 200
        endY: 400
        strokeWidth: 4
        stroke: Color.BLUE
      },
      HBox {
        layoutX: 60
        layoutY: 420
        spacing: 10
        blocksMouse: true
        content: [
          Button {
            text: "Start"
            disable: bind anim.running
            action: function():Void {
              anim.playFromStart();
            }
          },
          Button {
            text: "Stop"
            disable: bind not anim.running
            action: function():Void {
              anim.stop();
            }
          },
          Slider {
            min: 100
            max: 2000
            width: 500
            value: bind durMs with inverse
          },
        ]
      }
    ]
  }
}



Dynamically Altering the time Variable of a KeyFrame

In addition to dynamically changing the values of a KeyFrame, you can also alter the value of its time variable.  To demonstrate this, the value of the Slider is bi-directionally bound to a variable from which the time value of the second KeyFrame is calculated.  Consequently, moving the slider changes the speed of the animation.

By the way, I didn't supply a Web Start link, because I'd like to encourage you to compile and run the application.  If you'd like a Web Start link, please leave a comment.

Regards,

Jim Weaver

P.S. Thanks to Eric M. Smith (who *does* have a motivational blog) for adding a third KeyFrame to this example (see his comment to this post).  This addition makes the behavior more metronome-like, and if you click in the lower-right area the pendulum exhibits some John Travolta/Saturday Night Fever dance-action :-)

June 16, 2009

Painting with JavaJeff (JavaFX Jeff?)

One of the great features of JavaFX is that it can instantiate and call methods of Java classes.  Java author "JavaJeff" Friesen has created a Painter/Canvas infrastructure using JavaFX and Java.  This infrastructure supports complex/fast painting possibilities such as fireworks, plasma, fractals, and even fire.

PlasmaPainter

Check out Jeff's blog post that provides the code, an explanation, and a Web Start link for the "plasma" painting example shown in the screenshot above.

Regards,
Jim Weaver


April 01, 2009

Drop the Checkered Flag: Solution to JavaFX Puzzler #2

Checkered-flag Yesterday I challenged the readers of this blog with JavaFX Puzzler #2, which calls for creating a "figure 8" track around which a race car travels.  Please take a look at that post for the requirements, as well as the selection criteria for the winners.

I am pleased to announce that the winners of this JavaFX Puzzler are Fei Cheng and Marc Brideau, and that they each will receive a coupon with which they can get the Pro JavaFX early access eBook for free.  Here is the screenshot and code for the example solution:


PathTransition


package javafxpassionpuzzler2;

import javafx.animation.Interpolator;
import javafx.animation.Timeline;
import javafx.animation.transition.AnimationPath;
import javafx.animation.transition.OrientationType;
import javafx.animation.transition.PathTransition;
import javafx.scene.Scene;
import javafx.scene.input.*;
import javafx.scene.shape.*;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

var ellipse: Ellipse;
var path: Path = Path {
  elements: [
    MoveTo {
      x: 200
      y: 200
    },
    ArcTo {
      x: 100
      y: 100
      radiusX: 50
      radiusY: 50
      sweepFlag: false
    },
    ArcTo {
      x: 200
      y: 200
      radiusX: 50
      radiusY: 50
      sweepFlag: false
    },
    ArcTo {
      x: 300
      y: 300
      radiusX: 50
      radiusY: 50
      sweepFlag: true
    },
    ArcTo {
      x: 200
      y: 200
      radiusX: 50
      radiusY: 50
      sweepFlag: true
    },
  ]
}

var anim = PathTransition {
  duration: 10s
  node: bind ellipse
  path: AnimationPath.createFromPath(path)
  orientation: OrientationType.ORTHOGONAL_TO_TANGENT
  interpolate: Interpolator.LINEAR
  repeatCount: Timeline.INDEFINITE
};

Stage {
  title: "PathTransition Puzzler"
  scene: Scene {
    width: 400
    height: 400
    content: [
      path,
      ellipse = Ellipse {
        centerX: 200
        centerY: 200
        radiusX: 20
        radiusY: 10
        fill: Color.BLUE
        onMousePressed: function(me:MouseEvent):Void {
          if (anim.paused) {
            anim.play();
          }
          else {
            anim.pause();
          }
        }
      }
    ]
  }
}
anim.playFromStart();

Please leave a comment if you have any questions about this solution. Congratulations to Fei Cheng and Marc Brideau!

Regards,
Jim Weaver
JavaFXpert.com

March 02, 2009

Pro JavaFX Released in Apress Alpha (Early Access) Program

Pro-javafx-alpha As many of you know, Weiqi Gao, Stephen Chin, Dean Iverson and I have been writing a book entitled  Pro JavaFX that is targeted at serious developers who want to use JavaFX.  In addition to the book being available in printed form by JavaOne 2009, we made the decision last week to allow the book to be offered as an Apress Alpha (early access) eBook.  This enables you to see the chapters of the book as drafts are submitted and revised, which gives you access to the content as soon as it is created (typos and all).  This also enables us to fine-tune the material based upon your feedback.

Here's the list of the chapters for the book:

  1. Getting a Jump Start in JavaFX
  2. Taking a Closer look at the JavaFX Language
  3. Creating a User Interface in JavaFX
  4. Using Functions, Classes and Other Advanced Features
  5. Creating Custom UI Components in JavaFX
  6. Using the Media Classes
  7. Dynamically Laying out Nodes in the User Interface
  8. Extending JavaFX with Third-Party Libraries
  9. Building a Professional JavaFX Application
  10. Developing JavaFX Mobile Applications

Currently, seven chapters (1, 2, 3, 4, 6, 7, 8) are available for download, and each of these is in first or second draft.  Chapters 5, 9 and 10 will be available soon as first drafts.  Here is a screenshot from Chapter 8 in which a drawing program built in JavaFX (in Chapter 5) is used to draw the shapes available in the JFXtras project:

DrawJFXtras

Weiqi, Stephen, Dean and I welcome you to join us in this journey as we complete the development of what is becoming a definitive treatment of JavaFX for professional developers.

For therapeutic purposes I'm sure, Stephen Chin wrote a quick blog post that gives an "exclusive, behind-the-scenes look into the making of the Pro JavaFX™ Platform book" :-)

Regards,
Jim Weaver
JavaFXpert.com

February 05, 2009

Vaibhav Gets Physical in JavaFX

Vaibhav-4 Note to the students of the JavaFX with Passion! course:  Please run and examine the code for the examples referenced in this post.  The next "extra credit" assignment may use concepts contained in these examples ;-)

 Vaibhav Choudhary has compiled some JavaFX samples from his previous blog posts into one post.  Some of them use formulas from a physics textbook to simulate gravity, etc. 

 

Vaibhav-6 Vaibhav has provided Web Start links to each of the examples, as well as links to the code and underlying explanations for the examples.

Thanks, Vaibhav, for these fun, instructive, examples!

Jim Weaver

February 04, 2009

Devoxx Tech Talk on JavaFX SDK 1.0 by Bair, Brehovsky and Potts

I *really* wanted to attend Devoxx 2008, and would have loved to have seen the Tech Talk on JavaFX SDK 1.0 by Richard Bair, Martin Brehovsky and Jasper Potts.  These three are brilliant JavaFX engineers whose main focus is graphics, effects and animation.  The talk that they gave provides a jump start on JavaFX and then concentrates on their areas of expertise, building a cool graphical demo in the process.


Devoxx_2009_JavaFX

Note: Although Jasper played a big role in preparing the presentation, only Richard and Martin gave the talk, because Jasper had been up most of the night, reportedly "working on another demo for Devoxx." ;-)

Devoxx_2009_JavaFX_demo

Thanks and congratulations to Richard, Martin and Jasper for these great resources!

Regards,
Jim Weaver

P.S. Jasper really was working on a demo, screenshot shown below, that you you can learn more about here.

Devoxx_2009_JavaFX_swish_demo

January 25, 2009

Spotlight Effects with JavaFX

By: Dean Iverson



Puppy_spotlight

Today I want to write about the Group class. The Group class is a type of Node that holds a reference to a sequence of child nodes. When nodes are grouped together like this they can be transformed (translated, rotated, scaled) all at once. It is very common to see the Group class in any JavaFX program. It is a workhorse in the field of GUIs: faithful, reliable, utilitarian. Boring? Not on your life! Lurking under that plain exterior is a facilitator of sexy GUIs just waiting to be unleashed.

Blend, Baby

A quick look at the documentation for the Group node will reveal two variables in the class (not counting the ones inherited from Node, of course). The first is the aforementioned sequence of nodes held by the contents variable. But the other is a variable called blendMode. It is of type BlendMode, which is a Java enumeration. There are 19 different blend modes that range from the obvious to the obscure. It may not look like much, but there is a lot of special effects potential contained in those 19 enums.

If you take a black rectangle and blend it with a circle that contains a radial gradient... Presto! Instant spotlight. If you then take this spotlight and blend it with other nodes... Well, cool things start to happen.

A First Experiment

Puppies For our first spotlight experiment, let's use these cute puppies. Aren't they adorable? Don't worry, I promise that no harm will come to them during these experiments. All we're going to do is shine a spotlight on them. Well, about 19 different spotlights to be exact. I wrote a little program that demonstrates what affect each of the 19 different blend modes has on our spotlight. Here is the source code:

var blendModes = BlendMode.values();
var blendModeIndex = 5;

var spotlightX = 0.0;
var spotlightY = 0.0;

var puppyImage = Image {
  url: "{__DIR__}puppies.jpg"
}

Stage {
  title: "Spotlighting Group Blend Modes"
  scene: Scene {
    content: [
      Group {
        blendMode: bind blendModes[blendModeIndex]
        content: [
          ImageView {
            image: puppyImage
          },
          Group {
            id: "spotlight"
            blendMode: BlendMode.DIFFERENCE
            content: [
              Rectangle {
                width: bind puppyImage.width
                height: bind puppyImage.height
                fill: Color.BLACK;
                onMouseClicked: function( me:MouseEvent ) {
                  if (blendModeIndex == (sizeof blendModes) - 1) {
                    blendModeIndex = 0;
                  } else {
                    ++blendModeIndex;
                  }
                }
              },
              Circle {
                translateX: bind spotlightX
                translateY: bind spotlightY
                radius: 100
                fill: RadialGradient {
                  centerX: 0.5
                  centerY: 0.5
                  stops: [
                    Stop { offset: 0.1, color: Color.WHITE },
                    Stop { offset: 0.5, color: Color.BLACK },
                  ]
                }
              }
            ]
          }
        ]
      },
      Text {
        x: 10
        y: 30
        fill: Color.LIGHTGRAY
        font: Font { size: 24 }
        content: bind blendModes[blendModeIndex].name()
      },
    ]
  },
}

Timeline {
  keyFrames: [
    at(0.0s) { spotlightX => 30; spotlightY => 110 },
    at(0.5s) { spotlightX => 30; spotlightY => 110 },
    at(1.0s) { spotlightX => 125; spotlightY => 84 },
    at(1.5s) { spotlightX => 125; spotlightY => 84 },
    at(2.0s) { spotlightX => 210; spotlightY => 110 },
    at(2.5s) { spotlightX => 210; spotlightY => 110 },
    at(3.0s) { spotlightX => 280; spotlightY => 115 },
    at(3.5s) { spotlightX => 280; spotlightY => 115 },
  ]
  repeatCount: Timeline.INDEFINITE
  autoReverse: true
}.play()

The first thing to notice is that BlendMode is a regular Java enum. Since JavaFX integrates so well with Java, we can call all of the normal enum methods like BlendMode.values() to get a sequence containing all of the blend modes. We can also make use of all the usual methods on the individual members of an enum like BlendMode.SRC_OVER.name() to get the name string of the enum.

Next, check out the group with the id of "spotlight" that is declared within the scene. It consists of a black rectangle that is the same size as the puppy image and a circle that has a radial gradient fill. The circle is translated using the spotlightX and spotlightY variables. These two shapes are blended with the DIFFERENCE blend mode. This works well to create a hole through which to view our puppies.

Finally, the spotlight group is placed in another group with an ImageView that displays the puppy image. The blend mode of this new group is bound to the current index into the blendModes sequence. This index is incremented every time you click on the spotlight group. This way you can just click to cycle through each blend mode to see its effect. Here are some of my favorites.

Multiply Difference
Green Soft_light

You can download the source code for this project here, or just run the project by clicking the following Java Web Start launch icon:

Webstart.small2

Text In The Spotlight

The cool thing about this functionality is that you can use it to blend any nodes that are inserted into a group. In honor of the M3DD Conference I was inspired to use this technique to recreate the famous "slide to unlock" effect of the iPhone. All it takes is some text, a gradient-filled circle, and some animation. This is no problem in JavaFX; the code for the effect is simple.

var spotlightX:Number = 0;
var scene:Scene;
var textNode:Text;

Stage {
  title: "Spotlight Text"
  scene: scene = Scene {
    width: 200
    height: 100
    fill: Color.BLACK
    content: Group {
      blendMode: BlendMode.SRC_ATOP
      content: [
        textNode = Text {
          content: "slide to unlock"
          translateX: bind (scene.width - textNode.layoutBounds.width) / 2
          translateY: bind (scene.height - textNode.layoutBounds.height) / 2
          textOrigin: TextOrigin.TOP
          fill: Color.GRAY
          font: Font { size: 24 }
        },
        Circle {
          radius: 15
          translateX: bind spotlightX
          translateY: bind scene.height / 2
          fill: RadialGradient {
            centerX: 0.5
            centerY: 0.5
            stops: [
              Stop { offset: 0.0, color: Color.WHITE },
              Stop { offset: 0.1, color: Color.WHITESMOKE },
              Stop { offset: 0.5, color: Color.GRAY },
            ]
          }
        }
      ]
    }
  }
}

Timeline {
  keyFrames: [
    at(0s) { spotlightX => textNode.boundsInParent.minX - 30 },
    at(2s) { spotlightX => textNode.boundsInParent.maxX + 30 }
  ]
  repeatCount: Timeline.INDEFINITE
}.play()

The heart of the effect is a group that blends the text node with the circle. Here we use the SRC_ATOP blend mode which effectively clips the spotlight to the shape of the text. The result looks like this:

Slide_to_unlock

It loses something without being able to see the animation, but once again you can download the source code for this project here, or just run the project by clicking the following Java Web Start launch icon:

Webstart.small2

Being able to blend nodes together inside a Group is a powerful feature that we've only scratched the surface of here. I encourage you to play around with the different blend modes and see what cool things you can discover. You may just find that implementing the group and blend technique is the key to making short work of that difficult effect you've been struggling with.

Dean Iverson
JavaFXpert.com

September 04, 2008

JavaFX Applets Meet Google Chrome

In the JFX Custom Nodes category of this blog, graphics designer Mark Dingman of Malden Labs and I have been collaborating on an imaginary Sound Beans application. This category contains a growing series of posts in which we are demonstrating how to create JavaFX UI custom controls.  This series also provide a case study in how a graphics designer and an application developer can work together effectively in developing JavaFX applications.  Today I'd like to highlight the recent Google Chrome browser announcement by showing you how to create and run a JavaFX applet in Chrome.  Here's a screenshot of the TableNode example from an earlier post running as a JavaFX applet in Chrome:


TableNodeExampleApplet

To try this out, first obtain Google Chrome and install it.  Then obtain Java SE 6 Update 10 and install it as well.  By the way, installing Java SE 6 update 10 will enable this JavaFX applet to run on Firefox 3 and Internet Explorer as well.  Go ahead and run this example, being sure to scroll the custom TableNode control and to click on its rows.  Also, select the Burn icon and move the slider to demonstrate the custom ProgressNode control.


Looking at the Code

In addition to the ButtonNode.fx, MenuNode.fx, DeckNode.fx, ProgressNode.fx and TableNode.fx files from previous posts in this series, you'll need the following files:

TableNodeExampleApplet.fx:

/*
 *  TableNodeExampleApplet.fx -
 *  An example of using the TableNode custom node in an Applet.  It also
 *  demonstrates the ProgressNode, DeckNode, MenuNode and ButtonNode
 *  custom nodes
 *
 *  Developed 2008 by James L. Weaver (jim.weaver at lat-inc.com)
 *  to demonstrate how to create custom nodes and applets in JavaFX
 */
package com.javafxpert.table_node_example.ui;

import javafx.application.*;
import javafx.ext.swing.*;
import javafx.scene.*;
import javafx.scene.geometry.*;
import javafx.scene.image.*;
import javafx.scene.layout.*;
import javafx.scene.paint.*;
import javafx.scene.text.*;
import javafx.scene.transform.*;
import java.lang.Object;
import java.lang.System;
import com.javafxpert.custom_node.DeckNode;
import com.javafxpert.custom_node.TableNode;
import com.javafxpert.custom_node.ProgressNode;
import com.javafxpert.custom_node.ButtonNode;
import com.javafxpert.custom_node.MenuNode;
import com.javafxpert.table_node_example.model.TableNodeExampleModel;

var deckRef:DeckNode;

Application {
  var model = TableNodeExampleModel.getInstance();
  var stageRef:Stage;
  var menuRef:MenuNode;
  stage:
    stageRef = Stage {
      fill: Color.BLACK
      content: [
        deckRef = DeckNode {
          fadeInDur: 700ms
          content: [
            // The "Splash" page
            Group {
              var vboxRef:VBox;
              var splashFont =
                Font {
                  name: "Sans serif"
                  style: FontStyle.BOLD
                  size: 12
                };
              id: "Splash"
              content: [
                ImageView {
                  image:
                    Image {
                      url: "{__DIR__}images/splashpage.png"
                    }
                },
                vboxRef = VBox {
                  translateX: bind stageRef.width - vboxRef.getWidth() - 10
                  translateY: 215
                  spacing: 1
                  content: [
                    Text {
                      content: "A Fictitious Audio Application that Demonstrates"
                      fill: Color.WHITE
                      font: splashFont
                    },
                    Text {
                      content: "Creating JavaFX Custom Nodes"
                      fill: Color.WHITE
                      font: splashFont
                    },
                    Text {
                      content: "Application Developer: Jim Weaver"
                      fill: Color.WHITE
                      font: splashFont
                    },
                    Text {
                      content: "Graphics Designer: Mark Dingman"
                      fill: Color.WHITE
                      font: splashFont
                    },
                  ]
                }
              ]
            },
            // The "Play" page
            VBox {
              var tableNode:TableNode
              id: "Play"
              spacing: 4
              content: [
                Group {
                  content: [
                    ImageView {
                      image:
                        Image {
                          url: "{__DIR__}images/playing_currently.png"
                        }
                    },
                    Text {
                      textOrigin: TextOrigin.TOP
                      content: bind "{tableNode.selectedIndex}"
                      font: Font {
                        size: 24
                      }
                    }
                  ]
                },
                tableNode = TableNode {
                  height: 135
                  rowHeight: 25
                  rowSpacing: 2
                  columnWidths: [150, 247, 25, 70]
                  tableFill: Color.BLACK
                  rowFill: Color.#1c1c1c
                  selectedRowFill: Color.#2d2d2d
                  selectedIndex: -1
                  vertScrollbarWidth: 20
                  vertScrollbarFill: LinearGradient {
                    startX: 0.0
                    startY: 0.0
                    endX: 1.0
                    endY: 0.0
                    stops: [
                      Stop {
                        offset: 0.0
                        color: Color.#0b0b0b
                      },
                      Stop {
                        offset: 1.0
                        color: Color.#343434
                      }
                    ]
                  }
                  vertScrollbarThumbFill: Color.#efefef
                  content: bind
                    for (obj in model.playlistObjects) {
                      if (obj instanceof String)
                        Text {
                          textOrigin: TextOrigin.TOP
                          fill: Color.#b7b7b7
                          content: obj as String
                          font:
                            Font {
                              size: 11
                            }
                        }
                      else if (obj instanceof Image)
                        ImageView {
                          image: obj as Image
                        }
                      else
                        null
                    }
                  onSelectionChange:
                    function(row:Integer):Void {
                      System.out.println("Table row #{row} selected");
                    }
                }
              ]
            },
            // The "Burn" page
            Group {
              var vboxRef:VBox;
              id: "Burn"
              content: [
                vboxRef = VBox {
                  translateX: bind stageRef.width / 2 - vboxRef.getWidth() / 2
                  translateY: bind stageRef.height / 2 - vboxRef.getHeight() / 2
                  spacing: 15
                  content: [
                    Text {
                      textOrigin: TextOrigin.TOP
                      content: "Burning custom playlist to CD..."
                      font:
                        Font {
                          name: "Sans serif"
                          style: FontStyle.PLAIN
                          size: 22
                        }
                      fill: Color.#d3d3d3
                    },
                    ProgressNode {
                      width: 430
                      height: 15
                      progressPercentColor: Color.#bfdfef
                      progressTextColor: Color.#0c1515
                      progressText: bind "{model.remainingBurnTime} Remaining"
                      progressFill:
                        LinearGradient {
                          startX: 0.0
                          startY: 0.0
                          endX: 0.0
                          endY: 1.0
                          stops: [
                            Stop {
                              offset: 0.0
                              color: Color.#00c0ff
                            },
                            Stop {
                              offset: 0.20
                              color: Color.#00acea
                            },
                            Stop {
                              offset: 1.0
                              color: Color.#0070ae
                            },
                          ]
                        }
                      barFill:
                        LinearGradient {
                          startX: 0.0
                          startY: 0.0
                          endX: 0.0
                          endY: 1.0
                          stops: [
                            Stop {
                              offset: 0.0
                              color: Color.#707070
                            },
                            Stop {
                              offset: 1.0
                              color: Color.#585858
                            },
                          ]
                        }
                      progress: bind model.burnProgressPercent / 100.0
                    },
                    ComponentView {
                      component:
                        FlowPanel {
                          background: Color.BLACK
                          content: [
                            Label {
                              text: "Slide to simulate burn progress:"
                              foreground: Color.#d3d3d3
                            },
                            Slider {
                              orientation: Orientation.HORIZONTAL
                              minimum: 0
                              maximum: 100
                              value: bind model.burnProgressPercent with inverse
                              preferredSize: [200, 20]
                            }
                          ]
                        }
                    }
                  ]
                }
              ]
            },
            // The "Config" page
            Group {
              id: "Config"
              content: [
                ImageView {
                  image:
                    Image {
                      url: "{__DIR__}images/config.png"
                    }
                }
              ]
            },
            // The "Help" page
            Group {
              id: "Help"
              content: [
                ImageView {
                  image:
                    Image {
                      url: "{__DIR__}images/help.png"
                    }
                }
              ]
            }
          ]
        },
        menuRef = MenuNode {
          translateX: bind stageRef.width / 2 - menuRef.getWidth() / 2
          translateY: bind stageRef.height - menuRef.getHeight()
          buttons: [
            ButtonNode {
              title: "Play"
              imageURL: "{__DIR__}icons/play.png"
              action:
                function():Void {
                  deckRef.visibleNodeId = "Play";
                }
            },
            ButtonNode {
              title: "Burn"
              imageURL: "{__DIR__}icons/burn.png"
              action:
                function():Void {
                  deckRef.visibleNodeId = "Burn";
                }
            },
            ButtonNode {
              title: "Config"
              imageURL: "{__DIR__}icons/config.png"
              action:
                function():Void {
                  deckRef.visibleNodeId = "Config";
                }
            },
            ButtonNode {
              title: "Help"
              imageURL: "{__DIR__}icons/help.png"
              action:
                function():Void {
                  deckRef.visibleNodeId = "Help";
                }
            },
          ]
        }
      ]
    }
}


Note that the Application class has a stage attribute just as the Frame had in previous examples.  Here's the TableNodeExamplePage.html file that you'll open in your browser.  The draggable param, by the way, enables that neat "pull the applet out of the browser" trick that I'll show you in a bit:

<html>
  <body bgcolor="Black">
    <center>
      <applet code="javafx.application.Applet" width=500 height=400
          archive="javafxrt.jar, Scenario.jar, javafxgui.jar, javafx-swing.jar, TableNodeExample.jar">
        <param name="ApplicationClass" value="com.javafxpert.table_node_example.ui.TableNodeExampleApplet"/>
        <param name="jnlp_href" value="TableNodeExampleApplet.jnlp"/>
        <param name="draggable" value="true">
      </applet>
    </center>
  </body>
</html>


Finally, here's the Java Web Start TableNodeExampleApplet.jnlp file that is used by the HTML file above:

<?xml version="1.0" encoding="UTF-8"?>
<jnlp spec="1.0+" href="TableNodeExampleApplet.jnlp">
  <information>
    <title>TableNodeExampleApplet</title>
    <vendor>JMentor</vendor>
    <description>TableNodeExampleApplet</description>
    <description kind="short">TableNodeExampleApplet</description>
    <homepage href="http://jmentor.com"/>
    <offline-allowed />
  </information>
  <security>
    <all-permissions/>
  </security>
  <resources>
    <property name="jnlp.packEnabled" value="true"/>
    <j2se version="1.6+" href="http://java.sun.com/products/autodl/j2se" java-vm-args="-Xmx800m" />
    <!--j2se version="1.6+" java-vm-args="-Xmx800m" /-->
    <jar href="TableNodeExample.jar" main="true" download="eager"/>
    <jar href="Scenario.jar"/>
    <jar href="javafxrt.jar"/>
    <jar href="javafxgui.jar"/>
    <jar href="javafx-swing.jar"/>
  </resources>
  <applet-desc
    name="TableNodeExampleApplet"
    main-class="javafx.application.Applet"
    width="500"
    height="400">
  </applet-desc>
</jnlp>


Dragging the Applet out of the Browser and onto the Desktop

As shown in the following screenshot, one of the cool features of Java SE 6 update 10 is that you can drag a Java or JavaFX applet out of the browser and onto the desktop.  By default, you press the Alt key while dragging the applet:

TableNodeExampleApplet-Drag

Here is our JavaFX Applet living happily on the desktop after the browser has been closed, and the user has selected the Burn page:

TableNodeExampleApplet-Dragged

Google Chrome will be a Driving Force for RIA

According to Google, Java SE 6 Update 10 is the version that must be used in order to run Java in the Chrome browser.  As I've mentioned previously, one of the objectives of Java SE 6 Update 10 is to solve the JRE and Java/JavaFX deployment issues.  Because Google Chrome is destined to be a great, cross-platform browser, and because it requires the version of Java that makes rich-client Java/JavaFX programs feasible, this will increase the adoption rate of JavaFX applets and applications.

Thanks Google!

Jim Weaver
JavaFXpert.com

July 11, 2008

Radar Love: JavaFX Animation Examples by Sven

You may recall that I've featured some of Sven Drieling's work previously in this blog, for example in the JavaFX Animation - Some of Sven's Canvas Tricks post.  Well, Sven has updated his animation examples and added to the collection to help us prepare for the JavaFX SDK.  Here's a screenshot of Sven's AnimationRadar example, in which he animates an arc-shaped clip of an image to look like the sweep of a radar.  Because it's an animation, the running program looks cooler than the static screenshot. :-)

Animationradar

In another example, Sven uses nested Timelines to progressively start three blocks animating.  Here's a screenshot:

Nestedtimelines

Take a look at the page in which Sven stashes his many animation examples, and try out the examples via Java Web Start links on that page while studying his great code.

Thanks Sven!
Jim Weaver
JavaFX Script: Dynamic Java Scripting for Rich Internet/Client-side Applications

May 15, 2008

Video of Cool JavaFX Demos at JavaOne 2008

Note from Jim Weaver: Since this was written, an updated version of JavaFX has been released.  You can get the new version of JavaFX at at JavaFX.com.

During the Tuesday afternoon general session at JavaOne 2008, there were some cool JavaFX-related announcements and demos.  Grab some popcorn and watch this video of a portion of the session, which includes:

  • An announcement that ON2 and Sun now have a relationship that will make the ON2 video codec available as part of the Java and JavaFX platforms.  This will allow video to be converted to a common format and played on any machine or device that supports Java.
  • The JavaFX version of the parleys.com website, which is the Belgium Java Users Group's (JUG) site for sharing video presentations about Java.  Stephan Janssen, who leads the JUG as well as the JavaPolis conference in Belgium, presented the site with Jo Voordeckers, the JavaFX developer of the site.  Stephan also announced that any JUG can post presentation videos to the new parleys.com site, which up to now has received most of its content from the JavaPolis conferences.
  • An early peek at a design tool for JavaFX in which graphics can be created in Photoshop and Illustrator, and exported for use by a JavaFX program.  This tool is currently named Distiller, and will support the collaboration of graphic designers and software developers.  As a side note, ReportMill is also making some great progress on their JavaFX designer tool.
  • A demo of a three-dimensional multi-player game named MoonTank developed in JavaFX.  One of the demonstrators is Chris Oliver, the founder of JavaFX.

Enjoy the video.  This JavaFX stuff is getting exciting!
Jim Weaver
JavaFX Script: Dynamic Java Scripting for Rich Internet/Client-side Applications

Immediate eBook (PDF) download available at the book's Apress site

My Photo

Upcoming Speaking Engagements:


  • Stephen Chin and Jim Weaver speaking about JavaFX Platform

  • Speaking on JavaFX and Java at Øredev in Malmö, Sweden on 2-6 November, 2009

Upcoming JavaFX Training:


  • Developing Secure, Rich Internet Applications Hosted on a Variety of Clients Using JavaFX Technology

Enter your email address:

Delivered by FeedBurner

Available now as early access eBook


  • Click book image above to obtain eBook

Twitter Updates

    follow me on Twitter

    Affiliations:

    DZone Links:


    July 2009

    Sun Mon Tue Wed Thu Fri Sat
          1 2 3 4
    5 6 7 8 9 10 11
    12 13 14 15 16 17 18
    19 20 21 22 23 24 25
    26 27 28 29 30 31  

    Disclaimer:

    • By reading this site, you are agreeing that under no circumstances will Veriana Networks, Inc. or its affiliates be responsible for (1) any information contained on or omitted from the site, (2) any person's reliance on any such information, whether or not the information is correct, current or complete, (3) the consequences of any action you or any other person takes or fails to take, whether or not based on information provided by or as a result of the use of the sites.