1. Project introduction
1.1 Background
In the field of modern data visualization, Pie Chart is widely used for its intuitive display of the overall proportion of each part. In order to enhance interactivity and appeal, pie charts are often givenRotateAnimation: Rotate automatically and smoothly, allowing users to focus on sectors from different angles. The rotation angle can highlight data changes, guide viewing order, and enhance interface dynamics. However, to achieve a smooth and interactive pie chart rotation in the Java Swing/Java2D environment, you need to understand the following difficulties:
Angle Mapping: Map time or frames to rotation angle and align correctly with the pie sector.
Drawing order: During the rotation process, correctly handle the drawing order of sectors to avoid confusion in front and rear sector occlusion.
Animation driver:use
Or high-precision timer to control rotational fluency.
Interactive response: Supports pause/continue, direction switching, rate adjustment and drag control.
1.2 Goal
This article will start from scratch and implement one step by stepJava2D Swing EditionofRotable pie chart component, the focus is:
Automatic rotation: Rotate smoothly and continuously at the set rate.
Angle control: You can obtain and set the current rotation angle at any time to achieve "instantaneous jump" or animation transition.
Direction switching: rotate clockwise or counterclockwise to switch dynamically.
Drag and drop control: Mouse drag and drop to control the pie chart angle in real time, interrupt/recover automatic rotation.
Complete package: Provides an easy-to-use API that supports embedding in any Swing interface.
2. Related technologies and knowledge
To achieve the above functions, you need to master and understand the following technical points.
2.1 Java2D drawing basics
Graphics2D
: The core rendering context of Java2D, supporting anti-aliasing, transformation, compounding, etc.Shape structure:
Arc2D
Draw the fan shape,Path2D
Construct the side shape.Anti-aliasing:pass
RenderingHint.KEY_ANTIALIASING
Improve drawing quality.transparency:use
AlphaComposite
Control translucent effects.
2.2 Animation Driver
Swing Timer:
Periodic events are triggered in the event distribution thread (EDT), and the graph is safely refreshed.
Frame rate and rate: Calculate the increment angle per frame based on delay and rotation degree per minute (RPM)
delta = rpm * 360° / (60_000ms / delay)
。Smoothness: Choose the right one
delay
(For example, 16ms≈60FPS or 40ms≈25FPS) Balance smoothness and performance.
2.3 Depth sorting
Although we are demonstrating a 2D pie chart, if you add3D sideorshadow, thenDepth sorting: In each frame, judge the "front and back" relationship based on the current central angle of the sector, first draw the distant sector and then draw the near sector to ensure the natural occlusion effect.
2.4 Interactive processing
Drag and drag the mouse:
MouseListener
+MouseMotionListener
Capture press, drag, and release events, and map drag distance to angle offset in real time.Pause/resume: The automatic rotation stops when dragging starts, and can continue when released.
Direction switching and rate adjustment: Allow callers to change dynamically by exposing the API
rpm
andclockwise
Lottery.
Ideas for realization
Combining the above-mentioned technology stack, we will implement it according to the following ideas:
-
Data Model
Define the internal
PieSlice
Class: Save sectorvalue
、color
、label
、startAngle
、arcAngle
。totalValue
Accumulate all sector values.computeAngles()
Methods assign angles proportionally.
-
Component packaging
-
Inheritance
JPanel
, namedRotatingPieChartPanel
, expose API:addSlice(value, color, label)
setRotateSpeed(rpm)
setClockwise(boolean)
start()
/stop()
setAngle(double)
/getAngle()
Achieve "instantaneous jump".
-
-
Animation and drawing
Create in the constructor
Timer(animationDelay, e->{ advanceOffset(); repaint(); })
。advanceOffset()
according torpm
andclockwise
calculateangleOffset
。paintComponent()
Called indrawPie()
, divided into three steps: Shadow → Side (deep sorting required) → Top.
-
Interaction
-
Add to
MouseAdapter
:mousePressed
Start dragging and record the initialangleOffset
with mouse pointer;mouseDragged
Map to incremental angle according to horizontal displacement, updateangleOffset
andrepaint()
;mouseReleased
End drag and restart the painting.
-
-
Depth sorting
When drawing the side, first copy the sector list and follow each sector.Center anglesort of sine values (or cosine values);
depthKey = ((startAngle + arcAngle/2 + angleOffset))
, draw after the value is greater.
4. Complete implementation of the code
import .*; import .*; import .*; import .*; import .*; import ; /** * RotatingPieChartPanel: Pie Chart component that can be rotated automatically/manually and correctly sorted */ public class RotatingPieChartPanel extends JPanel { /** Internal sector model */ private static class PieSlice { double value; // Sector value Color color; // Sector color String label; // Sector tags double startAngle; // Start angle (degree) double arcAngle; // Sector angle (degree) boolean highlighted; // Is it bright? PieSlice(double value, Color color, String label) { = value; = color; = label; = false; } } private final List<PieSlice> slices = new ArrayList<>(); private double totalValue = 0.0; // Rotary control private double angleOffset = 0.0; // Current offset angle private double rpm = 1.0; // Degrees per minute private boolean clockwise = true; // Rotation direction private Timer animationTimer; // for automatic rotation // 3D effect depth (pixel) private double depth = 50.0; // Drag and drop interaction state private boolean dragging = false; private double dragStartOffset; private Point dragStartPoint; public RotatingPieChartPanel() { setBackground(); setPreferredSize(new Dimension(600, 400)); initInteraction(); } /** Initialize mouse interaction: drag and drop control */ private void initInteraction() { MouseAdapter ma = new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { // Stop automatic rotation and enter drag state stop(); dragging = true; dragStartOffset = angleOffset; dragStartPoint = (); } @Override public void mouseDragged(MouseEvent e) { if (!dragging) return; Point pt = (); double dx = - ; // 0.5 degrees per pixel angleOffset = dragStartOffset + dx * 0.5; repaint(); } @Override public void mouseReleased(MouseEvent e) { dragging = false; start(); // Restore automatic rotation } }; addMouseListener(ma); addMouseMotionListener(ma); } /** Add sector */ public void addSlice(double value, Color color, String label) { (new PieSlice(value, color, label)); totalValue += value; computeAngles(); repaint(); } /** Recalculate sector angle */ private void computeAngles() { double angle = 0.0; for (PieSlice s : slices) { = angle; = / totalValue * 360.0; angle += ; } } /** Set the rotation rate (RPM) */ public void setRotateSpeed(double rpm) { = rpm; if (animationTimer != null && ()) { stop(); start(); } } /** Set rotation direction */ public void setClockwise(boolean cw) { = cw; } /** Set 3D depth */ public void setDepth(double depth) { = depth; repaint(); } /** Start automatic rotation */ public void start() { if (animationTimer != null && ()) return; int delay = 40; // 25 FPS double deltaDeg = rpm * 360.0 / (60_000.0 / delay); animationTimer = new Timer(delay, e -> { angleOffset += (clockwise ? -deltaDeg : deltaDeg); repaint(); }); (); } /** Stop automatic rotation */ public void stop() { if (animationTimer != null) { (); animationTimer = null; } } /** Get the current angle */ public double getAngle() { return angleOffset; } /** Directly set the angle (instantaneous jump) */ public void setAngle(double angle) { = angle % 360.0; repaint(); } @Override protected void paintComponent(Graphics g) { (g); renderPie((Graphics2D) g); } /** Draw pie chart: Shadow → Side (Deep Sort) → Top */ private void renderPie(Graphics2D g2) { // Anti-aliasing (RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); int w = getWidth(), h = getHeight(); double cx = w / 2.0, cy = h / 2.0 - depth / 2.0; double r = (w, h - depth) / 2.0 - 20.0; // 1. Draw shadows drawShadow(g2, cx, cy, r); // 2. Depth sort and draw sides List<PieSlice> sorted = new ArrayList<>(slices); ((this::depthKey)); for (PieSlice s : sorted) { drawSide(g2, cx, cy, r, s); } // 3. Draw the top surface for (PieSlice s : sorted) { drawTop(g2, cx, cy, r, s); } } /** Calculate depth sort key: the sin value of the sector center angle */ private double depthKey(PieSlice s) { double mid = + / 2.0 + angleOffset; return ((mid)); } /** Draw the bottom shadow */ private void drawShadow(Graphics2D g2, double cx, double cy, double r) { Ellipse2D shadow = new ( cx - r, cy + depth - r / 3.0 * 2, 2 * r, r / 2.0 ); Composite old = (); (( AlphaComposite.SRC_OVER, 0.3f )); (); (shadow); (old); } /** Draw sector side */ private void drawSide(Graphics2D g2, double cx, double cy, double r, PieSlice s) { double sa = ( + angleOffset); double ea = ( + + angleOffset); Point2D p1 = new ( cx + r * (sa), cy + r * (sa) ); Point2D p2 = new ( cx + r * (ea), cy + r * (ea) ); Point2D p3 = new ((), () + depth); Point2D p4 = new ((), () + depth); Path2D side = new (); ((), ()); ((), ()); ((), ()); ((), ()); (); (()); (side); if () { (); (new BasicStroke(2)); (side); } } /** Draw the top surface of the sector */ private void drawTop(Graphics2D g2, double cx, double cy, double r, PieSlice s) { Arc2D top = new ( cx - r, cy - r, 2 * r, 2 * r, + angleOffset, , ); (); (top); if () { (); (new BasicStroke(2)); (top); } } // Extensible: Add highlighting and prompting functions} /** * DemoMain: Demo RotatingPieChartPanel usage */ class DemoMain { public static void main(String[] args) { (() -> { RotatingPieChartPanel pie = new RotatingPieChartPanel(); (30, , "red"); (20, , "blue"); (40, , "green"); (10, ,"orange"); (60); (2.5); (false); (); JFrame f = new JFrame("Sortable Pie Chart Example"); (JFrame.EXIT_ON_CLOSE); (pie); (); (null); (true); }); } }
5. Interpretation of method-level functions
-
addSlice(value, color, label)
Create
PieSlice
Object, accumulatedtotalValue
, callcomputeAngles()
Recalculate the angular distribution of all sectors.
-
computeAngles()
Traversal
slices
List, proportional(value / totalValue) * 360°
Assign each sectorarcAngle
, and accumulate in turnstartAngle
。
-
start()
/stop()
use
:
delay = 40ms
,every timeactionPerformed
Calculate increments
double deltaDeg = rpm * 360.0 / (60_000.0 / delay); angleOffset += clockwise ? -deltaDeg : deltaDeg;
- Call
repaint()
Refresh the component.
-
paintComponent(...)
-
First
(g)
Clear the background and callrenderPie(g2)
:Enable anti-aliasing
Computing Center
(cx,cy)
and radiusr
Call
drawShadow
、drawSide
(Deep sort) anddrawTop
-
-
depthKey(PieSlice s)
Calculate the sector center angle:
+ /2 + angleOffset
Take the sine value as the basis for depth sorting (the larger the "front"), and sort the list, ensuring that the side of "back" is drawn first, then the side and top of "front".
-
drawShadow
Draw a translucent black ellipse at the bottom, use
AlphaComposite
Set to 0.3f.
-
drawSide
Calculate two points at the edge of the sector
(p1, p2)
, and extend downwarddepth
Get two points at the bottom(p4, p3)
;Structure
Path2D
Quadrilateral fills darker colors;
-
drawTop
use
Draw the fan-shaped top surface;
-
Drag and drop interaction
mousePressed
Stop automatic rotation and record the initial state;mouseDragged
Updated based on horizontal displacement map to incremental angleangleOffset
;mouseReleased
Restore automatic rotation.
6. Project summary and extended thinking
6.1 Core gains
In-depth understandingJava2DApplication skills in complex dynamic graphics;
masterRotate animationandFrame rate controlImplementation;
Learn to useDepth sortingSolve the problem of rotation occlusion;
familiarDrag and drop interactionIntegration in graphics components.
6.2 Performance optimization suggestions
Shape Cache: Pre-generated for each sector at a fixed angle step size
Path2D
andArc2D
, avoiding the creation of large numbers of objects per frame.Off-screen buffering:use
BufferedImage
orVolatileImage
Render the static part off-screen (shadow, side basic shape), and only dynamically draw the rotating part.OpenGL Acceleration: Set system properties
-Dsun.=true
Enable hardware acceleration.
6.3 Extended Functions
Gradients and textures: Add gradient fill or map to the fan.
Multi-layer pie chart/ring chart: Supports rings (Donut) or nested pie charts.
Tags and guide lines: Dynamically display the label during rotation, and the guide wire can be displayed optionally.
JavaFX version: Higher performance and lighting effects based on JavaFX Canvas or 3D API.
The above is the detailed explanation of the code for Java to implement pie chart rotation angle. For more information about Java pie chart rotation angle, please pay attention to my other related articles!