从点击到绚丽:手把手教你用JavaFX开发炫酷鼠标项目 - 附全套源码

目录

引言

项目概述

项目地址

界面展示(精美UI布局)

技术栈

核心框架

项目管理

系统集成

项目结构

项目中所用到的设计模式

1.策略模式(Strategy Pattern)

1. 应用场景

2. 具体实现

3. 优势

2.观察者模式(Observer Pattern)

1. 应用场景

2. 具体实现

3. 优势

整体代码

1.程序入口

2.特效控制器

3.特效接口

4.彩虹尾巴特效

5.特效配置类

6.特效样式

7.全局样式

8.主界面布局

8.特效控制面板布局

9.POM文件依赖

部署说明

1. 源码获取

2. IDE运行配置(IntelliJ IDEA)

3. 生成exe可执行文件

直接运行

命令行运行

常见问题解决

结语


引言

在这篇博客中,我将分享如何使用JavaFX开发一个炫酷的鼠标特效应用,从项目架构到具体实现,完整记录开发过程中的经验和心得。

项目概述

Beautiful Mouse炫酷鼠标是一个基于JavaFX开发的桌面应用程序,通过全局鼠标事件监听,实现了多种炫酷的鼠标特效。用户可以根据个人喜好选择不同的特效样式,并且可以实时调整特效的参数,如颜色、大小、速度等。

项目地址

Gitee已开源:BeautifulMouse炫酷鼠标项目Giee开源地址

界面展示(精美UI布局)

技术栈

核心框架

  • Java 17
  • JavaFX:用于构建桌面GUI应用
  • FXML:用于UI布局设计
  • CSS:用于界面样式设计

项目管理

  • Maven:项目构建和依赖管理

系统集成

  • JNA:实现与操作系统的底层交互
  • JNativeHook:实现全局鼠标事件监听

项目结构

beautifulMouse/
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   └── com/bm/beautifulmouse/
│   │   │       ├── MainApplication.java          # 程序主入口
│   │   │       ├── controller/                   # 控制器层
│   │   │       │   └── EffectController.java     # 特效控制器
│   │   │       ├── effect/                       # 特效实现层
│   │   │       │   ├── Effect.java              # 特效接口
│   │   │       │   ├── StarEffect.java          # 星星特效
│   │   │       │   ├── RainbowTrailEffect.java  # 彩虹尾巴特效
│   │   │       │   ├── ParticleEffect.java      # 粒子特效
│   │   │       │   ├── SnowEffect.java          # 雪花特效
│   │   │       │   ├── HeartEffect.java         # 心形特效
│   │   │       │   └── FirecrackerEffect.java   # 爆竹特效
│   │   │       └── model/                       # 模型层
│   │   │           └── EffectConfig.java        # 特效配置类
│   │   └── resources/
│   │       ├── css/
│   │       │   ├── effect-style.css             # 特效样式
│   │       │   └── style.css                    # 全局样式
│   │       ├── fxml/
│   │       │   ├── main.fxml                    # 主界面布局
│   │       │   └── effect-view.fxml             # 特效控制面板布局
│   │       └── images/                          # 图片资源
│   └── test/                                    # 测试目录
│       └── java/
│           └── com/bm/beautifulmouse/
│               └── effect/
│                   └── EffectTest.java          # 特效测试类
├── .gitignore                                   # Git忽略文件
├── LICENSE                                      # 开源协议
├── README.md                                    # 项目说明
└── pom.xml                                      # Maven配置文件

项目中所用到的设计模式

1.策略模式(Strategy Pattern)

1. 应用场景

在实现不同类型的鼠标特效时,每种特效都有其独特的生成算法和动画效果。

2. 具体实现
// 策略接口
public interface Effect {
    void generate(Pane container, double x, double y, EffectConfig config);
}

// 具体策略实现
public class StarEffect implements Effect {
    @Override
    public void generate(Pane container, double x, double y, EffectConfig config) {
        // 星星特效实现
    }
}

public class RainbowTrailEffect implements Effect {
    @Override
    public void generate(Pane container, double x, double y, EffectConfig config) {
        // 彩虹尾巴特效实现
    }
}
3. 优势
  • 特效算法可以独立变化

  • 易于添加新的特效类型

  • 避免使用复杂的条件语句

2.观察者模式(Observer Pattern)

1. 应用场景

用户通过控制面板调整特效参数时,需要实时更新特效显示。

