如何在JavaFX中实现带羽化硬度调节功能的柔边画笔
JavaFX实现柔边羽化画笔的方案
JavaFX 原生没有直接提供画笔硬度/羽化的封装API,但可以通过以下两种成熟方案实现需求:
方案1:径向渐变自定义笔刷(推荐,效果最接近Photoshop)
核心逻辑是用RadialGradient生成从中心实色到边缘透明的渐变圆形作为笔刷,硬度参数控制透明过渡的区间:硬度1时过渡区间为0(硬边),硬度0时过渡从中心就开始(最大柔边)。
代码修改步骤:
- 新增硬度相关变量和控件绑定
// 新增成员变量 private double brushHardness = 0.5; // 取值范围0~1,默认中等硬度 @FXML private Slider hardnessSlider; // 可在FXML里加个滑块控制硬度 public void initialize() { // 原有初始化逻辑外新增绑定 hardnessSlider.valueProperty().addListener((obs, oldVal, newVal) -> { brushHardness = newVal.doubleValue(); }); GraphicsContext gc = canvas.getGraphicsContext2D(); // 鼠标事件替换原有绘制逻辑 canvas.setOnMouseReleased(e -> last = null); canvas.setOnMouseClicked(e -> { if (!eraser) drawBrushPoint(e.getX(), e.getY()); }); canvas.setOnMouseDragged(e -> { double size = Double.parseDouble(brushSize.getText()); Point2D current = new Point2D(e.getX(), e.getY()); if (eraser) { // 橡皮擦柔边逻辑 gc.setGlobalBlendMode(BlendMode.CLEAR); double stopPos = Math.max(0, Math.min(1, brushHardness)); RadialGradient eraseGradient = new RadialGradient( 0, 0, 0.5, 0.5, 0.5, true, CycleMethod.NO_CYCLE, new Stop(stopPos, Color.TRANSPARENT), new Stop(1, Color.TRANSPARENT) ); gc.setFill(eraseGradient); gc.fillOval(e.getX() - size/2, e.getY() - size/2, size, size); gc.setGlobalBlendMode(BlendMode.SRC_OVER); last = current; return; } if (last != null) { // 两点之间补画笔刷,避免线条断档 double distance = current.distance(last); double step = size / 4; // 步长设为笔刷大小的1/4保证平滑 for (double i = 0; i < distance; i += step) { double ratio = i / distance; double x = last.getX() + (current.getX() - last.getX()) * ratio; double y = last.getY() + (current.getY() - last.getY()) * ratio; drawBrushPoint(x, y); } } else { drawBrushPoint(current.getX(), current.getY()); } last = current; }); } // 新增绘制单点球边笔刷的方法 private void drawBrushPoint(double x, double y) { double size = Double.parseDouble(brushSize.getText()); Color brushColor = colorPicker.getValue(); // 根据硬度计算渐变停止位 double stopPos = Math.max(0, Math.min(1, brushHardness)); RadialGradient gradient = new RadialGradient( 0, 0, 0.5, 0.5, 0.5, true, CycleMethod.NO_CYCLE, new Stop(stopPos, brushColor), new Stop(1, Color.TRANSPARENT) ); gc.setFill(gradient); gc.fillOval(x - size/2, y - size/2, size, size); }
方案2:高斯模糊简化实现
如果不需要精确的硬度控制,可以通过调整GaussianBlur效果的半径模拟柔边:硬度越低,模糊半径越大。这种方案实现更简单,但性能比自定义渐变笔刷稍差。
示例核心代码:
GaussianBlur blur = new GaussianBlur(); // 硬度0对应模糊半径为笔刷大小的1/2,硬度1对应模糊半径0 blur.setRadius((1 - brushHardness) * Double.parseDouble(brushSize.getText()) / 2); gc.setEffect(blur); // 执行原有绘制逻辑 gc.strokeLine(last.getX(), last.getY(), mouseX, mouseY); gc.setEffect(null); // 绘制完成后清除效果避免影响后续操作
性能优化建议
如果需要频繁切换笔刷大小、硬度,可以提前把常用参数的笔刷预生成Image缓存,绘制时直接调用gc.drawImage(),避免每次实时生成渐变,提升绘制流畅度。
内容的提问来源于stack exchange,提问作者Dániel Sáfár
相关产品推荐
相关产品推荐

