Creating a timeline image with JavaFX
To create some timeline images for my book, I created this little JavaFX application to be able to easily update the content and recreate the image. Of course you can do the same in an image editor, but hey I’m a programmer and lazy, so I want a program to do the job for me ;-)
History of programming languages
Other timelines generated with this code below…
Source code
The sources are available on GitHub inside the repository with all the book example code.
The datasets used to generate the timelines, are defined in an enum in the format startYear, endYear, entries(year, label):
LANGUAGE_BIRTHDATES(1990, 2024, Map.ofEntries(
entry(1990, "Ruby\n "),
entry(1991, "Python\n "),
entry(1995, "Java, JavaScript, PHP, Qt"),
entry(2000, "C#"),
entry(2009, "Go"),
entry(2014, "Swift"))),
Drawing the timeline itself is achieved with Lines and a label added to a Pane:
public class JavaTimeline extends Pane {
public JavaTimeline(int width, int height, int offset, DataSet dataSet) {
// Set the background color
this.setStyle("-fx-background-color: #ffffff");
// Draw the main line from left to right
int axisLength = width - (2 * offset);
Line horizontalAxis = new Line(offset, offset * 3, axisLength, offset * 3);
horizontalAxis.setStroke(Color.DARKGREY);
horizontalAxis.setStrokeWidth(5);
this.getChildren().add(horizontalAxis);
// Draw the year lines
int yearsToDraw = dataSet.getEndYear() - dataSet.getStartYear() + 1;
double distanceBetweenYears = axisLength / yearsToDraw;
for (int i = 0; i < yearsToDraw; i++) {
int currentYear = dataSet.getStartYear() + i;
double yearLineX = (offset * 3) + (i * distanceBetweenYears);
Line yearLine = new Line(yearLineX, offset * 2, yearLineX, offset * 4);
yearLine.setStroke(Color.DARKGREY);
yearLine.setStrokeWidth(currentYear % 10 == 0 ? 4 : 2);
this.getChildren().add(yearLine);
// Add a label for every 10 year
if (currentYear % 10 == 0) {
Label yearLabel = new Label(String.valueOf(currentYear));
yearLabel.setLayoutX(yearLineX - 20);
yearLabel.setLayoutY(offset * 5);
yearLabel.setStyle("-fx-font-weight: bold; -fx-font-size: 18px;");
this.getChildren().add(yearLabel);
}
// Add a special notation
String notation = dataSet.getEntries().get(currentYear);
if (notation != null && !notation.isEmpty()) {
Line notationLine = new Line(yearLineX, offset * 5, yearLineX, offset * 9);
notationLine.setStroke(Color.RED);
notationLine.setStrokeWidth(3);
this.getChildren().add(notationLine);
int notationLabelWidth = height - (offset * 9);
Label notationLabel = new Label(String.valueOf(dataSet.getStartYear() + i) + " - " + notation);
notationLabel.setLayoutX(yearLineX);
notationLabel.setLayoutY((offset * 9));
notationLabel.setPrefWidth(notationLabelWidth);
notationLabel.setPrefHeight(40);
notationLabel.setStyle("-fx-font-size: 14px; -fx-text-alignment: left;");
notationLabel.getTransforms().add(new Rotate(70, 0, 20 / 2, 0, Rotate.Z_AXIS));
this.getChildren().add(notationLabel);
}
}
}
}