2. 具体实现
public class EffectController {
    private void setupListeners() {
        // 观察参数变化
        sizeSlider.valueProperty().addListener((obs, oldVal, newVal) -> {
            config.setSize(newVal.doubleValue());
            updateEffect();
        });

        colorPicker.valueProperty().addListener((obs, oldVal, newVal) -> {
            config.setColor(newVal);
            updateEffect();
        });
    }
}
3. 优势
  • 实现了UI和特效逻辑的解耦

  • 支持参数的实时预览

  • 便于添加新的监听行为

整体代码

1.程序入口

package com.bm.beautifulmouse;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Screen;
import javafx.stage.Stage;
import org.jnativehook.GlobalScreen;
import org.jnativehook.NativeHookException;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * 应用程序主入口类
 */
public class MainApplication extends Application {
    
    @Override
    public void start(Stage stage) throws Exception {
        // 初始化全局鼠标监听
        initializeGlobalMouseHook();
        
        // 加载主界面
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/main.fxml"));
        Scene scene = new Scene(fxmlLoader.load());
        
        // 添加样式表
        scene.getStylesheets().addAll(
            getClass().getResource("/css/style.css").toExternalForm(),
            getClass().getResource("/css/effect-style.css").toExternalForm()
        );
        
        // 获取屏幕尺寸
        Screen screen = Screen.getPrimary();
        double screenWidth = screen.getBounds().getWidth();
        double screenHeight = screen.getBounds().getHeight();
        
        // 配置舞台
        stage.setTitle("Beautiful Mouse");
        stage.setScene(scene);
        stage.setMaximized(true); // 设置最大化
        stage.setMinWidth(800);   // 设置最小宽度
        stage.setMinHeight(600);  // 设置最小高度
        
        // 显示舞台
        stage.show();
    }

