Create Jazzy and Jazzier Bar Charts

Learn how to create two animated bar charts with simple JavaFX applications.

Two Animated Bar Charts

The simple bar chart in the following applet shows what percent of developers use each of several standard programming languages.

The second applet shows a bar chart that has the appearance of tubes filling with liquid. We'll call this a "tube chart," to distinguish it from the first bar chart. The graph shows the price of gasoline or petrol in a few countries around the world. The animation is slowed down a bit so that you have more time to process what is happening behind the scenes.


Both charts include a custom window title with an "x" button to close the graph.

The bar charts in this article have elements in common with the line graph presented in the article "An Easy Animated Line Graph," which is described in detail for beginners. See that article for more details about how the code works.

How the Simple Bar Chart Works

The simple bar chart application (called MyBarGraph in the zipped project files) contains a simple scene in which all of the bars are animated at the same time until they reach their correct value. The scene in Main.fx contains objects that provide the following:

  • The x-axis and y-axis lines with values labeled.
  • A sequence variable called bargraph, which is created from the Bar class (details follow).
  • A Reload button, plus the mouse events to play the timeline from the beginning.
  • A custom window title plus the mouse events to move the window around on the screen.
  • A custom "x" button in the window title, plus the mouse events to close the window.

The code is commented so that you can review this functionality. Including bargraph as an object in the scene is all that is required to draw and animate the bars, as defined in the Bar class.

The bargraph variable contains the data points plus other information from the Bar class, as shown in the following source code.

Source Code: Bar Chart Variables in Main.fx
/* Percentage usages */
var bars: Integer[] = [15,20,25,15,8,7,10];

var bar_names: String[] = ["C++","C","Java","Perl","PHP","SQL","Others"];

/* instance of Bar */
var bargraph: Bar[];

for(i in [0..6]){
    insert
    Bar {
        X: 100 + i * 50
        Y: 150
        height: bars[i]
        txt: bar_names[i]
    } into bargraph

}               

The data points are initialized in the bars variable, and the bar labels are initialized in the bar_names variable. The variable values that will be used in the Bar class (defined in Bar.fx) are written to the bargraph sequence variable. Each item in this sequence includes the height of the bar, taken from the bars[] sequence, the bar labels, and X and Y values, which are bound to the x and y coordinates for the rectangle that defines the bars. These same variables are declared as instance variables in the Bar class. The values of the instance variables will only be used in case a value in the bargraph[] sequence is null.

The custom Bar class includes the timeline, the rectangle that defines the bars, and the text that will appear under each bar.

The timeline code is shown in the following code snippet from Bar.fx.

Source Code: The Timeline for Animation
public var t = Timeline {
        repeatCount: 1
        keyFrames: [
            KeyFrame {
                time: 5s
                canSkip: true
                values: [
                    variable => height tween Interpolator.LINEAR
                ]
            }
        ]
    }
  

The values variable in the KeyFrame instance shows that the animation progresses from variable (initialized to zero) to height (the height of the bar from the bargraph[] variable) at a constant speed. The value of variable increases as the timeline progresses until it reaches the value of height. Becaue the full height of each bar is achieved in the duration specified in the KeyFrame, the taller bars are drawn faster than the shorter bars.

The vertical upwards animation is controlled by the translateY variable in the Rectangle object instance, as follows.

Source Code: The translateY Variable in the Rectangle Instance for Bar.fx
Rectangle {
    translateY: bind 50 - variable * 5

By default, vertical animation occurs from top to bottom, and this order can be reversed by subtracting variable, used as the value in KeyFrame, from the starting point. The x-axis is drawn at position y=50, so the translateY variable starts at 50 and subtracts the increasing value of variable to go up instead of down. The height of the bar is multiplied by a factor of 5 to scale correctly.

How the Tube Chart Works

The tube chart has many similarities to the previous bar chart application. A custom TubeFill class is defined, which contains the timeline plus instances of the objects that will be drawn. In Main.fx, a tubes[]sequence is declared, and values that will apply to the TubeFill class are inserted.

Source Code: How the tubes[] Variable is Populated in Main.fx
var tubes: TubeFill[];
var height: Number[] = [120.0, 100.0, 50.0, 90.0];
var name: String[] = ["India", " USA", "Brazil", "Australia"];
for(i in [0..3]){
    insert
    TubeFill {
        translateX: -80 + i * 140
        translateY: -40
        fillHeight: height[i]
        grad: LinearGradient {
        startX: 0.0
        startY: 0.0
        endX: 1.0
        endY: 0.0
        stops: [
            Stop {
                color: Color.BLACK  
                offset: 0.0
            },
            Stop {
                color: Color.SADDLEBROWN  
                offset: 0.5
            },
            Stop {
                color: Color.BLACK  
                offset: 1.0
            },
        ]
    }
    } into tubes

}

In this graph, a y-axis with values is drawn for each bar, so the y-axis is defined in the TubeFill class rather than in Main.fx.

For both the tubes and the liquid, a rectangle is defined for the column, with an ellipse at the top. In both cases, the ShapeSubtract class is used to cut out the portion of the rectangle that is occupied by the ellipse.

The KeyFrame instance in the timeline contains two values.

Source Code: The Two Values in the KeyFrame Instance in TubeFill.fx
KeyFrame {
    time: 30s
    canSkip: true
    values: bind [
        fill => fillHeight tween Interpolator.LINEAR
        op => 1.0 tween Interpolator.LINEAR
    ]
}

The fill => fillHeight value controls the animation of each bar to its proper height. The vertical animation is achieved by the translateY variable, defined in both the ShapeSubtract instance and the Ellipse instance defined for the liquid.

The op => 1.0 value increases the opacity of the ellipse over the timeline, which makes the ellipse invisible when the height of the bar is zero and also provides a more realistic appearance. The op variable is given its value in the ellipse instance for the liquid.

Source Code: The translateY Variable in the Ellipse Object Instance in TubeFill.fx
Ellipse {
    translateY: bind 91 - fill
    opacity: bind op
                    

Try It

Here are some suggestions for ways that you can modify the code to experiment with the applications.

Bar Chart

  • Change the animation so that the bars are drawn one at a time.
  • Add data for one more bar.

Tube Chart

  • Remove the translateY variable from the Ellipse and the ShapeSubtract instances for the liquid one at a time. See how the change affects the result.
  • Change the color of the tubes.
  • Change the opacity of the background rectangle and other objects, one at a time. See how each change affects the result.

Related Links