diff --git a/PaintingBoard.iml b/PaintingBoard.iml index c90834f..615e3ef 100644 --- a/PaintingBoard.iml +++ b/PaintingBoard.iml @@ -7,5 +7,6 @@ + \ No newline at end of file diff --git a/src/Codes/shape/AbstractShape.java b/src/Codes/shape/AbstractShape.java index 8fbfd3f..fea9f21 100644 --- a/src/Codes/shape/AbstractShape.java +++ b/src/Codes/shape/AbstractShape.java @@ -66,5 +66,4 @@ public abstract class AbstractShape implements Serializable { */ public abstract void draw(Graphics2D g); - } \ No newline at end of file diff --git a/src/Codes/shape/Brush.java b/src/Codes/shape/Brush.java index a895dfd..596fb10 100644 --- a/src/Codes/shape/Brush.java +++ b/src/Codes/shape/Brush.java @@ -22,13 +22,11 @@ public Brush() { @Override public void draw(Graphics2D g) { - - g.setPaint(color); g.setStroke(new BasicStroke(0, BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL)); for (int i = 0; i < 100; i++) { - double d = (double) fx[i]; - double c = (double) fy[i]; + double d = fx[i]; + double c = fy[i]; g.drawLine((int) (x1 + d * Math.sin(d)), (int) (y1 + c * Math.sin(c)), (int) (x2 + d * Math.sin(d)), (int) (y2 + c * Math.sin(c))); } diff --git a/src/Codes/shape/FillRect.java b/src/Codes/shape/FillRect.java index 1f56087..f9de7de 100644 --- a/src/Codes/shape/FillRect.java +++ b/src/Codes/shape/FillRect.java @@ -2,11 +2,9 @@ import java.awt.*; /** - * + * 填充矩形 */ public class FillRect extends AbstractShape { - - @Override public void draw(Graphics2D g2d) { g2d.setPaint(color); diff --git a/src/Codes/shape/FillRoundRect.java b/src/Codes/shape/FillRoundRect.java index 3593f2a..015982b 100644 --- a/src/Codes/shape/FillRoundRect.java +++ b/src/Codes/shape/FillRoundRect.java @@ -3,10 +3,9 @@ import java.awt.*; /** + * 填充圆角矩形 */ public class FillRoundRect extends AbstractShape { - - @Override public void draw(Graphics2D g2d) { g2d.setPaint(color); diff --git a/src/Codes/shape/Hexagon.java b/src/Codes/shape/Hexagon.java index 2e35b52..7a543ab 100644 --- a/src/Codes/shape/Hexagon.java +++ b/src/Codes/shape/Hexagon.java @@ -3,11 +3,9 @@ import java.awt.*; /** - * + * 六边形 */ public class Hexagon extends AbstractShape { - - @Override public void draw(Graphics2D g2d) { g2d.setPaint(color); diff --git a/src/Codes/shape/Images.java b/src/Codes/shape/Images.java index 3e346ad..9ee4845 100644 --- a/src/Codes/shape/Images.java +++ b/src/Codes/shape/Images.java @@ -3,15 +3,11 @@ import java.awt.*; /** - * + * 图片 */ public class Images extends AbstractShape { - - @Override public void draw(Graphics2D g) { g.drawImage(image, 0, 0, board); } - - } diff --git a/src/Codes/shape/Line.java b/src/Codes/shape/Line.java index 6473538..7c1952d 100644 --- a/src/Codes/shape/Line.java +++ b/src/Codes/shape/Line.java @@ -3,12 +3,11 @@ import java.awt.*; /** - * + * 线段 */ public class Line extends AbstractShape { public Line() { - } @Override diff --git a/src/Codes/shape/Oval.java b/src/Codes/shape/Oval.java index 7ac6e7f..231210d 100644 --- a/src/Codes/shape/Oval.java +++ b/src/Codes/shape/Oval.java @@ -3,11 +3,9 @@ import java.awt.*; /** - * + * 椭圆 */ public class Oval extends AbstractShape { - - @Override public void draw(Graphics2D g2d) { g2d.setPaint(color); diff --git a/src/Codes/shape/Pencil.java b/src/Codes/shape/Pencil.java index 4ac5322..88d4cb4 100644 --- a/src/Codes/shape/Pencil.java +++ b/src/Codes/shape/Pencil.java @@ -3,23 +3,16 @@ import java.awt.*; /** - * + * 铅笔 */ public class Pencil extends AbstractShape { - - public Pencil() { - } - @Override public void draw(Graphics2D g) { g.setPaint(color); - g.setStroke(new BasicStroke(width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL)); - g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - g.drawLine(x1, y1, x2, y2); } } diff --git a/src/Codes/shape/Pentagon.java b/src/Codes/shape/Pentagon.java index d1d3c6a..14b852f 100644 --- a/src/Codes/shape/Pentagon.java +++ b/src/Codes/shape/Pentagon.java @@ -3,11 +3,9 @@ import java.awt.*; /** - * + * 五边形 */ public class Pentagon extends AbstractShape { - - @Override public void draw(Graphics2D g2d) { g2d.setPaint(color); diff --git a/src/Codes/shape/Rectangle.java b/src/Codes/shape/Rectangle.java index aabd895..e660d62 100644 --- a/src/Codes/shape/Rectangle.java +++ b/src/Codes/shape/Rectangle.java @@ -3,19 +3,14 @@ import java.awt.*; /** - * + * 矩形 */ public class Rectangle extends AbstractShape { - - public Rectangle() { - } - @Override public void draw(Graphics2D g2d) { - g2d.setColor(color); g2d.setStroke(new BasicStroke(width)); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, diff --git a/src/Codes/shape/RoundRect.java b/src/Codes/shape/RoundRect.java index 3236527..624b5e9 100644 --- a/src/Codes/shape/RoundRect.java +++ b/src/Codes/shape/RoundRect.java @@ -3,13 +3,9 @@ import java.awt.*; /** - * + * 圆角矩形 */ public class RoundRect extends AbstractShape { - /** - * 定义一个RoundRectangle类,继承Shape类,用于绘制一个圆角矩形 - */ - @Override public void draw(Graphics2D g2d) { g2d.setPaint(color); diff --git a/src/Codes/shape/Select.java b/src/Codes/shape/Select.java new file mode 100644 index 0000000..21e0a5a --- /dev/null +++ b/src/Codes/shape/Select.java @@ -0,0 +1,73 @@ +package Codes.shape; + +import java.awt.*; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import javax.swing.*; + +/** + * 可拖动矩形,边框为虚线,内部透明 + */ +public class Select extends JPanel { + private int x, y, width, height; + private boolean dragging = false; + private int offsetX, offsetY; + + public Select() { + setOpaque(false); // 透明背景 + addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + if (isInsideRectangle(e.getX(), e.getY())) { + dragging = true; + offsetX = e.getX() - x; + offsetY = e.getY() - y; + } + } + + @Override + public void mouseReleased(MouseEvent e) { + dragging = false; + } + }); + + addMouseMotionListener(new MouseAdapter() { + @Override + public void mouseDragged(MouseEvent e) { + if (dragging) { + x = e.getX() - offsetX; + y = e.getY() - offsetY; + repaint(); + } + } + }); + } + + public void setRectangle(int x, int y, int width, int height) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + repaint(); + } + + private boolean isInsideRectangle(int mouseX, int mouseY) { + return mouseX >= x && mouseX <= x + width && mouseY >= y && mouseY <= y + height; + } + + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + Graphics2D g2d = (Graphics2D) g; + + // 设置虚线边框 + float[] dashPattern = {5, 5}; + g2d.setStroke(new BasicStroke(2, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, dashPattern, 0)); + g2d.setColor(Color.BLACK); + g2d.drawRect(x, y, width, height); + + // 内部透明效果 + g2d.setColor(new Color(255, 255, 255, 100)); + g2d.fillRect(x, y, width, height); + } +} diff --git a/src/Codes/shape/Text.java b/src/Codes/shape/Text.java index fff08e9..daaa02a 100644 --- a/src/Codes/shape/Text.java +++ b/src/Codes/shape/Text.java @@ -6,18 +6,12 @@ * 定义一个Text类,继承Shape类,用于文字输入 */ public class Text extends AbstractShape { - - - @Override public void draw(Graphics2D g) { - //Font(String name, int style, int size) g.setColor(color); g.setFont(new Font(fontName, italic + boldType, fontSize)); if (s != null) { - g.drawString(s, x1, y1); - //System.out.println(fontName+" "+s+" "+fontSize ); } } } \ No newline at end of file diff --git a/src/Codes/shape/Triangle.java b/src/Codes/shape/Triangle.java index 344e71a..02063cf 100644 --- a/src/Codes/shape/Triangle.java +++ b/src/Codes/shape/Triangle.java @@ -6,8 +6,6 @@ * 定义一个Triangle类,继承Shape类,用于绘制一个三角形 */ public class Triangle extends AbstractShape { - - @Override public void draw(Graphics2D g2d) { g2d.setPaint(color); diff --git a/src/Codes/start/Start.java b/src/Codes/start/Start.java index 64ffd46..b5810dd 100644 --- a/src/Codes/start/Start.java +++ b/src/Codes/start/Start.java @@ -9,7 +9,7 @@ public class Start { public static MyFrame wds; public static void main(String[] args) { -// try { +// try // //调用Windows的文件系统 // UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); // } catch (Exception e) { diff --git a/src/Codes/tools/ColorPanel.java b/src/Codes/tools/ColorPanel.java index 93d8b9f..68c5118 100644 --- a/src/Codes/tools/ColorPanel.java +++ b/src/Codes/tools/ColorPanel.java @@ -23,12 +23,12 @@ public class ColorPanel extends JPanel { * 调色板 * 颜色数组,用来设置按钮的背景颜色 */ - private final Color[] colors = {new Color(255, 255, 255), new Color(0, 0, 0), new Color(128, 128, 128), - new Color(195, 195, 195), new Color(67, 0, 0, 255), new Color(185, 122, 87), new Color(255, 0, 0), - new Color(255, 174, 201), new Color(255, 128, 0), new Color(0, 255, 0), new Color(255, 255, 0), - new Color(34, 117, 76), new Color(128, 128, 0), new Color(0, 0, 255), new Color(0, 158, 158), - new Color(255, 107, 39), new Color(56, 141, 246), new Color(163, 73, 164), new Color(200, 191, 231), - new Color(87, 195, 169), new Color(8, 193, 194), new Color(24, 76, 64), new Color(168, 95, 223), + private final Color[] colors = {new Color(0, 0, 0), new Color(255, 255, 255), new Color(80, 79, 79), + new Color(128, 128, 128), new Color(67, 0, 0, 255), new Color(177, 96, 49), new Color(255, 0, 0), + new Color(255, 174, 201), new Color(255, 107, 39), new Color(255, 128, 0), new Color(255, 255, 0), + new Color(230, 218, 152), new Color(239, 233, 151), new Color(107, 251, 107), new Color(29, 29, 255), + new Color(0, 0, 255), new Color(26, 124, 232), new Color(104, 118, 198), new Color(163, 195, 241), + new Color(49, 75, 110), new Color(8, 193, 194), new Color(128, 44, 189), new Color(177, 121, 223), new Color(199, 73, 4)}; public ColorPanel() { @@ -39,7 +39,7 @@ private void addColorPanel() { // 主面板添加左边面板 this.setPreferredSize(new Dimension(60, 60)); this.setLayout(null); - this.setBackground(new Color(195, 195, 195)); + this.setBackground(new Color(239, 238, 238)); // 左边面板添加子面板 JPanel panelDownChild = new JPanel(); @@ -76,7 +76,7 @@ private void addColorPanel() { // 右面板 JPanel right = new JPanel(); - right.setBackground(new Color(195, 195, 195)); + right.setBackground(new Color(239, 238, 238)); right.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0)); right.setPreferredSize(new Dimension(60, 240)); @@ -119,7 +119,7 @@ private void addColorPanel() { JButton jbt = (JButton) e.getSource(); // 拿到被选中按钮的背景颜色 Color c = jbt.getBackground(); - // 把背景颜色复制给WIndowStart中的颜色属性 + // 把背景颜色复制给WindowStart中的颜色属性 MyFrame.color = c; MyFrame.itemList[MyFrame.index].color = c; diff --git a/src/Codes/tools/DrawPanel.java b/src/Codes/tools/DrawPanel.java index 6445fe6..3c21867 100644 --- a/src/Codes/tools/DrawPanel.java +++ b/src/Codes/tools/DrawPanel.java @@ -8,10 +8,13 @@ import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; +import java.awt.geom.AffineTransform; import java.io.Serial; import java.util.Objects; import static Codes.tools.MyFrame.*; +import static java.lang.Math.abs; +import static java.lang.Math.max; /** * 画图面板类,用来画图 @@ -20,6 +23,11 @@ public class DrawPanel extends JPanel { @Serial private static final long serialVersionUID = 1L; + private double zoomLevel = 1.0; + private double offsetX = 0, offsetY = 0; + private Image backgroundImage; + private int x1 = 0, x2 = 0, y1 = 0, y2 = 0; + private int currentX = 0, currentY = 0; public DrawPanel() { // 设置光标类型,为十字形 @@ -30,21 +38,51 @@ public DrawPanel() { this.addMouseListener(new MouseAction()); this.addMouseMotionListener(new MouseMotion()); } + public void setBackgroundImage(Image image) { + this.backgroundImage = image; + repaint(); // 重新绘制面板 + } + + public void clearBackgroundImage() { + this.backgroundImage = null; + repaint(); // 重新绘制面板 + } + + public void setZoomAndOffset(double zoom, double offsetX, double offsetY) { + this.zoomLevel = zoom; + this.offsetX = offsetX; + this.offsetY = offsetY; + repaint(); + } // 重写paintComponent方法,使得画板每次刷新时可将之前的所有图形重新画出来。 @Override public void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D) g; // 定义画板 - int j = 0; - while (j <= index) { - draw(g2d, itemList[j]); - j++; + // 绘制背景图像 + if (backgroundImage != null) { + g2d.drawImage(backgroundImage, 0, 0, getWidth(), getHeight(), this); + } + + // 保存当前变换 + AffineTransform originalTransform = g2d.getTransform(); + + // 应用缩放和平移 + g2d.translate(offsetX, offsetY); + g2d.scale(zoomLevel, zoomLevel); + // 绘制所有图形 + for (int j = 0; j <= index; j++) { + if (itemList[j] != null) { + draw(g2d, itemList[j]); + } } - } + // 恢复原始变换 + g2d.setTransform(originalTransform); + } void draw(Graphics2D g2d, AbstractShape abstractShape) { // 将画笔传入到各个子类中,用来完成各自的绘图 abstractShape.draw(g2d); @@ -57,7 +95,6 @@ public void undo() { if (index > 0) { // 确保有图形可以撤销 index--; // 回退到上一个图形 // 如果当前选择的是铅笔、橡皮擦或刷子等工具(绘制多个点的图形) - if (itemList[index] != null) { System.out.println(itemList[index].currentChoice); if (itemList[index].currentChoice == 3 || itemList[index].currentChoice == 16 @@ -127,7 +164,7 @@ public void createNewGraphics() { case 8 -> itemList[index] = new FillOval(); case 9 -> itemList[index] = new Circle(); case 10 -> itemList[index] = new FillCircle(); - case 11 -> itemList[index] = new RoundRect(); + case 11 -> itemList[index] = new RoundRect(); case 12 -> itemList[index] = new FillRoundRect(); case 13 -> itemList[index] = new Triangle(); case 14 -> itemList[index] = new Pentagon(); @@ -148,7 +185,10 @@ public void createNewGraphics() { itemList[index].color = color; itemList[index].width = stroke; itemList[index].currentChoice = currentChoice; - //System.out.println(index); + itemList[index].x1 = -30; + itemList[index].y1 = -30; + itemList[index].x2 = -30; + itemList[index].y2 = -30; } // 鼠标事件mouseAction类,继承了MouseAdapter,用来完成鼠标相应事件操作 @@ -156,15 +196,18 @@ class MouseAction extends MouseAdapter { @Override public void mousePressed(MouseEvent e) { // 设置状态提示 - statusBar.setText("坐标:[" + e.getX() + ", " + e.getY() + "]像素"); - itemList[index].x1 = itemList[index].x2 = e.getX(); - itemList[index].y1 = itemList[index].y2 = e.getY(); + currentX = (int) (e.getX()/(zoomLevel/2+0.5)); + currentY = (int) (e.getY()/(zoomLevel/2+0.5)); + positionLabel.setText("坐标: [ " + currentX + ", " + currentY + " ]像素"); + itemList[index].x1 = itemList[index].x2 = x1 = x2 = currentX; + itemList[index].y1 = itemList[index].y2 = y1 = y2 = currentY; + judgeSizeLabel(); // 如果当前选择的图形是画笔或者橡皮檫,则进行下面的操作 if (currentChoice == 3 || currentChoice == 16 || currentChoice == 17) { lengthCount = 0; - itemList[index].x1 = itemList[index].x2 = e.getX(); - itemList[index].y1 = itemList[index].y2 = e.getY(); + itemList[index].x1 = itemList[index].x2 = currentX ; + itemList[index].y1 = itemList[index].y2 = currentY; index++; lengthCount++; createNewGraphics(); @@ -173,15 +216,21 @@ public void mousePressed(MouseEvent e) { @Override public void mouseReleased(MouseEvent e) { - statusBar.setText("坐标:[" + e.getX() + ", " + e.getY() + "]像素"); + currentX = (int) (e.getX()/(zoomLevel/2+0.5)); + currentY = (int) (e.getY()/(zoomLevel/2+0.5)); + + positionLabel.setText("坐标: [ " + currentX + ", " + currentY + " ]像素"); if (currentChoice == 3 || currentChoice == 16 || currentChoice == 17) { - itemList[index].x1 = e.getX(); - itemList[index].y1 = e.getY(); + itemList[index].x1 = currentX; + itemList[index].y1 = currentY; itemList[index].length = lengthCount; } - itemList[index].x2 = e.getX(); - itemList[index].y2 = e.getY(); + itemList[index].x2 = x2 = currentX; + itemList[index].y2 = y2 = currentY; + + judgeSizeLabel(); + repaint(); index++; createNewGraphics(); @@ -189,12 +238,12 @@ public void mouseReleased(MouseEvent e) { @Override public void mouseEntered(MouseEvent e) { - statusBar.setText("坐标:[" + e.getX() + ", " + e.getY() + "]像素"); + positionLabel.setText("坐标: [ " + e.getX() + ", " + e.getY() + " ]像素"); } @Override public void mouseExited(MouseEvent e) { - statusBar.setText("坐标:"); + positionLabel.setText("坐标:"); } } @@ -204,25 +253,51 @@ public void mouseExited(MouseEvent e) { class MouseMotion extends MouseMotionAdapter { @Override public void mouseDragged(MouseEvent e) { - statusBar.setText("坐标:[" + e.getX() + "," + e.getY() + "]像素"); + currentX = (int) (e.getX()/(zoomLevel/2+0.5)); + currentY = (int) (e.getY()/(zoomLevel/2+0.5)); + + positionLabel.setText("坐标: [ " + currentX + ", " + currentY + " ]像素"); if (currentChoice == 3 || currentChoice == 16 || currentChoice == 17) { - itemList[index - 1].x1 = itemList[index].x2 = itemList[index].x1 = e.getX(); - itemList[index - 1].y1 = itemList[index].y2 = itemList[index].y1 = e.getY(); + itemList[index - 1].x1 = itemList[index].x2 = itemList[index].x1 = x2 = currentX; + itemList[index - 1].y1 = itemList[index].y2 = itemList[index].y1 = y2 = currentY; index++; lengthCount++; createNewGraphics(); } else { - itemList[index].x2 = e.getX(); - itemList[index].y2 = e.getY(); + itemList[index].x2 = x2 = currentX; + itemList[index].y2 = y2 = currentY; } + + judgeSizeLabel(); repaint(); } @Override public void mouseMoved(MouseEvent e) { - statusBar.setText("坐标:[" + e.getX() + "," + e.getY() + "]像素"); + currentX = (int) (e.getX()/(zoomLevel/2+0.5)); + currentY = (int) (e.getY()/(zoomLevel/2+0.5)); + + positionLabel.setText("坐标: [ " + currentX + ", " + currentY + " ]像素"); + sizeLabel.setText("图形大小:"); } } + public void setZoomLevel(double zoomLevel) { + this.zoomLevel = zoomLevel; + repaint(); // 重新绘制面板 + } + + private void judgeSizeLabel() { + if(currentChoice == 9|| currentChoice == 10){ + int r = max(abs(x2-x1+1), abs(y2-y1+1)); + sizeLabel.setText("图形大小: "+r+" × "+r+"像素"); + } + else if(currentChoice != 3) { + sizeLabel.setText("图形大小: "+abs(x2-x1+1)+" × "+abs(y2-y1+1)+"像素"); + } + else { + sizeLabel.setText("图形大小:"); + } + } } diff --git a/src/Codes/tools/MyFrame.java b/src/Codes/tools/MyFrame.java index 269d6d5..a85e482 100644 --- a/src/Codes/tools/MyFrame.java +++ b/src/Codes/tools/MyFrame.java @@ -3,8 +3,10 @@ import Codes.shape.*; import javax.swing.*; +import javax.swing.event.ChangeEvent; import java.awt.*; import java.awt.event.*; +import java.awt.geom.AffineTransform; import java.io.Serial; import java.util.Objects; @@ -17,8 +19,6 @@ public class MyFrame extends JFrame { * 保存文件的标志 */ public static int saved = 0; - - /** * 铅笔或橡皮擦图形的存储长度 */ @@ -46,10 +46,31 @@ public class MyFrame extends JFrame { * 画图区域 */ public static DrawPanel drawingArea; + /** + * 画板大小 + */ + public static int width; + public static int height; /** * 底部状态栏 */ - public static JLabel statusBar; + public static JPanel statusBar; + /** + * 页面布局管理器 + */ + public static GridBagConstraints gbc; + /** + * 位置信息 + */ + public static JLabel positionLabel; + /** + * 图形大小信息 + */ + public static JLabel sizeLabel; + /** + * 画布大小信息 + */ + public static JLabel canvasLabel; /** * 画笔粗细 */ @@ -78,6 +99,11 @@ public class MyFrame extends JFrame { */ public static MySlide slider; + /** + * 缩放区域滑动条 + */ + public static MyZoom zoom; + public MyFrame() { init(); } @@ -87,25 +113,23 @@ private void init() { // 设置标题 this.setTitle("画图"); // 设置窗口大小 - this.setSize(950, 600); + width = 1000; + height = 600; + this.setSize(width, height); // 居中显示 this.setLocationRelativeTo(null); setVisible(true); + this.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); - /* - 粗细调节滑动条 - */ + //粗细调节滑动条 slider = new MySlide(); - this.add(slider, BorderLayout.EAST); + this.add(slider,BorderLayout.EAST); // 添加菜单 menu = new MyMenu(this); - toolbar = new MyToolbar(this); - /* - 调色板 - */ + //调色板 ColorPanel colorPanel = new ColorPanel(); add(colorPanel, BorderLayout.WEST); @@ -120,16 +144,44 @@ private void init() { // 创建各种基本图形的按钮 drawingArea = new DrawPanel(); this.add(drawingArea, BorderLayout.CENTER); - statusBar = new JLabel(); - this.add(statusBar, BorderLayout.SOUTH); - statusBar.setText("坐标"); + zoom = new MyZoom(drawingArea); + //创建底部状态栏并设置布局 + statusBar = new JPanel(new GridBagLayout()); + gbc = new GridBagConstraints(); + gbc.insets = new Insets(2, 5, 2, 5); + gbc.fill = GridBagConstraints.HORIZONTAL; + + add(statusBar, BorderLayout.SOUTH); + gbc.gridx = 3; + gbc.weightx = 0.8; + statusBar.add(zoom,gbc); + + //位置信息 + positionLabel = new JLabel("坐标:"); + positionLabel.setPreferredSize(new Dimension(100, 20)); + gbc.gridx = 0; + gbc.weightx = 0.2; + statusBar.add(positionLabel, gbc); + + //图形大小信息 + sizeLabel = new JLabel("图形大小:"); + sizeLabel.setPreferredSize(new Dimension(120, 20)); + gbc.gridx = 1; + statusBar.add(sizeLabel, gbc); + + //画布大小信息 + canvasLabel = new JLabel("画布大小:"); + canvasLabel.setPreferredSize(new Dimension(120, 20)); + canvasLabel.setText("画布大小: "+width+" × "+height+"像素"); + gbc.gridx = 2; + statusBar.add(canvasLabel, gbc); /* * 由于JLabel是透明的,当我们把JLabel控件加载到JPanel控件之上时, 会发现JLabel的背景色总是和JPanel的背景色保持一致, */ // 设置该组件为透明 statusBar.setOpaque(true); - statusBar.setBackground(new Color(195, 195, 195)); + statusBar.setBackground(new Color(239, 238, 238)); drawingArea.createNewGraphics(); this.addWindowListener(new WindowAdapter() { @@ -146,8 +198,8 @@ public void windowClosing(WindowEvent e) { } } }); - + this.revalidate(); // 重新验证组件布局 + this.repaint(); // 强制刷新 } - } diff --git a/src/Codes/tools/MyMenu.java b/src/Codes/tools/MyMenu.java index 08e940d..d1cc998 100644 --- a/src/Codes/tools/MyMenu.java +++ b/src/Codes/tools/MyMenu.java @@ -4,6 +4,8 @@ import javax.imageio.ImageIO; import javax.swing.*; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; import javax.swing.filechooser.FileFilter; import java.awt.*; import java.awt.event.InputEvent; @@ -22,17 +24,15 @@ public class MyMenu { private final String[] strokes = {"/image/stroke1.png", "/image/stroke2.png", "/image/stroke3.png", "/image/stroke4.png"}; - + //private DrawPanel drawPanel; public MyMenu(MyFrame frame) { + addMenu(frame); } void addMenu(MyFrame frame) { - /* - ** 顶部菜单条 - */ - + // 顶部菜单条 JMenuBar jMenuBar = new JMenuBar(); JMenuItem[] strokeItems = new JMenuItem[strokes.length]; // 实例化菜单对象 @@ -41,6 +41,7 @@ void addMenu(MyFrame frame) { JMenu setMenu = new JMenu("设置"); JMenu helpMenu = new JMenu("帮助"); JMenu strokeMenu = new JMenu("粗细"); + JMenu BGMenu =new JMenu("背景"); // 实例化菜单项,并通过ImageIcon对象添加图片 定义文件菜单的菜单项 JMenuItem fileItemNew = new JMenuItem("新建", new ImageIcon(getClass().getResource("/image/new.png"))); JMenuItem fileItemOpen = new JMenuItem("打开", new ImageIcon(getClass().getResource("/image/open.png"))); @@ -51,35 +52,89 @@ void addMenu(MyFrame frame) { JMenuItem setItemUndo = new JMenuItem("撤销", new ImageIcon(getClass().getResource("/image/undo.png"))); JMenuItem helpItemUse = new JMenuItem("使用手册"); JMenuItem helpItemInfo = new JMenuItem("关于画图"); + + //背景选择 + JMenuItem origin = new JMenuItem("原始模式"); + origin.addActionListener(e -> { + drawingArea.clearBackgroundImage(); + drawingArea.setBackground(Color.WHITE); + drawingArea.repaint(); // 刷新界面 + }); + JMenuItem black = new JMenuItem("黑夜模式"); + black.addActionListener(e -> { + // 将画布背景设置为黑色 + drawingArea.clearBackgroundImage(); + drawingArea.setBackground(Color.BLACK); // 设置画布背景为黑色 + drawingArea.repaint(); // 刷新界面 + }); + JMenuItem green = new JMenuItem("护眼模式"); + green.addActionListener(e -> { + drawingArea.clearBackgroundImage(); + // 将浮点数 RGB 值转换为整数 + int r = (int) Math.round(199.00); + int g = (int) Math.round(237.00); + int b = (int) Math.round(204.00); + + // 创建颜色对象 + Color eyeProtectionColor = new Color(r, g, b); + + // 设置背景颜色 + drawingArea.setBackground(eyeProtectionColor); + drawingArea.repaint(); // 刷新界面 + }); + JMenuItem grid = new JMenuItem("网格纸"); + grid.addActionListener(e -> { + // 加载图片 + ImageIcon gridIcon = new ImageIcon(getClass().getResource("/image/grid.png")); + Image gridImage = gridIcon.getImage(); + + // 设置自定义背景面板 + drawingArea.setBackgroundImage(gridImage); // 假设 DrawPanel 中有 setBackgroundImage 方法 + drawingArea.repaint(); + }); + JMenuItem graph = new JMenuItem("坐标纸"); + graph.addActionListener(e -> { + // 加载图片 + ImageIcon gridIcon = new ImageIcon(getClass().getResource("/image/graph.png")); + Image gridImage = gridIcon.getImage(); + + // 设置自定义背景面板 + drawingArea.setBackgroundImage(gridImage); // 假设 DrawPanel 中有 setBackgroundImage 方法 + drawingArea.repaint(); + }); + + BGMenu.add(origin); + BGMenu.add(black); + BGMenu.add(green); + BGMenu.add(grid); + BGMenu.add(graph); for (int i = 0; i < 4; i++) { strokeItems[i] = new JMenuItem("", new ImageIcon(getClass().getResource(strokes[i]))); strokeMenu.add(strokeItems[i]); } helpItemInfo.addActionListener(e -> JOptionPane.showMessageDialog(null, """ - 关于画图 - ****该软件由***开发完成**** - ****班级:计算机1602班 ***** - ****学号:00000000 ***** - ****邮箱:1111111@qq.com + ****该软件由软件学院2023级tjj,zyj,lyt开发完成**** + ****项目地址:https://github.com/janeyujie/PaintingBoard """, "关于画图", JOptionPane.PLAIN_MESSAGE)); helpItemUse.addActionListener(e -> JOptionPane.showMessageDialog(null, """ - ##################\r - #画图软件使用说明书#\r - ####################\r + ********************\r + 画图软件使用说明书\r + ********************\r 1.本软件可以实现以下功能:\r - (1)在画布上绘制直线、矩形、椭圆等图形\r + (1)依据鼠标轨迹绘制曲线\r (2)设置画笔的颜色和粗细\r - (3)绘制填充图形\r - (4)依据鼠标轨迹绘制曲线\r - (5)橡皮擦、保存图片\r - 2.本软件主要分为四个模块:菜单、工具栏、调色板、和画布\r + (3)在画布上绘制直线、矩形、椭圆等图形\r + (4)绘制填充图形,橡皮擦擦去图形\r + (5)保存绘制结果为图片、导入图片\r + 2.本软件主要分为六个模块:菜单、工具栏、调色板、滑动条、状态栏和画布\r (1)菜单栏的文件子菜单包括打开、新建、保存图片以及退出程序,设置有快捷键,方便操作,\r - 菜单栏的设置子菜单包括设置画笔的粗细和颜色;\r + 菜单栏的设置子菜单包括设置画笔的粗细和颜色;背景子菜单可以选择画布的背景;\r (2)工具栏主要包括保存文件、清空画板、撤回操作、图形绘制和文字的绘制;\r (3)调色板位于界面的左侧,用于设置画笔的颜色,可以使用已设定的颜色,也可以自己选择系统提供的颜色;\r - (4)画布用于图形绘制,使用鼠标选中要绘制的图形即可进行绘制。""", "使用说明", JOptionPane.PLAIN_MESSAGE)); + (4)画布用于图形绘制,使用鼠标选中要绘制的图形即可进行绘制。 + (5)滑动条用于调节画笔粗细,状态栏用于显示当前坐标以及绘制的图形大小。""", "使用说明", JOptionPane.PLAIN_MESSAGE)); helpMenu.add(helpItemUse); helpMenu.add(helpItemInfo); // 设置快捷键 @@ -96,12 +151,13 @@ void addMenu(MyFrame frame) { fileMenu.add(fileItemSave); fileMenu.add(fileItemExit); setMenu.add(setItemColor); - setMenu.add(setItemUndo); setMenu.add(strokeMenu); + setMenu.add(setItemUndo); // 添加菜单到菜单条 jMenuBar.add(fileMenu); jMenuBar.add(setMenu); + jMenuBar.add(BGMenu); jMenuBar.add(helpMenu); // 添加菜单条 frame.setJMenuBar(jMenuBar); @@ -220,59 +276,39 @@ void openFile() { JOptionPane.showMessageDialog(fileChooser, "无效的文件名", "无效的文件名", JOptionPane.ERROR_MESSAGE); } - /* - BufferedImage image; -try { - image = ImageIO.read(fileName); - if (image == null) { - JOptionPane.showMessageDialog(MyFrame.this, "无法读取图片!"); - return; - } - - // 获取图片原始尺寸 - int imgWidth = image.getWidth(); - int imgHeight = image.getHeight(); - - // 设置画布大小为图片大小 - drawingArea.setPreferredSize(new Dimension(imgWidth, imgHeight)); - drawingArea.revalidate(); - drawingArea.repaint(); - - // 如果需要缩放图片适应画布(可选) - int canvasWidth = drawingArea.getWidth(); - int canvasHeight = drawingArea.getHeight(); - if (canvasWidth > 0 && canvasHeight > 0) { - // 按比例缩放图片 - Image scaledImage = image.getScaledInstance(canvasWidth, canvasHeight, Image.SCALE_SMOOTH); - BufferedImage resizedImage = new BufferedImage(canvasWidth, canvasHeight, BufferedImage.TYPE_INT_ARGB); - Graphics2D g2d = resizedImage.createGraphics(); - g2d.drawImage(scaledImage, 0, 0, null); - g2d.dispose(); - image = resizedImage; - } - - // 更新画布显示图片 - index = 0; - currentChoice = 0; - drawingArea.createNewGraphics(); - itemList[index].image = image; - itemList[index].board = drawingArea; - drawingArea.repaint(); - index++; - currentChoice = 3; - drawingArea.createNewGraphics(); - ColorChanger colorChanger=new ColorChanger(drawingArea); - colorChanger.processImage(image); -} catch (IOException e) { - e.printStackTrace(); -} - * */ - BufferedImage image; - try { index = 0; currentChoice = 0; - image = ImageIO.read(fileName); + BufferedImage image = ImageIO.read(fileName); + + if (image == null) { + JOptionPane.showMessageDialog(Start.wds, "无法读取图片!"); + return; + } + + // 获取图片原始尺寸 + int imgWidth = image.getWidth(); + int imgHeight = image.getHeight(); + + // 设置画布大小为图片大小 + drawingArea.setPreferredSize(new Dimension(imgWidth, imgHeight)); + drawingArea.revalidate(); + drawingArea.repaint(); + + // 如果需要缩放图片适应画布(可选) + int canvasWidth = drawingArea.getWidth(); + int canvasHeight = drawingArea.getHeight(); + if (canvasWidth > 0 && canvasHeight > 0) { + // 按比例缩放图片 + Image scaledImage = image.getScaledInstance(canvasWidth, canvasHeight, Image.SCALE_SMOOTH); + BufferedImage resizedImage = new BufferedImage(canvasWidth, canvasHeight, BufferedImage.TYPE_INT_ARGB); + Graphics2D g2d = resizedImage.createGraphics(); + g2d.drawImage(scaledImage, 0, 0, null); + g2d.dispose(); + image = resizedImage; + } + + // 更新画布显示图片 drawingArea.createNewGraphics(); itemList[index].image = image; itemList[index].board = drawingArea; diff --git a/src/Codes/tools/MySlide.java b/src/Codes/tools/MySlide.java index e735597..615e900 100644 --- a/src/Codes/tools/MySlide.java +++ b/src/Codes/tools/MySlide.java @@ -13,8 +13,7 @@ public class MySlide extends JPanel { public JSlider slider; public MySlide() { - setBounds(450, 300, 50, 200); - + setBounds(450, 300, 25, 150); // 设置面板布局 setLayout(new BorderLayout()); @@ -28,17 +27,16 @@ public MySlide() { slider.setUI(customUI); // 显示当前值的标签 - JLabel label = new JLabel(": 1px", JLabel.CENTER); + JLabel label = new JLabel("1px", JLabel.CENTER); label.setFont(new Font("Arial", Font.BOLD, 12)); // 添加滑动条监听器 slider.addChangeListener(_ -> { - // 动态更新画笔粗细 MyFrame.stroke = slider.getValue(); itemList[index].width = stroke; - label.setText(": " + MyFrame.stroke + "px"); + label.setText(MyFrame.stroke + "px"); }); // 鼠标移动事件监听,检测鼠标是否悬停在滑轮上 @@ -53,14 +51,14 @@ public void mouseMoved(MouseEvent e) { }); // 限制滑动条高度 - slider.setPreferredSize(new Dimension(50, 150)); + slider.setPreferredSize(new Dimension(35, 150)); // 添加组件到面板 add(slider, BorderLayout.CENTER); add(label, BorderLayout.SOUTH); // 设置外边距 - setBorder(BorderFactory.createEmptyBorder(50, 10, 50, 10)); + setBorder(BorderFactory.createEmptyBorder(25, 10, 25, 10)); } //重写BasicSliderUI类,更换滑轮的UI样式 private static class RoundThumbSliderUI extends BasicSliderUI { diff --git a/src/Codes/tools/MyToolbar.java b/src/Codes/tools/MyToolbar.java index 793087e..3a2dee1 100644 --- a/src/Codes/tools/MyToolbar.java +++ b/src/Codes/tools/MyToolbar.java @@ -12,7 +12,7 @@ */ public class MyToolbar { /** - * 定义各种绘图的按钮 + * 定义绘图按钮 */ private JButton[] btnPaint; private JComboBox jFont; @@ -20,17 +20,15 @@ public class MyToolbar { /** * 将图片资源的相对路径存放于数组中,方便使用 */ - private final String[] images = { "/image/save.png", "/image/refresh.png", "/image/undo.png", "/image/pencil.png", + private final String[] images = { "/image/save.png", "/image/refresh.png", "/image/undo.png","/image/pencil.png", "/image/line.png", "/image/rectangle.png", "/image/rectangle3.png", "/image/oval.png", "/image/oval2.png", "/image/circle.png", "/image/fillcircle.png", "/image/rectangle2.png", "/image/rectangle4.png", "/image/triangle.png", "/image/pentagon.png", "/image/hexagon.png", "/image/eraser.png", "/image/brush.png", "/image/font.png", }; - private final String[] tipText = { "保存", "清空", "撤销", "铅笔", "直线", "空心矩形", "填充矩形", "空心椭圆", "填充椭圆", "空心圆形", "填充圆形", - "空心圆角矩形", "填充圆角矩形", "三角形", "五边形", "六边形", "橡皮擦", "填充", "文本", "粗细" }; + private final String[] tipText = { "保存", "清空", "撤销", "铅笔", "直线", "矩形", "填充矩形", "椭圆", "填充椭圆", "圆形", "填充圆形", + "圆角矩形", "填充圆角矩形", "三角形", "五边形", "六边形", "橡皮擦", "刷子", "文本", "粗细" }; private final String[] font = { "宋体", "隶书", "华文彩云", "仿宋_GB2312", "华文行楷", "方正舒体" }; - private final String[] fontSize = { "8", "9", "10", "11", "12", "14", "16", "18", "20", "22", "24", "26", "28", - "36", - "48", "72" }; + private final String[] fontSize = { "8", "9", "10", "11", "12", "14", "16", "18", "20", "22", "24", "26", "28", "36", "48", "72" }; public MyToolbar(MyFrame myFrame) { addToolbar(myFrame); @@ -43,7 +41,7 @@ void addToolbar(MyFrame myFrame) { toolbar.setLayout(new FlowLayout(FlowLayout.LEFT)); toolbar.setBackground(new Color(195, 195, 195)); - // 中文会乱码 + // 粗体 Checkbox btnBold = new Checkbox("bold"); // 斜体 @@ -63,7 +61,7 @@ void addToolbar(MyFrame myFrame) { // 设置按钮图标以及图片 for (int i = 0; i < images.length; i++) { - // System.out.println(images[i]);//测试 + //System.out.println(images[i]);//测试 btnPaint[i] = new JButton(); icon[i] = new ImageIcon(Objects.requireNonNull(getClass().getResource(images[i]))); btnPaint[i].setIcon(icon[i]); @@ -113,8 +111,23 @@ void addToolbar(MyFrame myFrame) { btnPaint[2].addActionListener(e -> drawingArea.undo()); // 添加监听 - btnItalic.addItemListener(e -> MyFrame.italic = Font.ITALIC); - btnBold.addItemListener(e -> MyFrame.boldType = Font.BOLD); + // 添加监听 + // 添加监听 + btnItalic.addItemListener(e -> { + if (btnItalic.getState()) { // 判断是否选中 + MyFrame.italic = Font.ITALIC; // 设置斜体 + } else { + MyFrame.italic = Font.PLAIN; // 恢复普通字体 + } + }); + + btnBold.addItemListener(e -> { + if (btnBold.getState()) { // 判断是否选中 + MyFrame.boldType = Font.BOLD; // 设置粗体 + } else { + MyFrame.boldType = Font.PLAIN; // 恢复普通字体 + } + }); // 设置字体大小 toolbar.add(jFontSize); diff --git a/src/Codes/tools/MyZoom.java b/src/Codes/tools/MyZoom.java new file mode 100644 index 0000000..5b725ff --- /dev/null +++ b/src/Codes/tools/MyZoom.java @@ -0,0 +1,220 @@ +package Codes.tools; + +import Codes.tools.DrawPanel; + +import javax.swing.*; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.plaf.basic.BasicSliderUI; +import java.awt.*; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.Hashtable; + +import static Codes.tools.MyFrame.*; + +public class MyZoom extends JPanel { + + private double zoomLevel = 1.0; // 当前缩放比例 + private DrawPanel drawingArea; // 绘图区域 + private JSlider zoomSlider; // 缩放滑动条 + private JLabel zoomValueLabel; // 显示缩放值的标签 + + public MyZoom(DrawPanel drawingArea) { + this.drawingArea = drawingArea; + initComponents(); + } + + private void initComponents() { + // 设置布局 + //setLayout(new FlowLayout(FlowLayout.CENTER, 10, 5)); + + // 创建缩放滑动条 + zoomSlider = new JSlider(JSlider.HORIZONTAL, 10, 40, 10); // 最小值 10,最大值 40,初始值 10 + zoomSlider.setMajorTickSpacing(10); // 大刻度间隔 + zoomSlider.setMinorTickSpacing(1); // 小刻度间隔 + zoomSlider.setPaintTicks(false); // 显示刻度 + zoomSlider.setPaintLabels(false); // 显示刻度标签 + zoomSlider.setPreferredSize(new Dimension(200, 20)); // 设置滑动条大小 + zoomSlider.setBorder(BorderFactory.createEmptyBorder(3,0,0,0)); + // 应用自定义的滑轮UI + zoomSlider.setUI(new CustomSliderUI(zoomSlider)); + + //滑轮悬停效果 + zoomSlider.addMouseListener(new MouseAdapter() { + @Override + public void mouseEntered(MouseEvent e) { + ((CustomSliderUI) zoomSlider.getUI()).setHover(true); // 鼠标悬停时设置滑轮为悬停状态 + zoomSlider.repaint(); + } + + @Override + public void mouseExited(MouseEvent e) { + ((CustomSliderUI) zoomSlider.getUI()).setHover(false); // 鼠标移出时取消悬停状态 + zoomSlider.repaint(); + } + }); + + + // 自定义刻度标签 + Hashtable labelTable = new Hashtable<>(); + for (int i = 10; i <= 40; i += 1) { + labelTable.put(i, new JLabel(String.format("%.1f", i / 10.0))); + } + zoomSlider.setLabelTable(labelTable); + + // 创建缩放值标签 + zoomValueLabel = new JLabel("1.0"); + zoomValueLabel.setFont(new Font("宋体", Font.PLAIN, 14)); + + // 添加滑动条和标签到面板 + add(new JLabel("缩放程度:")); + add(zoomSlider); + add(zoomValueLabel); + + // 添加滑动条值变化监听器 + zoomSlider.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + // 获取滑动条的值 + int sliderValue = zoomSlider.getValue(); + zoomLevel = sliderValue / 10.0; // 将整数值转换为小数值 + + // 更新缩放值标签 + zoomValueLabel.setText(String.format("%.1f", zoomLevel)); + canvasLabel.setText("画布大小: "+(int)(width/(zoomLevel/2+0.5))+" × "+(int)(height/(zoomLevel/2+0.5))+"像素"); + + // 更新绘图区域的缩放比例 + updateZoom(); + } + }); + } + + + private static class CustomSliderUI extends BasicSliderUI { + private boolean isHover = false; // 标记鼠标是否悬停在滑轮上 + + public CustomSliderUI(JSlider slider) { + super(slider); + } + + // 设置悬停状态 + public void setHover(boolean hover) { + isHover = hover; + } + + // 获取滑轮的区域 + public Rectangle getThumbBounds() { + return thumbRect; + } + + @Override + public void paint(Graphics g, JComponent c) { + // 调用父类的 paint 方法以清除背景 + super.paint(g, c); + + // 强制仅重绘滑轮和轨迹区域,避免残影 + Graphics2D g2 = (Graphics2D) g.create(); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + // 绘制滑动条轨迹 + paintTrack(g2); + + // 绘制滑轮 + paintThumb(g2); + + g2.dispose(); + } + + @Override + public void paintThumb(Graphics g) { + Graphics2D g2 = (Graphics2D) g.create(); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + // 滑轮白色部分(固定大小) + int whiteSize = 18; // 白色圆大小 + int whiteX = thumbRect.x + (thumbRect.width - whiteSize) / 2; + int whiteY = thumbRect.y + (thumbRect.height - whiteSize) / 2; + + g2.setColor(new Color(255, 255, 255)); + g2.fillOval(whiteX, whiteY, whiteSize, whiteSize); + + // 滑轮蓝色部分(根据悬停状态调整大小) + int blueSize = isHover ? 14 : 10; // 悬停时增大蓝色圆 + int blueX = thumbRect.x + (thumbRect.width - blueSize) / 2; + int blueY = thumbRect.y + (thumbRect.height - blueSize) / 2; + + g2.setColor(new Color(0, 120, 250)); + g2.fillOval(blueX, blueY, blueSize, blueSize); + + g2.dispose(); + } + + @Override + public void paintTrack(Graphics g) { + Graphics2D g2 = (Graphics2D) g.create(); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + int trackHeight = 4; // 轨迹高度 + int trackWidth = trackRect.width; // 轨迹宽度(水平滑动条时是宽度) + + // 绘制完整轨迹(底层) + g2.setColor(new Color(200, 200, 200)); // 灰色轨迹线 + g2.fillRoundRect(trackRect.x, trackRect.y + (trackRect.height - trackHeight) / 2, trackWidth, trackHeight, 3, 3); + + // 绘制动态轨迹(覆盖) + int thumbCenterX = thumbRect.x + thumbRect.width / 2; // 计算滑轮中心X坐标 + int dynamicTrackWidth = thumbCenterX - trackRect.x; // 蓝色轨迹宽度(从滑块位置到轨迹起点) + + // 绘制动态轨迹(蓝色部分) + g2.setColor(new Color(0, 120, 250)); // 蓝色动态轨迹 + g2.fillRoundRect(trackRect.x, trackRect.y + (trackRect.height - trackHeight) / 2, dynamicTrackWidth, trackHeight, 3, 3); + + g2.dispose(); + } + + @Override + protected Dimension getThumbSize() { + // 设置滑轮的尺寸 + return new Dimension(16, 16); + } + + @Override + public void setThumbLocation(int x, int y) { + // 调用父类方法设置滑轮位置 + super.setThumbLocation(x, y); + + // 强制仅重绘滑轮和动态轨迹区域 + slider.repaint(trackRect.x, trackRect.y, trackRect.width, trackRect.height); + } + } + + + private void updateZoom() { + // 获取绘图区域的大小 + int width = drawingArea.getWidth(); + int height = drawingArea.getHeight(); + + // 计算缩放后的偏移量 + double offSetX = width / 2; + double offSetY = height / 2; + + // 调整偏移量以保持缩放中心 + if (offSetX < (width / zoomLevel) / 2) { + offSetX = (width / zoomLevel) / 2; + } + if (offSetX > width - ((width / zoomLevel) / 2)) { + offSetX = width - ((width / zoomLevel) / 2); + } + if (offSetY < (height / zoomLevel) / 2) { + offSetY = (height / zoomLevel) / 2; + } + if (offSetY > height - ((height / zoomLevel) / 2)) { + offSetY = height - ((height / zoomLevel) / 2); + } + + // 更新绘图区域的缩放比例 + drawingArea.setZoomLevel(zoomLevel); + drawingArea.repaint(); // 重新绘制绘图区域 + } +} diff --git a/src/image/graph.png b/src/image/graph.png new file mode 100644 index 0000000..ceb0032 Binary files /dev/null and b/src/image/graph.png differ diff --git a/src/image/grid.png b/src/image/grid.png new file mode 100644 index 0000000..e4d9106 Binary files /dev/null and b/src/image/grid.png differ diff --git a/src/image/select.png b/src/image/select.png new file mode 100644 index 0000000..d1af11d Binary files /dev/null and b/src/image/select.png differ