    private void initializeGlobalMouseHook() {
        Logger.getLogger(GlobalScreen.class.getPackage().getName()).setLevel(Level.OFF);
        try {
            GlobalScreen.registerNativeHook();
        } catch (NativeHookException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}

2.特效控制器

package com.bm.beautifulmouse.controller;

import com.bm.beautifulmouse.effect.Effect;
import com.bm.beautifulmouse.effect.ParticleEffect;
import com.bm.beautifulmouse.effect.SnowEffect;
import com.bm.beautifulmouse.effect.StarEffect;
import com.bm.beautifulmouse.effect.HeartEffect;
import com.bm.beautifulmouse.effect.PawEffect;
import com.bm.beautifulmouse.effect.FirecrackerEffect;
import com.bm.beautifulmouse.effect.RainbowTrailEffect;
import com.bm.beautifulmouse.model.EffectConfig;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;

public class EffectController {
    @FXML private Pane effectPane;
    @FXML private ComboBox<String> effectTypeCombo;
    @FXML private ColorPicker colorPicker;
    @FXML private Slider sizeSlider;
    @FXML private Slider speedSlider;
    @FXML private Slider rangeSlider;
    @FXML private Slider particleCountSlider;

    // 标签引用
    @FXML private Label sizeLabel;
    @FXML private Label speedLabel;
    @FXML private Label rangeLabel;
    @FXML private Label particleCountLabel;

    private Effect currentEffect;
    private EffectConfig config;

    @FXML
    public void initialize() {
        // 初始化配置
        config = new EffectConfig();
        currentEffect = new SnowEffect(); // 设置初始特效为雪花特效
        // 设置下拉框样式和内容
        effectTypeCombo.getItems().addAll("雪花特效", "粒子特效", "星星特效", "心形特效", "猫爪特效", "爆竹特效", "彩虹尾巴");
        effectTypeCombo.setValue("雪花特效");
        effectTypeCombo.getStyleClass().add("custom-combo-box");

        // 设置滑块的默认值和样式
        sizeSlider.setValue(1.0);
        speedSlider.setValue(1.0);
        rangeSlider.setValue(1.0);
        particleCountSlider.setValue(5.0);

        // 添加滑块样式类
        sizeSlider.getStyleClass().add("custom-slider");
        speedSlider.getStyleClass().add("custom-slider");
        rangeSlider.getStyleClass().add("custom-slider");
        particleCountSlider.getStyleClass().add("custom-slider");

        // 设置颜色选择器默认颜色和样式
        colorPicker.setValue(Color.WHITE);
        colorPicker.getStyleClass().add("custom-color-picker");

        // 添加事件监听器
        setupEventListeners();

        // 添加鼠标移动事件监听
        effectPane.setOnMouseMoved(event -> {
            if (currentEffect instanceof RainbowTrailEffect ||
                    currentEffect instanceof StarEffect) {  // 添加StarEffect判断
                // 更新配置
                config.setColor(colorPicker.getValue());
                config.setSize(sizeSlider.getValue());
                config.setSpeed(speedSlider.getValue());
                config.setRange(rangeSlider.getValue());
                config.setParticleCount((int) particleCountSlider.getValue());

                // 生成特效
                currentEffect.generate(effectPane, event.getX(), event.getY(), config);
            }
        });
    }

    private void setupEventListeners() {
        // 添加点击事件监听器
        effectPane.setOnMouseClicked(event -> {
            if (currentEffect != null) {
                // 更新配置
                config.setColor(colorPicker.getValue());
                config.setSize(sizeSlider.getValue());
                config.setSpeed(speedSlider.getValue());
                config.setRange(rangeSlider.getValue());
                config.setParticleCount((int) particleCountSlider.getValue());

                // 生成特效
                currentEffect.generate(effectPane, event.getX(), event.getY(), config);
            }
        });

        // 特效类型切换监听
        effectTypeCombo.setOnAction(e -> {
            switch(effectTypeCombo.getValue()) {
                case "雪花特效":
                    currentEffect = new SnowEffect();
                    break;
                case "粒子特效":
                    currentEffect = new ParticleEffect();
                    break;
                case "星星特效":
                    currentEffect = new StarEffect();
                    break;
                case "心形特效":
                    currentEffect = new HeartEffect();
                    break;
                case "猫爪特效":
                    currentEffect = new PawEffect();
                    break;
                case "爆竹特效":
                    currentEffect = new FirecrackerEffect();
                    break;
                case "彩虹尾巴":
                    currentEffect = new RainbowTrailEffect();
                    break;
            }
        });

        // 其他控件值变化监听器
        colorPicker.setOnAction(e -> config.setColor(colorPicker.getValue()));
        sizeSlider.valueProperty().addListener((obs, old, newVal) ->
                config.setSize(newVal.doubleValue()));
        speedSlider.valueProperty().addListener((obs, old, newVal) ->
                config.setSpeed(newVal.doubleValue()));
        rangeSlider.valueProperty().addListener((obs, old, newVal) ->
                config.setRange(newVal.doubleValue()));
        particleCountSlider.valueProperty().addListener((obs, old, newVal) ->
                config.setParticleCount(newVal.intValue()));
    }

    /**
     * 重置为默认设置
     */
    @FXML
    private void resetToDefault() {
        colorPicker.setValue(Color.WHITE);
        sizeSlider.setValue(1.0);
        speedSlider.setValue(1.0);
        rangeSlider.setValue(50);
        particleCountSlider.setValue(5);
    }

    /**
     * 设置炫丽预设
     */
    @FXML
    private void setFancyPreset() {
        colorPicker.setValue(Color.MAGENTA);
        sizeSlider.setValue(1.5);
        speedSlider.setValue(1.8);
        rangeSlider.setValue(75);
        particleCountSlider.setValue(30);
    }

    /**
     * 设置柔和预设
     */
    @FXML
    private void setSoftPreset() {
        colorPicker.setValue(Color.SKYBLUE);
        sizeSlider.setValue(0.8);
        speedSlider.setValue(0.6);
        rangeSlider.setValue(40);
        particleCountSlider.setValue(15);
    }

    /**
     * 设置控件初始值
     */
    private void setupControls() {
        // 设置特效类型选项
        effectTypeCombo.getItems().addAll(
            "星星特效",
            "彩虹尾巴",
            "粒子特效",
            "雪花特效",
            "心形特效"
        );
        effectTypeCombo.setValue("星星特效");

        // 设置默认值
        resetToDefault();
    }

    /**
     * 设置监听器
     */
    private void setupListeners() {
        // 大小滑块监听
        sizeSlider.valueProperty().addListener((obs, oldVal, newVal) -> {
            config.setSize(newVal.doubleValue());
            sizeLabel.setText(String.format("%.1f", newVal.doubleValue()));
        });

        // 速度滑块监听
        speedSlider.valueProperty().addListener((obs, oldVal, newVal) -> {
            config.setSpeed(newVal.doubleValue());
            speedLabel.setText(String.format("%.1f", newVal.doubleValue()));
        });

        // 范围滑块监听
        rangeSlider.valueProperty().addListener((obs, oldVal, newVal) -> {
            config.setRange(newVal.doubleValue());
            rangeLabel.setText(String.format("%.0f", newVal.doubleValue()));
        });

        // 粒子数量滑块监听
        particleCountSlider.valueProperty().addListener((obs, oldVal, newVal) -> {
            config.setParticleCount(newVal.intValue());
            particleCountLabel.setText(String.format("%d", newVal.intValue()));
        });

        // 颜色选择器监听
        colorPicker.valueProperty().addListener((obs, oldVal, newVal) -> {
            config.setColor(newVal);
        });
    }
}

3.特效接口

import com.bm.beautifulmouse.model.EffectConfig;
import javafx.scene.layout.Pane;

/**
 * 特效接口
 * 所有具体特效实现都需要实现此接口
 */
public interface Effect {
    /**
     * 生成特效的方法
     * @param container 特效容器,用于放置特效元素
     * @param x 鼠标X坐标
     * @param y 鼠标Y坐标
     * @param config 特效配置参数
     */
    void generate(Pane container, double x, double y, EffectConfig config);
}

4.彩虹尾巴特效

package com.bm.beautifulmouse.effect;

import com.bm.beautifulmouse.model.EffectConfig;
import javafx.animation.*;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.*;
import javafx.util.Duration;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;

public class RainbowTrailEffect implements Effect {
    private final Random random = new Random();
    private final Queue<Circle> particles = new LinkedList<>();
    private final Queue<Timeline> animations = new LinkedList<>();
    private Circle lastParticle = null;
    private static final int MAX_PARTICLES = 50;
    private static final Color[] RAINBOW_COLORS = {
        Color.RED, Color.ORANGE, Color.YELLOW, 
        Color.GREEN, Color.BLUE, Color.PURPLE
    };

    @Override
    public void generate(Pane container, double x, double y, EffectConfig config) {
        // 创建新的粒子
        Circle particle = createParticle(x, y, config);
        container.getChildren().add(particle);
        
        // 如果存在上一个粒子,创建连线
        if (lastParticle != null) {
            Line connection = createConnection(lastParticle, particle);
            container.getChildren().add(connection);
            
            // 为连线创建消失动画
            Timeline connectionTimeline = new Timeline(
                new KeyFrame(Duration.ZERO, new KeyValue(connection.opacityProperty(), 0.5)),
                new KeyFrame(Duration.seconds(0.5), new KeyValue(connection.opacityProperty(), 0))
            );
            connectionTimeline.setOnFinished(e -> container.getChildren().remove(connection));
            connectionTimeline.play();
        }
        
        // 更新最后一个粒子
        lastParticle = particle;
        particles.offer(particle);
        
        // 创建粒子动画
        Timeline timeline = createAnimation(container, particle, config);
        animations.offer(timeline);
        timeline.play();
        
        // 添加旋转效果
        addRotation(particle);
        
        // 清理旧的粒子
        cleanOldParticles(container);
    }

    private Circle createParticle(double x, double y, EffectConfig config) {
        Circle particle = new Circle(3 * config.getSize());
        particle.setCenterX(x);
        particle.setCenterY(y);
        
        Color baseColor = RAINBOW_COLORS[random.nextInt(RAINBOW_COLORS.length)];
        Color randomColor = baseColor.deriveColor(
            random.nextDouble() * 360,
            0.8 + random.nextDouble() * 0.2,
            0.8 + random.nextDouble() * 0.2,
            1.0
        );
        particle.setFill(randomColor);
        particle.setEffect(new javafx.scene.effect.Glow(0.8));
        
        return particle;
    }

    private Line createConnection(Circle start, Circle end) {
        Line connection = new Line();
        connection.startXProperty().bind(start.centerXProperty());
        connection.startYProperty().bind(start.centerYProperty());
        connection.endXProperty().bind(end.centerXProperty());
        connection.endYProperty().bind(end.centerYProperty());
        connection.setStroke(start.getFill());
        connection.setStrokeWidth(2);
        connection.setOpacity(0.5);
        return connection;
    }

    private void addRotation(Circle particle) {
        RotateTransition rotate = new RotateTransition(Duration.seconds(1), particle);
        rotate.setByAngle(360);
        rotate.setCycleCount(Timeline.INDEFINITE);
        rotate.play();
    }

    private Timeline createAnimation(Pane container, Circle particle, EffectConfig config) {
        Timeline timeline = new Timeline(
            new KeyFrame(Duration.ZERO,
                new KeyValue(particle.opacityProperty(), 1.0),
                new KeyValue(particle.radiusProperty(), 3 * config.getSize())
            ),
            new KeyFrame(Duration.seconds(0.5 / config.getSpeed()),
                new KeyValue(particle.opacityProperty(), 0.0),
                new KeyValue(particle.radiusProperty(), config.getSize())
            )
        );
        
        timeline.setOnFinished(e -> {
            container.getChildren().remove(particle);
            particles.remove(particle);
            animations.remove(timeline);
            if (lastParticle == particle) {
                lastParticle = null;
            }
        });
        
        return timeline;
    }

    private void cleanOldParticles(Pane container) {
        while (particles.size() >= MAX_PARTICLES) {
            Circle oldParticle = particles.poll();
            Timeline oldAnimation = animations.poll();
            if (oldAnimation != null) {
                oldAnimation.stop();
            }
            container.getChildren().remove(oldParticle);
            if (lastParticle == oldParticle) {
                lastParticle = null;
            }
        }
    }
}

5.特效配置类

import javafx.scene.paint.Color;
public class EffectConfig {
    private Color color = Color.WHITE;
    private double size = 1.0;
    private int particleCount = 52;
    private double speed = 1.55;
    private double range = 1.55;

    public Color getColor() { return color; }
    public void setColor(Color color) { this.color = color; }

    public double getSize() { return size; }
    public void setSize(double size) { this.size = size; }

    public int getParticleCount() { return particleCount; }
    public void setParticleCount(int count) { this.particleCount = count; }

    public double getSpeed() { return speed; }
    public void setSpeed(double speed) { this.speed = speed; }

    public double getRange() { return range; }
    public void setRange(double range) { this.range = range; }
}

6.特效样式

/* 全局样式 */
.root {
    -fx-background-color: #2b2b2b;
    -fx-font-family: "Microsoft YaHei", "Segoe UI", Arial, sans-serif;
}

/* 标签样式 */
.label {
    -fx-text-fill: #e8e8e8;
    -fx-font-size: 14px;
    -fx-padding: 5 0 5 0;
}

/* 下拉框样式 */
.custom-combo-box {
    -fx-background-color: #3c3f41;
    -fx-text-fill: white;
    -fx-mark-color: #f0f0f0;
    -fx-background-radius: 5;
}

.custom-combo-box .list-cell {
    -fx-background-color: transparent;
    -fx-text-fill: white;
    -fx-padding: 5 10 5 10;
}

.custom-combo-box .list-view {
    -fx-background-color: #3c3f41;
    -fx-background-radius: 5;
}

.custom-combo-box .list-cell:hover {
    -fx-background-color: #4c5052;
}

/* 滑块样式 */
.custom-slider {
    -fx-padding: 10 0 10 0;
}

.custom-slider .track {
    -fx-background-color: #515151;
    -fx-background-radius: 2;
}

.custom-slider .thumb {
    -fx-background-color: #6a9ec5;
    -fx-background-radius: 50;
    -fx-padding: 7;
}

.custom-slider:hover .thumb {
    -fx-background-color: #7bafd4;
}

/* 颜色选择器样式 */
.custom-color-picker {
    -fx-background-color: #3c3f41;
    -fx-background-radius: 5;
}

.custom-color-picker .color-picker-label {
    -fx-text-fill: white;
}

/* 控制面板容器样式 */
.control-panel {
    -fx-background-color: rgba(43, 43, 43, 0.85);
    -fx-background-radius: 10;
    -fx-padding: 15;
    -fx-spacing: 10;
    -fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.4), 10, 0, 0, 0);
}

