Drawing and animation
IA tooling, not an exam topic. These are JavaFX patterns for drawing tools, simulations, and small games. Most form-and-table IAs will not need them. Nothing here is examinable, and your IA must be your own work.
A Canvas is a blank rectangle you draw on directly with shapes, lines, text, and images. This page covers drawing on it, reacting to the mouse, and running a smooth animation loop. Reach for these only if your IA is a drawing tool, a simulation, or a game-style app.
New here? Read Start here: how every recipe fits together first; it explains the two places you edit and the fx:id link every recipe relies on.
Draw shapes and lines on a canvas
What this does: draws shapes, lines, and text onto a Canvas using its GraphicsContext, the object that holds the drawing commands.
When you would use it in your IA: a board for a game, a simple diagram, a chart you want full control over, or any free-form drawing surface.
In SceneBuilder (the layout): add a Canvas, give it an fx:id of canvas, and set its width and height.
<Canvas fx:id="canvas" width="300" height="200" />
In the controller (the .java file):
Imports:
import javafx.fxml.FXML;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;
Controller:
@FXML private Canvas canvas;
@FXML
public void initialize() {
GraphicsContext gc = canvas.getGraphicsContext2D();
// your IA logic here: what to draw
gc.setFill(Color.CORNFLOWERBLUE);
gc.fillRect(20, 20, 100, 60); // x, y, width, height
gc.setStroke(Color.DARKRED);
gc.strokeLine(20, 100, 200, 100); // from (x1, y1) to (x2, y2)
gc.fillOval(140, 20, 50, 50); // x, y, width, height of the bounding box
gc.strokeText("Hello", 20, 150); // text at (x, y)
}
Where your own logic goes: the drawing calls marked // your IA logic here, which are your picture. Getting the GraphicsContext is reusable wiring.
Make it your own:
- Change the colours, coordinates, and sizes for your own drawing.
setFillthen afill...method draws a solid shape;setStrokethen astroke...method draws an outline.
Watch out for:
- The
fx:idcanvasmust match the@FXMLfield name exactly, or the screen will not load. - Coordinates start at
(0, 0)in the top-left corner, andyincreases downward, not upward. This trips up most people once. - A canvas is “paint and forget”: you are drawing pixels, not objects. Once a shape is drawn it has no identity, so to move or remove it you clear the area and draw again. The animation recipe below relies on this.
Mix with: Draw where the user clicks or drags, Run a smooth animation or game loop.
Draw where the user clicks or drags
What this does: reads the mouse position on the canvas and draws there, so the user can click or drag to make marks.
When you would use it in your IA: a paint or sketch tool, placing pieces on a board, or letting the user mark points on a diagram.
In SceneBuilder (the layout): add the Canvas (fx:id canvas) and set both onMousePressed and onMouseDragged to the same handler, handleDraw.
<Canvas fx:id="canvas" width="300" height="200"
onMousePressed="#handleDraw" onMouseDragged="#handleDraw" />
In the controller (the .java file):
Imports:
import javafx.fxml.FXML;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
Controller (one handler covers both a click and a drag):
@FXML private Canvas canvas;
@FXML
private void handleDraw(MouseEvent event) {
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.setFill(Color.BLACK);
// your IA logic here: what to draw at the mouse position
// event.getX() and event.getY() are the mouse position on the canvas.
gc.fillOval(event.getX() - 5, event.getY() - 5, 10, 10);
}
Where your own logic goes: what you draw at the mouse position, marked // your IA logic here. Reading the mouse position from the event is reusable wiring.
Make it your own:
- Change what you draw at the mouse position: a dot, a line from the last point, a game piece.
onMousePressedfires once per click;onMouseDraggedfires repeatedly as the mouse moves with the button down. Using both gives a continuous brush.- Subtracting
5(half the10-wide oval) centres the mark on the cursor.
Watch out for:
- The
fx:idcanvasmust match the@FXMLfield name, andhandleDrawmust exist in the controller with aMouseEventparameter, or the screen will not load. event.getX()andevent.getY()are measured from the canvas’s top-left corner, which is what you want when drawing on it.- For a separate “released” action (for example finishing a line), add
onMouseReleasedpointing at another method.
Mix with: Draw shapes and lines on a canvas, React to the Enter key.
Run a smooth animation or game loop
What this does: runs a piece of code on every frame (about 60 times a second) using an AnimationTimer, which is what gives smooth motion.
When you would use it in your IA: moving a sprite, a physics or particle simulation, or any game loop that updates and redraws continuously.
In SceneBuilder (the layout): add the Canvas (fx:id canvas) you draw on. The loop is created in code.
<Canvas fx:id="canvas" width="300" height="200" />
In the controller (the .java file):
Imports:
import javafx.animation.AnimationTimer;
import javafx.fxml.FXML;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;
Controller:
@FXML private Canvas canvas;
private double ballX = 0;
@FXML
public void initialize() {
GraphicsContext gc = canvas.getGraphicsContext2D();
AnimationTimer loop = new AnimationTimer() {
@Override
public void handle(long now) {
// 1. wipe the canvas so the last frame does not leave a trail
gc.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
// your IA logic here: update positions and draw this frame
gc.setFill(Color.TOMATO);
gc.fillOval(ballX, 80, 30, 30);
ballX += 2;
if (ballX > canvas.getWidth()) {
ballX = 0; // wrap back to the left
}
}
};
loop.start();
}
Where your own logic goes: the per-frame update and drawing, marked // your IA logic here. The loop, the clear, and the start/stop are reusable wiring.
Make it your own:
- Put your own per-frame logic in
handle: move things, check collisions, redraw. - Keep the moving values (positions, speeds) as fields so they carry over between frames.
- Call
loop.stop()to pause the animation, andloop.start()to resume.
Watch out for:
- The
fx:idcanvasmust match the@FXMLfield name exactly, or the screen will not load. - Clear the canvas at the start of each frame, or every drawing stays and you get smears instead of motion.
handleruns around 60 times a second, so keep the work inside it light. Heavy work there makes the whole app stutter.AnimationTimeris the heavier cousin ofTimeline. UseTimelinefor a fixed beat like a one-second countdown, andAnimationTimerwhen you need smooth, every-frame motion.- The
new AnimationTimer() { ... }form is an anonymous class with an overriddenhandlemethod. The override is ordinary OOP; the inline class is a convenience for giving the timer its one method.
Mix with: Do something on a repeating beat, Keep the UI responsive during slow work, Play a sound.