本文介绍了JavaFX中的画笔描边的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述 29岁程序员,3月因学历无情被辞! 我试图在JavaFX中编写一个绘画应用程序。我想要一个类似真正的画笔的画笔,但我不知道如何启动算法。下面的代码显示了我当前的画笔描边,尽管它是一个有用的描边,但它不是一个真正的画笔: import javafx.application。应用; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.canvas.Canvas; import javafx.scene.canvas.GraphicsContext; import javafx.scene.input.MouseEvent; import javafx.scene.paint.Color; import javafx.scene.shape.StrokeLineCap; import javafx.scene.shape.StrokeLineJoin; import javafx.stage.Stage; import static javafx.scene.input.MouseEvent。*; public class BrushTester extends Application { private static final Color color = Color.CHOCOLATE; private static final double START_OPACITY = 0.3; private static final double OPACITY_MODIFIER = 0.002; private double currentOpacity = START_OPACITY; private double strokeWidth = 15; public static void main(String [] args){ Application.launch(BrushTester.class); } @Override public void start(Stage primaryStage)throws Exception { Canvas canvas = new Canvas(600d,600d); GraphicsContext gc = canvas.getGraphicsContext2D(); canvas.addEventHandler(MOUSE_DRAGGED,e - > BrushTester.this.handleMouseDragged(gc,e)); canvas.addEventHandler(MOUSE_PRESSED,e - > handleMousePressed(gc,e)); canvas.addEventHandler(MOUSE_RELEASED,e - > handleMouseReleased(gc,e)); Group root = new Group(); root.getChildren()。add(canvas); primaryStage.setScene(new Scene(root,Color.DARKGRAY)); primaryStage.show(); $ b $ private void configureGraphicsContext(GraphicsContext gc){ gc.setStroke(new Color(color.getRed(),color.getGreen(),color.getBlue(), currentOpacity)); gc.setLineCap(StrokeLineCap.ROUND); gc.setLineJoin(StrokeLineJoin.ROUND); gc.setLineWidth(strokeWidth); public void handleMousePressed(GraphicsContext gc,MouseEvent e){ configureGraphicsContext(gc); gc.beginPath(); gc.moveTo(e.getX(),e.getY()); gc.stroke(); } public void handleMouseReleased(GraphicsContext gc,MouseEvent e){ currentOpacity = START_OPACITY; gc.closePath(); } public void handleMouseDragged(GraphicsContext gc,MouseEvent e){ currentOpacity = Math.max(0,currentOpacity - OPACITY_MODIFIER); configureGraphicsContext(gc); gc.lineTo(e.getX(),e.getY()); gc.stroke(); } } 任何人如何更接近真正的东西?解决方案这一切都取决于你想要达到的目标。我个人会使用 一个AnimationTimer 可自定义的画笔(即图像)中风,因此您可以指定大小和硬度 线条绘制算法(如Bresenham)将前一个鼠标位置与当前位置连接,以获得点之间的完整线条 $ b 一个简单的绘图算法的简单例子: import java.util.Random; import javafx.animation.AnimationTimer; import javafx.application.Application; 导入javafx.geometry.Point2D; import javafx.scene.Node; import javafx.scene.Scene; import javafx.scene.SnapshotParameters; import javafx.scene.canvas.Canvas; import javafx.scene.canvas.GraphicsContext; import javafx.scene.image.Image; import javafx.scene.image.WritableImage; import javafx.scene.input.MouseEvent; import javafx.scene.layout.BorderPane; import javafx.scene.layout.Pane; import javafx.scene.paint.Color; import javafx.scene.paint.CycleMethod; import javafx.scene.paint.RadialGradient; import javafx.scene.paint.Stop; import javafx.scene.shape.Circle; import javafx.stage.Stage; public class Main扩展应用程序{ private static double SCENE_WIDTH = 1280; private static double SCENE_HEIGHT = 720; static Random = new Random(); 画布画布; GraphicsContext graphicsContext; AnimationTimer循环; Point2D mouseLocation = new Point2D(0,0); boolean mousePressed = false; Point2D prevMouseLocation = new Point2D(0,0); 场景场景; Image brush = createBrush(30.0,Color.CHOCOLATE); double brushWidthHalf = brush.getWidth()/ 2.0; double brushHeightHalf = brush.getHeight()/ 2.0; @Override public void start(Stage primaryStage){ BorderPane root = new BorderPane(); canvas = new Canvas(SCENE_WIDTH,SCENE_HEIGHT); graphicsContext = canvas.getGraphicsContext2D(); Pane layerPane = new Pane(); layerPane.getChildren()。addAll(canvas); root.setCenter(layerPane); scene = new Scene(root,SCENE_WIDTH,SCENE_HEIGHT); primaryStage.setScene(scene); primaryStage.show(); addListeners(); startAnimation(); $ b private void startAnimation(){ loop = new AnimationTimer(){ @Override public void handle(long now){ if(mousePressed){ //试试这个 // graphicsContext.drawImage(brush ,mouseLocation.getX() - brushWidthHalf,mouseLocation.getY() - brushHeightHalf); //然后这个 bresenhamLine(prevMouseLocation.getX(),prevMouseLocation.getY(),mouseLocation.getX(),mouseLocation.getY()); prevMouseLocation = new Point2D(mouseLocation.getX(),mouseLocation.getY()); } }; loop.start(); } // https://de.wikipedia.org/wiki/Bresenham-Algorithmus private void bresenhamLine(double x0,double y0,double x1 ,double y1) { double dx = Math.abs(x1-x0),sx = x0 double dy = -Math.abs(y1-y0),sy = y0 double err = dx + dy,e2; / *错误值e_xy * / while(true){ graphicsContext.drawImage(brush,x0 - brushWidthHalf,y0 - brushHeightHalf); if(x0 == x1&& y0 == y1)break; e2 = 2. * err;如果(e2> dy){err + = dy; x0 + = sx; } / * e_xy + e_x> 0 * / if(e2 } } private void addListeners(){ scene.addEventFilter(MouseEvent.ANY,e - > ; { mouseLocation = new Point2D(e.getX(),e.getY()); mousePressed = e.isPrimaryButtonDown(); }); public static Image createImage(Node node){ WritableImage wi; SnapshotParameters parameters = new SnapshotParameters(); parameters.setFill(Color.TRANSPARENT); int imageWidth =(int)node.getBoundsInLocal()。getWidth(); int imageHeight =(int)node.getBoundsInLocal()。getHeight(); wi = new WritableImage(imageWidth,imageHeight); node.snapshot(parameters,wi); 返回wi; } public static Image createBrush(double radius,Color color){ //用给定颜色创建渐变图像 Circle brush =新的Circle(半径); RadialGradient gradient1 = new RadialGradient(0,0,0,0,radius,false,CycleMethod.NO_CYCLE,new Stop(0,color.deriveColor(1,1,1,0.3)),new Stop(1,color.deriveColor(1,1,1,0))); brush.setFill(gradient1); //创建图像 return createImage(brush); public static void main(String [] args){ launch(args); } } 当然你可以用e扩展它。 G。 多层 图层和图像上下文级别的JavaFX混合模式 来模拟强制我会使用一个绘制延迟(例如200毫秒)和一个缓冲区的鼠标位置,并让不透明度取决于鼠标是否仍然按下 使用贝塞尔曲线平滑线条 ... 刷子变化时的示例当您开始绘画时: import java.util.Random; import javafx.animation.AnimationTimer; import javafx.application.Application; 导入javafx.geometry.Point2D; import javafx.scene.Node; import javafx.scene.Scene; import javafx.scene.SnapshotParameters; import javafx.scene.canvas.Canvas; import javafx.scene.canvas.GraphicsContext; import javafx.scene.image.Image; import javafx.scene.image.WritableImage; import javafx.scene.input.MouseEvent; import javafx.scene.layout.BorderPane; import javafx.scene.layout.Pane; import javafx.scene.paint.Color; import javafx.scene.paint.CycleMethod; import javafx.scene.paint.RadialGradient; import javafx.scene.paint.Stop; import javafx.scene.shape.Circle; import javafx.stage.Stage; public class Main扩展应用程序{ private static double SCENE_WIDTH = 1280; private static double SCENE_HEIGHT = 720; static Random = new Random(); 画布画布; GraphicsContext graphicsContext; AnimationTimer循环; Point2D mouseLocation = new Point2D(0,0); boolean mousePressed = false; Point2D prevMouseLocation = new Point2D(0,0); 场景场景; double brushMaxSize = 30; Image brush = createBrush(brushMaxSize,Color.CHOCOLATE); double brushWidthHalf = brush.getWidth()/ 2.0; double brushHeightHalf = brush.getHeight()/ 2.0; 双重压力= 0; double pressureDelay = 0.04; private Image [] brushVariations = new Image [256]; @Override public void start(Stage primaryStage){ BorderPane root = new BorderPane(); canvas = new Canvas(SCENE_WIDTH,SCENE_HEIGHT); for(int i = 0; i< brushVariations.length; i ++){ double size =(brushMaxSize - 1)/(double)brushVariations.length *(双)i + 1; brushVariations [i] = createBrush(size,Color.CHOCOLATE); } graphicsContext = canvas.getGraphicsContext2D(); Pane layerPane = new Pane(); layerPane.getChildren()。addAll(canvas); root.setCenter(layerPane); scene = new Scene(root,SCENE_WIDTH,SCENE_HEIGHT); primaryStage.setScene(scene); primaryStage.show(); addListeners(); startAnimation(); $ b private void startAnimation(){ loop = new AnimationTimer(){ @Override public void handle(long now){ if(mousePressed){ //试试这个 // graphicsContext.drawImage(brush ,mouseLocation.getX() - brushWidthHalf,mouseLocation.getY() - brushHeightHalf); //然后这个 bresenhamLine(prevMouseLocation.getX(),prevMouseLocation.getY(),mouseLocation.getX(),mouseLocation.getY()); 压力+ =压力延迟; if(pressure> 1){ pressure = 1; } }其他{ 压力= 0; prevMouseLocation = new Point2D(mouseLocation.getX(),mouseLocation.getY()); } }; loop.start(); } // https://de.wikipedia.org/wiki/Bresenham-Algorithmus private void bresenhamLine(double x0,double y0,double x1 ,double y1) { double dx = Math.abs(x1-x0),sx = x0 double dy = -Math.abs(y1-y0),sy = y0 double err = dx + dy,e2; / *错误值e_xy * / while(true){ int variation =(int)(pressure *(brushVariations.length - 1)); 图像brushVariation = brushVariations [变体]; graphicsContext.setGlobalAlpha(pressure); graphicsContext.drawImage(brushVariation,x0 - brushWidthHalf,y0 - brushHeightHalf); if(x0 == x1&& y0 == y1)break; e2 = 2. * err;如果(e2> dy){err + = dy; x0 + = sx; } / * e_xy + e_x> 0 * / if(e2 } } private void addListeners(){ scene.addEventFilter(MouseEvent.ANY,e - > ; { mouseLocation = new Point2D(e.getX(),e.getY()); mousePressed = e.isPrimaryButtonDown(); }); public static Image createImage(Node node){ WritableImage wi; SnapshotParameters parameters = new SnapshotParameters(); parameters.setFill(Color.TRANSPARENT); int imageWidth =(int)node.getBoundsInLocal()。getWidth(); int imageHeight =(int)node.getBoundsInLocal()。getHeight(); wi = new WritableImage(imageWidth,imageHeight); node.snapshot(parameters,wi); 返回wi; } public static Image createBrush(double radius,Color color){ //用给定颜色创建渐变图像 Circle brush =新的Circle(半径); RadialGradient gradient1 = new RadialGradient(0,0,0,0,radius,false,CycleMethod.NO_CYCLE,new Stop(0,color.deriveColor(1,1,1,0.3)),new Stop(1,color.deriveColor(1,1,1,0))); brush.setFill(gradient1); //创建图像 return createImage(brush); public static void main(String [] args){ launch(args); $ / code $ / pre $ hr 限制刷子长度的变化示例 import javafx.animation.AnimationTimer; import javafx.application.Application; 导入javafx.geometry.Point2D; import javafx.scene.Node; import javafx.scene.Scene; import javafx.scene.SnapshotParameters; import javafx.scene.canvas.Canvas; import javafx.scene.canvas.GraphicsContext; import javafx.scene.control.ColorPicker; import javafx.scene.image.Image; import javafx.scene.image.WritableImage; import javafx.scene.input.MouseEvent; import javafx.scene.layout.BorderPane; import javafx.scene.layout.Pane; import javafx.scene.paint.Color; import javafx.scene.paint.CycleMethod; import javafx.scene.paint.RadialGradient; import javafx.scene.paint.Stop; import javafx.scene.shape.Circle; import javafx.stage.Stage; public class Main扩展应用程序{ private static double SCENE_WIDTH = 1280; private static double SCENE_HEIGHT = 720; 画布画布; GraphicsContext graphicsContext; AnimationTimer循环; Point2D mouseLocation = new Point2D(0,0); boolean mousePressed = false; Point2D prevMouseLocation = new Point2D(0,0); 场景场景; double brushMaxSize = 30; 双重压力= 0; double pressureDelay = 0.04; double pressureDirection = 1; double strokeTimeMax = 1; double strokeTime = 0; double strokeTimeDelay = 0.07; private Image [] brushVariations = new Image [256]; ColorPicker colorPicker = new ColorPicker(); @Override public void start(Stage primaryStage){ BorderPane root = new BorderPane(); canvas = new Canvas(SCENE_WIDTH,SCENE_HEIGHT); graphicsContext = canvas.getGraphicsContext2D(); graphicsContext.setFill(Color.WHITE); graphicsContext.fillRect(0,0,SCENE_WIDTH,SCENE_HEIGHT); Pane layerPane = new Pane(); layerPane.getChildren()。addAll(canvas); colorPicker.setValue(Color.CHOCOLATE); colorPicker.setOnAction(e - > { createBrushVariations(); }); root.setCenter(layerPane); root.setTop(colorPicker); scene = new Scene(root,SCENE_WIDTH,SCENE_HEIGHT,Color.WHITE); primaryStage.setScene(scene); primaryStage.show(); createBrushVariations(); addListeners(); startAnimation(); private void createBrushVariations(){ for(int i = 0; i< brushVariations.length; i ++){ double size =(brushMaxSize - 1)/(double)brushVariations.length *(double)i + 1; brushVariations [i] = createBrush(size,colorPicker.getValue()); $ b private void startAnimation(){ loop = new AnimationTimer(){ @Override public void handle(long now){ if(mousePressed){ //试试这个 // graphicsContext.drawImage( brush,mouseLocation.getX() - // brushWidthHalf,mouseLocation.getY() - brushHeightHalf); //然后这个 bresenhamLine(prevMouseLocation.getX(),prevMouseLocation.getY(),mouseLocation.getX(),mouseLocation.getY()); //增加或减少 strokeTime + = strokeTimeDelay * pressureDirection; //反向 if(strokeTime> strokeTimeMax){ pressureDirection = -1; } // while still if(strokeTime> 0){ pressure + = pressureDelay * pressureDirection; //压力钳位值为[0,1] if(压力> 1){压力= 1; } else if(pressure pressure = 0; } }其他{ 压力= 0; } }其他{ 压力= 0; pressureDirection = 1; strokeTime = 0; prevMouseLocation = new Point2D(mouseLocation.getX(),mouseLocation.getY()); } }; loop.start(); } // https://de.wikipedia.org/wiki/Bresenham-Algorithmus private void bresenhamLine(double x0,double y0,double x1 ,double y1){ double dx = Math.abs(x1-x0),sx = x0 double dy = -Math.abs(y1-y0),sy = y0 double err = dx + dy,e2; / *错误值e_xy * / while(true){ int variation =(int)(pressure *(brushVariations.length - 1)); 图像brushVariation = brushVariations [变体]; graphicsContext.setGlobalAlpha(pressure); graphicsContext.drawImage(brushVariation,x0 - brushVariation.getWidth()/ 2.0,y0 - brushVariation.getHeight()/ 2.0); if(x0 == x1&& y0 == y1) break; e2 = 2. * err; if(e2> dy){ err + = dy; x0 + = sx; } / * e_xy + e_x> 0 * / if(e2 err + = dx; y0 + = sy; } / * e_xy + e_y< 0 * / } } private void addListeners(){ canvas.addEventFilter(MouseEvent.ANY,e - > { mouseLocation = new Point2D(e.getX(),e.getY()); mousePressed = e.isPrimaryButtonDown(); }) ; $ b public static Image createImage(Node node){ WritableImage wi; SnapshotParameters parameters = new SnapshotParameters(); parameters.setFill(Color.TRANSPARENT); int imageWidth =(int)node.getBoundsInLocal()。getWidth(); int imageHeight =(int)node.getBoundsInLocal()。getHeight(); wi = new WritableImage(imageWidth,imageHeight); node.snapshot(parameters,wi); 返回wi; $ b public static Image createBrush(double radius,Color color){ //用给定颜色创建渐变图像 Circle brush = new Circle(radius); RadialGradient gradient1 = new RadialGradient(0,0,0,0,radius,false,CycleMethod.NO_CYCLE,new Stop(0,color.deriveColor(1,1,1,0.3)),new Stop(1,color.deriveColor(1,1,1,0))); brush.setFill(gradient1); //创建图像 return createImage(brush); public static void main(String [] args){ launch(args); $ b 这就是它的样子: 或者使用不同的颜色,我在最后添加了一个颜色选择器例如: I'm trying to write a painting application in JavaFX. I want a brush resembling a real paintbrush, but I'm not sure how to start the algorithm. The code below shows my current paintbrush stroke, although it's a useful stroke, it's not really a paintbrush:import javafx.application.Application;import javafx.scene.Group;import javafx.scene.Scene;import javafx.scene.canvas.Canvas;import javafx.scene.canvas.GraphicsContext;import javafx.scene.input.MouseEvent;import javafx.scene.paint.Color;import javafx.scene.shape.StrokeLineCap;import javafx.scene.shape.StrokeLineJoin;import javafx.stage.Stage;import static javafx.scene.input.MouseEvent.*;public class BrushTester extends Application { private static final Color color = Color.CHOCOLATE; private static final double START_OPACITY = 0.3; private static final double OPACITY_MODIFIER = 0.002; private double currentOpacity = START_OPACITY; private double strokeWidth = 15; public static void main(String[] args) { Application.launch(BrushTester.class); } @Override public void start(Stage primaryStage) throws Exception { Canvas canvas = new Canvas(600d, 600d); GraphicsContext gc = canvas.getGraphicsContext2D(); canvas.addEventHandler(MOUSE_DRAGGED, e -> BrushTester.this.handleMouseDragged(gc, e)); canvas.addEventHandler(MOUSE_PRESSED, e -> handleMousePressed(gc, e)); canvas.addEventHandler(MOUSE_RELEASED, e -> handleMouseReleased(gc, e)); Group root = new Group(); root.getChildren().add(canvas); primaryStage.setScene(new Scene(root, Color.DARKGRAY)); primaryStage.show(); } private void configureGraphicsContext(GraphicsContext gc) { gc.setStroke(new Color(color.getRed(), color.getGreen(), color.getBlue(), currentOpacity)); gc.setLineCap(StrokeLineCap.ROUND); gc.setLineJoin(StrokeLineJoin.ROUND); gc.setLineWidth(strokeWidth); } public void handleMousePressed(GraphicsContext gc, MouseEvent e) { configureGraphicsContext(gc); gc.beginPath(); gc.moveTo(e.getX(), e.getY()); gc.stroke(); } public void handleMouseReleased(GraphicsContext gc, MouseEvent e) { currentOpacity = START_OPACITY; gc.closePath(); } public void handleMouseDragged(GraphicsContext gc, MouseEvent e) { currentOpacity = Math.max(0, currentOpacity - OPACITY_MODIFIER); configureGraphicsContext(gc); gc.lineTo(e.getX(), e.getY()); gc.stroke(); }}Anyone with some tips on how to get closer to the real thing? 解决方案 It all depends on what you're trying to achieve. Personally I would usean AnimationTimera customizable Brush (i. e. an Image) instead of a stroke, so you can specify size and hardnessa line drawing algorithm (like Bresenham) to connect the previous mouse location with the current one to get a full line between pointsA quick example with a simple drawing algorithm:import java.util.Random;import javafx.animation.AnimationTimer;import javafx.application.Application;import javafx.geometry.Point2D;import javafx.scene.Node;import javafx.scene.Scene;import javafx.scene.SnapshotParameters;import javafx.scene.canvas.Canvas;import javafx.scene.canvas.GraphicsContext;import javafx.scene.image.Image;import javafx.scene.image.WritableImage;import javafx.scene.input.MouseEvent;import javafx.scene.layout.BorderPane;import javafx.scene.layout.Pane;import javafx.scene.paint.Color;import javafx.scene.paint.CycleMethod;import javafx.scene.paint.RadialGradient;import javafx.scene.paint.Stop;import javafx.scene.shape.Circle;import javafx.stage.Stage;public class Main extends Application { private static double SCENE_WIDTH = 1280; private static double SCENE_HEIGHT = 720; static Random random = new Random(); Canvas canvas; GraphicsContext graphicsContext; AnimationTimer loop; Point2D mouseLocation = new Point2D( 0, 0); boolean mousePressed = false; Point2D prevMouseLocation = new Point2D( 0, 0); Scene scene; Image brush = createBrush( 30.0, Color.CHOCOLATE); double brushWidthHalf = brush.getWidth() / 2.0; double brushHeightHalf = brush.getHeight() / 2.0; @Override public void start(Stage primaryStage) { BorderPane root = new BorderPane(); canvas = new Canvas( SCENE_WIDTH, SCENE_HEIGHT); graphicsContext = canvas.getGraphicsContext2D(); Pane layerPane = new Pane(); layerPane.getChildren().addAll(canvas); root.setCenter(layerPane); scene = new Scene(root, SCENE_WIDTH, SCENE_HEIGHT); primaryStage.setScene(scene); primaryStage.show(); addListeners(); startAnimation(); } private void startAnimation() { loop = new AnimationTimer() { @Override public void handle(long now) { if( mousePressed) { // try this // graphicsContext.drawImage( brush, mouseLocation.getX() - brushWidthHalf, mouseLocation.getY() - brushHeightHalf); // then this bresenhamLine( prevMouseLocation.getX(), prevMouseLocation.getY(), mouseLocation.getX(), mouseLocation.getY()); } prevMouseLocation = new Point2D( mouseLocation.getX(), mouseLocation.getY()); } }; loop.start(); } // https://de.wikipedia.org/wiki/Bresenham-Algorithmus private void bresenhamLine(double x0, double y0, double x1, double y1) { double dx = Math.abs(x1-x0), sx = x0<x1 ? 1. : -1.; double dy = -Math.abs(y1-y0), sy = y0<y1 ? 1. : -1.; double err = dx+dy, e2; /* error value e_xy */ while( true){ graphicsContext.drawImage( brush, x0 - brushWidthHalf, y0 - brushHeightHalf); if (x0==x1 && y0==y1) break; e2 = 2.*err; if (e2 > dy) { err += dy; x0 += sx; } /* e_xy+e_x > 0 */ if (e2 < dx) { err += dx; y0 += sy; } /* e_xy+e_y < 0 */ } } private void addListeners() { scene.addEventFilter(MouseEvent.ANY, e -> { mouseLocation = new Point2D(e.getX(), e.getY()); mousePressed = e.isPrimaryButtonDown(); }); } public static Image createImage(Node node) { WritableImage wi; SnapshotParameters parameters = new SnapshotParameters(); parameters.setFill(Color.TRANSPARENT); int imageWidth = (int) node.getBoundsInLocal().getWidth(); int imageHeight = (int) node.getBoundsInLocal().getHeight(); wi = new WritableImage(imageWidth, imageHeight); node.snapshot(parameters, wi); return wi; } public static Image createBrush( double radius, Color color) { // create gradient image with given color Circle brush = new Circle(radius); RadialGradient gradient1 = new RadialGradient(0, 0, 0, 0, radius, false, CycleMethod.NO_CYCLE, new Stop(0, color.deriveColor(1, 1, 1, 0.3)), new Stop(1, color.deriveColor(1, 1, 1, 0))); brush.setFill(gradient1); // create image return createImage(brush); } public static void main(String[] args) { launch(args); }}Of course you can extend this with e. g.multiple layersJavaFX's blend modes on layer and graphicscontext levelto simulate force I'd use a paint delay (eg 200 ms) and a buffer for the mouse locations and let the opacity depend on whether the mouse is still pressed or notsmooth the lines by using bezier curves...Example with Brush variations when you start painting:import java.util.Random;import javafx.animation.AnimationTimer;import javafx.application.Application;import javafx.geometry.Point2D;import javafx.scene.Node;import javafx.scene.Scene;import javafx.scene.SnapshotParameters;import javafx.scene.canvas.Canvas;import javafx.scene.canvas.GraphicsContext;import javafx.scene.image.Image;import javafx.scene.image.WritableImage;import javafx.scene.input.MouseEvent;import javafx.scene.layout.BorderPane;import javafx.scene.layout.Pane;import javafx.scene.paint.Color;import javafx.scene.paint.CycleMethod;import javafx.scene.paint.RadialGradient;import javafx.scene.paint.Stop;import javafx.scene.shape.Circle;import javafx.stage.Stage;public class Main extends Application { private static double SCENE_WIDTH = 1280; private static double SCENE_HEIGHT = 720; static Random random = new Random(); Canvas canvas; GraphicsContext graphicsContext; AnimationTimer loop; Point2D mouseLocation = new Point2D( 0, 0); boolean mousePressed = false; Point2D prevMouseLocation = new Point2D( 0, 0); Scene scene; double brushMaxSize = 30; Image brush = createBrush( brushMaxSize, Color.CHOCOLATE); double brushWidthHalf = brush.getWidth() / 2.0; double brushHeightHalf = brush.getHeight() / 2.0; double pressure = 0; double pressureDelay = 0.04; private Image[] brushVariations = new Image[256]; @Override public void start(Stage primaryStage) { BorderPane root = new BorderPane(); canvas = new Canvas( SCENE_WIDTH, SCENE_HEIGHT); for( int i=0; i < brushVariations.length; i++) { double size = (brushMaxSize - 1) / (double) brushVariations.length * (double) i + 1; brushVariations[i] = createBrush( size, Color.CHOCOLATE); } graphicsContext = canvas.getGraphicsContext2D(); Pane layerPane = new Pane(); layerPane.getChildren().addAll(canvas); root.setCenter(layerPane); scene = new Scene(root, SCENE_WIDTH, SCENE_HEIGHT); primaryStage.setScene(scene); primaryStage.show(); addListeners(); startAnimation(); } private void startAnimation() { loop = new AnimationTimer() { @Override public void handle(long now) { if( mousePressed) { // try this // graphicsContext.drawImage( brush, mouseLocation.getX() - brushWidthHalf, mouseLocation.getY() - brushHeightHalf); // then this bresenhamLine( prevMouseLocation.getX(), prevMouseLocation.getY(), mouseLocation.getX(), mouseLocation.getY()); pressure += pressureDelay; if( pressure > 1) { pressure = 1; } } else { pressure = 0; } prevMouseLocation = new Point2D( mouseLocation.getX(), mouseLocation.getY()); } }; loop.start(); } // https://de.wikipedia.org/wiki/Bresenham-Algorithmus private void bresenhamLine(double x0, double y0, double x1, double y1) { double dx = Math.abs(x1-x0), sx = x0<x1 ? 1. : -1.; double dy = -Math.abs(y1-y0), sy = y0<y1 ? 1. : -1.; double err = dx+dy, e2; /* error value e_xy */ while( true){ int variation = (int) (pressure * (brushVariations.length - 1)); Image brushVariation = brushVariations[ variation ]; graphicsContext.setGlobalAlpha(pressure); graphicsContext.drawImage( brushVariation, x0 - brushWidthHalf, y0 - brushHeightHalf); if (x0==x1 && y0==y1) break; e2 = 2.*err; if (e2 > dy) { err += dy; x0 += sx; } /* e_xy+e_x > 0 */ if (e2 < dx) { err += dx; y0 += sy; } /* e_xy+e_y < 0 */ } } private void addListeners() { scene.addEventFilter(MouseEvent.ANY, e -> { mouseLocation = new Point2D(e.getX(), e.getY()); mousePressed = e.isPrimaryButtonDown(); }); } public static Image createImage(Node node) { WritableImage wi; SnapshotParameters parameters = new SnapshotParameters(); parameters.setFill(Color.TRANSPARENT); int imageWidth = (int) node.getBoundsInLocal().getWidth(); int imageHeight = (int) node.getBoundsInLocal().getHeight(); wi = new WritableImage(imageWidth, imageHeight); node.snapshot(parameters, wi); return wi; } public static Image createBrush( double radius, Color color) { // create gradient image with given color Circle brush = new Circle(radius); RadialGradient gradient1 = new RadialGradient(0, 0, 0, 0, radius, false, CycleMethod.NO_CYCLE, new Stop(0, color.deriveColor(1, 1, 1, 0.3)), new Stop(1, color.deriveColor(1, 1, 1, 0))); brush.setFill(gradient1); // create image return createImage(brush); } public static void main(String[] args) { launch(args); }}Example with variation for limiting the brush lengthimport javafx.animation.AnimationTimer;import javafx.application.Application;import javafx.geometry.Point2D;import javafx.scene.Node;import javafx.scene.Scene;import javafx.scene.SnapshotParameters;import javafx.scene.canvas.Canvas;import javafx.scene.canvas.GraphicsContext;import javafx.scene.control.ColorPicker;import javafx.scene.image.Image;import javafx.scene.image.WritableImage;import javafx.scene.input.MouseEvent;import javafx.scene.layout.BorderPane;import javafx.scene.layout.Pane;import javafx.scene.paint.Color;import javafx.scene.paint.CycleMethod;import javafx.scene.paint.RadialGradient;import javafx.scene.paint.Stop;import javafx.scene.shape.Circle;import javafx.stage.Stage;public class Main extends Application { private static double SCENE_WIDTH = 1280; private static double SCENE_HEIGHT = 720; Canvas canvas; GraphicsContext graphicsContext; AnimationTimer loop; Point2D mouseLocation = new Point2D(0, 0); boolean mousePressed = false; Point2D prevMouseLocation = new Point2D(0, 0); Scene scene; double brushMaxSize = 30; double pressure = 0; double pressureDelay = 0.04; double pressureDirection = 1; double strokeTimeMax = 1; double strokeTime = 0; double strokeTimeDelay = 0.07; private Image[] brushVariations = new Image[256]; ColorPicker colorPicker = new ColorPicker(); @Override public void start(Stage primaryStage) { BorderPane root = new BorderPane(); canvas = new Canvas(SCENE_WIDTH, SCENE_HEIGHT); graphicsContext = canvas.getGraphicsContext2D(); graphicsContext.setFill(Color.WHITE); graphicsContext.fillRect(0, 0, SCENE_WIDTH, SCENE_HEIGHT); Pane layerPane = new Pane(); layerPane.getChildren().addAll(canvas); colorPicker.setValue(Color.CHOCOLATE); colorPicker.setOnAction(e -> { createBrushVariations(); }); root.setCenter(layerPane); root.setTop(colorPicker); scene = new Scene(root, SCENE_WIDTH, SCENE_HEIGHT, Color.WHITE); primaryStage.setScene(scene); primaryStage.show(); createBrushVariations(); addListeners(); startAnimation(); } private void createBrushVariations() { for (int i = 0; i < brushVariations.length; i++) { double size = (brushMaxSize - 1) / (double) brushVariations.length * (double) i + 1; brushVariations[i] = createBrush(size, colorPicker.getValue()); } } private void startAnimation() { loop = new AnimationTimer() { @Override public void handle(long now) { if (mousePressed) { // try this // graphicsContext.drawImage( brush, mouseLocation.getX() - // brushWidthHalf, mouseLocation.getY() - brushHeightHalf); // then this bresenhamLine(prevMouseLocation.getX(), prevMouseLocation.getY(), mouseLocation.getX(), mouseLocation.getY()); // increasing or decreasing strokeTime += strokeTimeDelay * pressureDirection; // invert direction if (strokeTime > strokeTimeMax) { pressureDirection = -1; } // while still if (strokeTime > 0) { pressure += pressureDelay * pressureDirection; // clamp value of pressure to be [0,1] if (pressure > 1) { pressure = 1; } else if (pressure < 0) { pressure = 0; } } else { pressure = 0; } } else { pressure = 0; pressureDirection = 1; strokeTime = 0; } prevMouseLocation = new Point2D(mouseLocation.getX(), mouseLocation.getY()); } }; loop.start(); } // https://de.wikipedia.org/wiki/Bresenham-Algorithmus private void bresenhamLine(double x0, double y0, double x1, double y1) { double dx = Math.abs(x1 - x0), sx = x0 < x1 ? 1. : -1.; double dy = -Math.abs(y1 - y0), sy = y0 < y1 ? 1. : -1.; double err = dx + dy, e2; /* error value e_xy */ while (true) { int variation = (int) (pressure * (brushVariations.length - 1)); Image brushVariation = brushVariations[variation]; graphicsContext.setGlobalAlpha(pressure); graphicsContext.drawImage(brushVariation, x0 - brushVariation.getWidth() / 2.0, y0 - brushVariation.getHeight() / 2.0); if (x0 == x1 && y0 == y1) break; e2 = 2. * err; if (e2 > dy) { err += dy; x0 += sx; } /* e_xy+e_x > 0 */ if (e2 < dx) { err += dx; y0 += sy; } /* e_xy+e_y < 0 */ } } private void addListeners() { canvas.addEventFilter(MouseEvent.ANY, e -> { mouseLocation = new Point2D(e.getX(), e.getY()); mousePressed = e.isPrimaryButtonDown(); }); } public static Image createImage(Node node) { WritableImage wi; SnapshotParameters parameters = new SnapshotParameters(); parameters.setFill(Color.TRANSPARENT); int imageWidth = (int) node.getBoundsInLocal().getWidth(); int imageHeight = (int) node.getBoundsInLocal().getHeight(); wi = new WritableImage(imageWidth, imageHeight); node.snapshot(parameters, wi); return wi; } public static Image createBrush(double radius, Color color) { // create gradient image with given color Circle brush = new Circle(radius); RadialGradient gradient1 = new RadialGradient(0, 0, 0, 0, radius, false, CycleMethod.NO_CYCLE, new Stop(0, color.deriveColor(1, 1, 1, 0.3)), new Stop(1, color.deriveColor(1, 1, 1, 0))); brush.setFill(gradient1); // create image return createImage(brush); } public static void main(String[] args) { launch(args); }}This is how it looks like:or using different colors, I added a color picker in the last example: 这篇关于JavaFX中的画笔描边的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 上岸,阿里云! 08-02 00:06