/* 分隔线样式 */
.separator {
    -fx-background-color: #515151;
}

/* 动画效果 */
.custom-combo-box,
.custom-slider .thumb,
.custom-color-picker {
    -fx-transition: -fx-background-color 0.2s ease-in-out;
}

/* 鼠标悬停效果 */
.custom-combo-box:hover,
.custom-color-picker:hover {
    -fx-background-color: #4c5052;
}

7.全局样式

/* 全局样式定义 */

/* 根节点样式 */
.root {
    /* 主题颜色 */
    -fx-primary: #2196F3;
    -fx-primary-light: #64B5F6;
    -fx-primary-dark: #1976D2;
    -fx-accent: #FF4081;
    
    /* 背景颜色 */
    -fx-background: #121212;
    -fx-background-light: #1E1E1E;
    
    /* 文本颜色 */
    -fx-text-primary: #FFFFFF;
    -fx-text-secondary: rgba(255, 255, 255, 0.7);
    
    /* 默认字体 */
    -fx-font-family: "Microsoft YaHei";
    -fx-font-size: 14px;
}

/* 按钮样式 */
.button {
    -fx-background-color: -fx-primary;
    -fx-text-fill: white;
    -fx-background-radius: 4px;
    -fx-padding: 8px 16px;
    -fx-cursor: hand;
}

.button:hover {
    -fx-background-color: -fx-primary-light;
}

.button:pressed {
    -fx-background-color: -fx-primary-dark;
}

/* 滑块样式 */
.slider {
    -fx-show-tick-labels: true;
    -fx-show-tick-marks: true;
    -fx-major-tick-unit: 0.5;
    -fx-block-increment: 0.1;
}

.slider .track {
    -fx-background-color: -fx-background-light;
}

.slider .thumb {
    -fx-background-color: -fx-primary;
}

/* 下拉框样式 */
.combo-box {
    -fx-background-color: -fx-background-light;
    -fx-text-fill: -fx-text-primary;
    -fx-background-radius: 4px;
    -fx-padding: 5px;
}

.combo-box .list-cell {
    -fx-background-color: -fx-background-light;
    -fx-text-fill: -fx-text-primary;
}

.combo-box .list-view {
    -fx-background-color: -fx-background-light;
}

/* 颜色选择器样式 */
.color-picker {
    -fx-background-color: -fx-background-light;
    -fx-background-radius: 4px;
    -fx-min-height: 35px;
}

/* 标签样式 */
.label {
    -fx-text-fill: -fx-text-primary;
}

/* 面板样式 */
.pane {
    -fx-background-color: -fx-background;
}

/* 工具提示样式 */
.tooltip {
    -fx-background-color: -fx-background-light;
    -fx-text-fill: -fx-text-primary;
    -fx-font-size: 12px;
}

/* 滚动条样式 */
.scroll-bar {
    -fx-background-color: transparent;
}

.scroll-bar .thumb {
    -fx-background-color: -fx-primary;
    -fx-background-radius: 4px;
}

/* 分隔线样式 */
.separator {
    -fx-background-color: rgba(255, 255, 255, 0.1);
}

/* 状态栏样式 */
.status-bar {
    -fx-background-color: -fx-background-light;
    -fx-padding: 4px;
}

/* 控制面板样式 */
.control-panel {
    -fx-background-color: rgba(43, 43, 43, 0.85);
    -fx-background-radius: 10 0 0 10;
    -fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.4), 10, 0, -5, 0);
}

/* 面板标题 */
.panel-title {
    -fx-font-size: 18px;
    -fx-font-weight: bold;
    -fx-text-fill: -fx-text-primary;
}

/* 控制标签 */
.control-label {
    -fx-font-size: 14px;
    -fx-text-fill: -fx-text-secondary;
}

/* 值标签 */
.value-label {
    -fx-font-size: 12px;
    -fx-text-fill: -fx-text-secondary;
    -fx-alignment: CENTER-RIGHT;
}

/* 预设按钮 */
.preset-button {
    -fx-min-width: 80px;
    -fx-background-color: -fx-primary;
    -fx-text-fill: white;
}

.preset-button:hover {
    -fx-background-color: -fx-primary-light;
}

/* 版本标签 */
.version-label {
    -fx-font-size: 12px;
    -fx-text-fill: -fx-text-secondary;
    -fx-alignment: CENTER;
}

/* 滚动面板样式 */
.control-panel-scroll {
    -fx-background: transparent;
    -fx-background-color: transparent;
    -fx-border-width: 0;
}

.control-panel-scroll > .viewport {
    -fx-background-color: transparent;
}

.control-panel-scroll > .scroll-bar:vertical {
    -fx-background-color: transparent;
    -fx-padding: 0 0 0 1;
}

.control-panel-scroll > .scroll-bar:vertical > .thumb {
    -fx-background-color: rgba(255, 255, 255, 0.3);
    -fx-background-radius: 2;
}

.control-panel-scroll > .scroll-bar:vertical > .track {
    -fx-background-color: transparent;
}

/* 确保控制面板始终显示在右侧 */
.control-panel-scroll {
    -fx-fit-to-height: true;
    -fx-hbar-policy: never;
    -fx-background-insets: 0;
    -fx-padding: 0;
}

/* 主容器样式 */
.main-container {
    -fx-background-color: -fx-background;
}

/* 特效面板样式 */
.effect-pane {
    -fx-background-color: transparent;
}

8.主界面布局

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.*?>
<?import javafx.scene.control.*?>
<?import javafx.geometry.Insets?>

<BorderPane xmlns:fx="http://javafx.com/fxml/1"
            xmlns="http://javafx.com/javafx/17"
            fx:controller="com.bm.beautifulmouse.controller.EffectController">

    <center>
        <!-- 这是特效显示的主面板 -->
        <Pane fx:id="effectPane" styleClass="effect-pane"/>
    </center>

    <right>
        <VBox spacing="10" styleClass="config-panel">
            <Label text="特效类型"/>
            <ComboBox fx:id="effectTypeCombo" maxWidth="Infinity"/>

            <Label text="特效颜色"/>
            <ColorPicker fx:id="colorPicker"/>

            <Label text="特效大小"/>
            <Slider fx:id="sizeSlider" min="0.1" max="2.0" value="1.0"/>

            <Label text="特效速度"/>
            <Slider fx:id="speedSlider" min="0.1" max="3.0" value="1.55"/>

            <Label text="特效范围"/>
            <Slider fx:id="rangeSlider" min="0.1" max="3.0" value="1.55"/>

            <Label text="粒子数量"/>
            <Slider fx:id="particleCountSlider" min="5" max="100" value="52.5"/>
        </VBox>
    </right>

</BorderPane>

8.特效控制面板布局

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.geometry.Insets?>

<AnchorPane xmlns="http://javafx.com/javafx"
            xmlns:fx="http://javafx.com/fxml"
            fx:controller="com.bm.beautifulmouse.controller.EffectController"
            stylesheets="@../css/effect-style.css">

    <VBox styleClass="control-panel" spacing="10" maxWidth="300">
        <padding>
            <Insets top="15" right="15" bottom="15" left="15"/>
        </padding>

        <Label text="特效类型"/>
        <ComboBox fx:id="effectTypeCombo" maxWidth="Infinity"/>

        <Separator/>

        <Label text="颜色"/>
        <ColorPicker fx:id="colorPicker" maxWidth="Infinity"/>

        <Label text="大小"/>
        <Slider fx:id="sizeSlider" min="0.1" max="2.0" value="1.0"/>

        <Label text="速度"/>
        <Slider fx:id="speedSlider" min="0.1" max="2.0" value="1.0"/>

        <Label text="范围"/>
        <Slider fx:id="rangeSlider" min="0.1" max="2.0" value="1.0"/>

        <Label text="粒子数量"/>
        <Slider fx:id="particleCountSlider" min="1" max="20" value="5"/>
    </VBox>

</AnchorPane>

9.POM文件依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.bm</groupId>
    <artifactId>beautiful-mouse</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <javafx.version>17.0.2</javafx.version>
        <main.class>com.bm.beautifulmouse.MainApplication</main.class>
    </properties>

    <dependencies>
        <!-- 本地 JavaFX 依赖 -->
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-controls</artifactId>
            <version>${javafx.version}</version>
            <scope>system</scope>
            <systemPath>${project.basedir}/src/main/resources/lib/javafx.controls.jar</systemPath>
        </dependency>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-fxml</artifactId>
            <version>${javafx.version}</version>
            <scope>system</scope>
            <systemPath>${project.basedir}/src/main/resources/lib/javafx.fxml.jar</systemPath>
        </dependency>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-base</artifactId>
            <version>${javafx.version}</version>
            <scope>system</scope>
            <systemPath>${project.basedir}/src/main/resources/lib/javafx.base.jar</systemPath>
        </dependency>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-graphics</artifactId>
            <version>${javafx.version}</version>
            <scope>system</scope>
            <systemPath>${project.basedir}/src/main/resources/lib/javafx.graphics.jar</systemPath>
        </dependency>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-media</artifactId>
            <version>${javafx.version}</version>
            <scope>system</scope>
            <systemPath>${project.basedir}/src/main/resources/lib/javafx.media.jar</systemPath>
        </dependency>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-swing</artifactId>
            <version>${javafx.version}</version>
            <scope>system</scope>
            <systemPath>${project.basedir}/src/main/resources/lib/javafx.swing.jar</systemPath>
        </dependency>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-web</artifactId>
            <version>${javafx.version}</version>
            <scope>system</scope>
            <systemPath>${project.basedir}/src/main/resources/lib/javafx.web.jar</systemPath>
        </dependency>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-swt</artifactId>
            <version>${javafx.version}</version>
            <scope>system</scope>
            <systemPath>${project.basedir}/src/main/resources/lib/javafx-swt.jar</systemPath>
        </dependency>
        <!-- 其他依赖 -->
        <dependency>
            <groupId>net.java.dev.jna</groupId>
            <artifactId>jna</artifactId>
            <version>5.13.0</version>
        </dependency>
        <dependency>
            <groupId>net.java.dev.jna</groupId>
            <artifactId>jna-platform</artifactId>
            <version>5.13.0</version>
        </dependency>
        <dependency>
            <groupId>com.1stleg</groupId>
            <artifactId>jnativehook</artifactId>
            <version>2.1.0</version>
        </dependency>
    </dependencies>


    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.10.1</version>
                <configuration>
                    <source>${maven.compiler.source}</source>
                    <target>${maven.compiler.target}</target>
                </configuration>
            </plugin>

            <plugin>
                <groupId>io.github.fvarrui</groupId>
                <artifactId>javapackager</artifactId>
                <version>1.6.6</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>package</goal>
                        </goals>
                        <configuration>
                            <mainClass>com.bm.beautifulmouse.MainApplication</mainClass>
                            
                            <!-- 只保留最基本的模块 -->
                            <modules>
                                <module>java.base</module>
                                <module>java.desktop</module>
                                <module>jdk.unsupported</module>
                            </modules>
                            
                            <!-- 基本配置 -->
                            <bundleJre>true</bundleJre>
                            <generateInstaller>false</generateInstaller>
                            <administratorRequired>false</administratorRequired>
                            
                            <!-- 启动参数 -->
                            <vmArgs>
                                <vmArg>--module-path "lib"</vmArg>
                                <vmArg>--add-modules javafx.controls,javafx.fxml</vmArg>
                                <vmArg>--add-opens java.base/java.lang=ALL-UNNAMED</vmArg>
                            </vmArgs>
                            
                            <!-- 平台配置 -->
                            <platform>windows</platform>
                            <name>BeautifulMouse</name>
                            <displayName>${name}</displayName>
                            
                            <!-- Windows 配置 -->
                            <winConfig>
                                <headerType>gui</headerType>
                                <icoFile>${project.basedir}/src/main/resources/icon.ico</icoFile>
                            </winConfig>
                            
                            <!-- 资源配置 -->
                            <additionalResources>
                                <additionalResource>${project.basedir}/src/main/resources/lib</additionalResource>
                            </additionalResources>

                            <!-- JRE 配置 -->
                            <customizedJre>true</customizedJre>
                            <jrePath>F:\jdk-17.0.0.1</jrePath>
                            
                            <!-- jlink 参数配置 -->
                            <jreMinVersion>17</jreMinVersion>
                            <linuxConfig>
                                <wrapJar>false</wrapJar>
                            </linuxConfig>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

部署说明

1. 源码获取

# 克隆项目
git clone https://gitee.com/xiongwenhao6/beautiful-mouse.git

2. IDE运行配置(IntelliJ IDEA)

配置VM选项

--module-path "项目路径/src/main/resources/lib" --add-modules javafx.controls,javafx.fxml

具体步骤:

  • 打开 "Run/Debug Configurations"

  • 选择 "Application" 配置

  • 在 "VM options" 中添加以上参数

  • 确保 "Main class" 设置为 com.bm.beautifulmouse.MainApplication

  • 启动项目(保证依赖全部导入)

3. 生成exe可执行文件

# 1. 清理项目
mvn clean

# 2. 打包项目
mvn package

项目打包后会在targer目录下会生成BeautifulMouse目录,然后切换到这个目录执行BeautifulMouse.exe文件

直接运行
  • 双击 BeautifulMouse.exe 文件

命令行运行
#切换到指定目录下
cd target/BeautifulMouse
#执行命令运行程序
.\BeautifulMouse.exe

常见问题解决

找不到JavaFX模块

Error: JavaFX runtime components are missing, and are required to run this application

解决方案:

  • 确保JavaFX库文件存在于lib目录下

  • 检查模块路径是否正确

  • 验证JavaFX版本与JDK版本匹配

模块路径错误

Error: Module javafx.controls not found

解决方案:

  • 检查lib目录是否包含所有必需的JavaFX jar文件

  • 确保路径中不包含特殊字符

  • 使用绝对路径替代相对路径

权限问题:

AccessDeniedException: Access is denied

解决方案:

  • 以管理员身份运行

  • 检查文件和目录权限

  • 确保当前用户有足够的访问权限

结语

感谢您阅读本博客!希望本文对您理解和使用Beautiful Mouse炫酷鼠标项目有所帮助。如果您在使用过程中遇到任何问题或有任何建议,请随时在评论区留言,我们会尽快回复您。您的反馈对我们非常重要,能够帮助我们不断改进和优化项目。

如果您喜欢这个项目,请考虑分享给您的朋友或同事,让更多的人体验到这个炫酷的鼠标特效应用!再次感谢您的支持,祝您编程愉快!😊

posted @ 2025-01-15 00:41  熊文豪1  阅读(12)  评论(0)    收藏  举报  来源