I want my blue circle go from one light blue circle to another, but only does half.
The coordinates appear being the same:
initial_lightblue: 211.7,230.5
blue: 193.7,239.1
final_lighblue: 193.7,239.1
My code
TranslateTransition transition = new TranslateTransition();
transition.setToX(c.getX());
transition.setToY(c.getY());
transition.setNode(view);
transition.setInterpolator(Interpolator.LINEAR);
transition.play();
Any sugestions?
From documentation
TranslateTransition : This Transition creates a move/translate animation that spans its duration. This is done by updating the
translateX, translateY and translateZ variables of the node at regular
interval.
So it's going to use the translate properties in order to relocate the actual node inside your pane. With that being said if you locate your node at x=50 and y=50 by setting its layoutX and layoutY properties their translation values will be 0, so if you actually try to set the end coordinates of the TranslateTransition to be ex. x = 100 y = 100 then its going to move your Node to the x = 150 and y = 150 and not to the (x,y) = (100,100) cause its going to change the translation property (x and y) from 0 to 100 which will move the node eventually to (x,y) = 150,150.
With that been said here is an example :
import javafx.animation.Interpolator;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class Test extends Application {
private Circle c1;
private Circle c2;
#Override
public void start(Stage stage) throws Exception {
AnchorPane pane = new AnchorPane();
// Set only the radius
c1 = new Circle(5);
c1.setFill(Color.BLUE);
// Let's translate the c1 to the location we want
c1.setTranslateX(50);
c1.setTranslateY(60);
// The same for circle2
c2 = new Circle(5);
c2.setFill(Color.RED);
c2.setTranslateX(120);
c2.setTranslateY(200);
pane.getChildren().addAll(c1, c2);
pane.setOnMouseClicked(e -> {
startAnimation();
});
stage.setScene(new Scene(pane, 400, 500));
stage.show();
}
private void startAnimation() {
TranslateTransition transition = new TranslateTransition();
transition.setNode(c1);
transition.setToX(c2.getTranslateX());
transition.setToY(c2.getTranslateY());
transition.setInterpolator(Interpolator.LINEAR);
transition.play();
}
public static void main(String[] args) {
launch(args);
}
}
P.S : In your code example you are referring to circles but you call c.getX() to actually take the x of the circle, that is quite strange cause the Circle class doesn't have method getX() I am quessing you are referreing to getCenterX() or those are not Circles .
Related
I've managed to move the camera using mouse drag. However, the problem is that once I've moved the camera and released the mouse press, the camera returns back to the origin.
I want the camera to remain in the changed position instead so that when I use the mouse again to move the camera, it moves from that position to wherever instead of from the origin.
package application;
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Camera;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.Scene;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Sphere;
public class Main extends Application {
private static final int WIDTH = 900;
private static final int HEIGHT = 600;
//Tracks drag starting point for x and y
private double anchorX, anchorY;
#Override
public void start(Stage primaryStage) {
Sphere sphere = new Sphere(50);
Group group = new Group();
group.getChildren().add(sphere);
Camera camera = new PerspectiveCamera();
Scene scene = new Scene(group, WIDTH, HEIGHT);
scene.setFill(Color.SILVER);
scene.setCamera(camera);
sphere.setTranslateX(WIDTH / 2);
sphere.setTranslateY(HEIGHT / 2);
initMouseControl(scene, camera, primaryStage, sphere);
primaryStage.setTitle("Solar System Simulator");
primaryStage.setScene(scene);
primaryStage.show();
}
private void initMouseControl(Scene scene, Camera camera, Stage stage, Sphere sphere) {
scene.setOnMousePressed(event -> {
//Save start points
anchorX = event.getSceneX();
anchorY = event.getSceneY();
});
scene.setOnMouseDragged(event -> {
camera.setTranslateY(anchorY - event.getSceneY());
camera.setTranslateX(anchorX - event.getSceneX());
});
stage.addEventHandler(ScrollEvent.SCROLL, event -> {
sphere.setTranslateZ(sphere.getTranslateZ() + event.getDeltaY());
});
}
public static void main(String[] args) {
launch(args);
}
}
I've tried googling to find a solution but couldn't find much on camera movement with javafx
First of all, your scene does not have depth buffer enabled . These constructors enable 3d features in a scene instance Scene(Parent root,double width,double height,boolean depthBuffer) and Scene(Parent root,double width,double height,boolean depthBuffer,SceneAntialiasing antiAliasing) both with depthBufer boolean set to true. Second ,if you want to translate a node ; translate it getting its current coordinates and then add new coords . that will avoid reset coords . I divided the dragged result by 10 because it brought too high values
public class Main extends Application {
private static final int WIDTH = 900;
private static final int HEIGHT = 600;
//Tracks drag starting point for x and y
private double anchorX, anchorY;
#Override
public void start(Stage primaryStage) {
Sphere sphere = new Sphere(50);
Group group = new Group();
group.getChildren().add(sphere);
Camera camera = new PerspectiveCamera();
// for 3d Scene constructor must have zbuffer= true and SceneAntialiasing
Scene scene = new Scene(group, WIDTH, HEIGHT, true, SceneAntialiasing.BALANCED);
scene.setFill(Color.SILVER);
scene.setCamera(camera);
sphere.setTranslateX(WIDTH / 2);
sphere.setTranslateY(HEIGHT / 2);
initMouseControl(scene, camera, primaryStage, sphere);
primaryStage.setTitle("Solar System Simulator");
primaryStage.setScene(scene);
primaryStage.show();
}
private void initMouseControl(Scene scene, Camera camera, Stage stage, Sphere sphere) {
scene.setOnMousePressed(event -> {
//Save start points
anchorX = event.getSceneX();
anchorY = event.getSceneY();
});
// translating camera from its current coords pluss event
scene.setOnMouseDragged(event -> {
camera.setTranslateY(camera.getTranslateY() + ((anchorY - event.getSceneY()) / 10));
camera.setTranslateX(camera.getTranslateX() + ((anchorX - event.getSceneX()) / 10));
});
stage.addEventHandler(ScrollEvent.SCROLL, event -> {
sphere.setTranslateZ(sphere.getTranslateZ() + event.getDeltaY());
});
}
public static void main(String[] args) {
launch(args);
}
}
I don't know in how far Google can help you fix your logic problems. I did no further analysis of your code but at least each time when you press the mouse button your camera position is reset to zero by design. Your changes are not cumulative.
My task is to write a program that makes a circle ball that gradually fades away as the ball moves to the right. But it's not working, I can make the ball move when the mouse is dragged but the opacity is the same. Can you guys help me? I don't know how to convert the opacity value into double
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.scene.paint.Color;
public class Project3 extends Application
{
public static void main(String[] args) {
launch(args);
}
public void start(Stage primaryStage) {
Pane root = createRootPane();
Scene scene1 = new Scene(root);
primaryStage.setScene(scene1);
primaryStage.setTitle(" Hai Vo ");
primaryStage.show();
}
public Pane createRootPane()
{
Circle ball = new Circle (100,50,25);
Pane root = new Pane(ball);
root.setMinSize(300,300);
root.setOnMouseDragged (
event ->
{
double x = event.getX();
ball.setCenterX(event.getX());
ball.setCenterY(event.getY());
ball.opacityProperty().bind(ball.centerXProperty());
double opacity = ball.opacityProperty();
ball.setOpacity(opacity);
} );
return root;
}
}
First, move the binding out of the event handler. The binding will ensure the opacity is always updated when the centerX property updates.
Second, don't set bound values; the last two lines of the event handler just set the opacity to its current value anyway.
Third, the opacity should be between 0 and 1. You want it to be 1 when centerX is 0 and 0 when centerX is 300 (or, generally, the width of the pane).
What you need is (in pseudocode)
opacity = 1 - ball.centerX / root.width
= (ball.centerX / root.width) * (-1) + 1
which you can express in bindings with
ball.centerXProperty()
.divide(root.widthProperty())
.multiply(-1)
.add(1)
So put together, you need:
public Pane createRootPane() {
Circle ball = new Circle (100,50,25);
Pane root = new Pane(ball);
ball.opacityProperty().bind(
ball.centerXProperty()
.divide(root.widthProperty())
.multiply(-1)
.add(1)
);
root.setMinSize(300,300);
root.setOnMouseDragged (
event ->
{
ball.setCenterX(event.getX());
ball.setCenterY(event.getY());
} );
return root;
}
I'm writing an educational application using JavaFX in which the user can draw and manipulate Bezier curves Line, QuadCurve, and CubicCurve. These curves should have the capability to be dragged with mouse. I've got two options available:
1- Using classes Line, QuadCurve, and CubicCurve, and then filling them with transparent color, and stroke them with another color, say black. The problem that arises for this option is that the user wants to drag a curve, but sees that another curve is dragged. The reason for this is that the curve that user is going to drag, resides below another one in the scene graph. For example, in the following figure the smaller curve is not capable of dragging, since it is below the larger one in the scene graph.
2- Using class javafx.scene.shape.Path, in which case the problem is that manipulating such a path is a little bit more complicated, since it's composed of some PathElements, and simply manipulating the elements does not change the Path, unless we remove an element from its elements property, and add a new one. Therefore I prefer approach 1.
How can I Overcome the problem arising in the first approach?
Thank you in advance for your help. A simplified version of my program code is as follows.
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.shape.CubicCurve;
import javafx.scene.shape.QuadCurve;
import javafx.stage.Stage;
public class SampleForStackOverflow extends Application
{
double lastMouseX;
double lastMouseY;
double lastTranslateX;
double lastTranslateY;
#Override
public void start(Stage window)
{
final double STROKE_WIDTH = 5;
QuadCurve quad = new QuadCurve(100, 200, 150, 50, 200, 200);
quad.setFill(Color.TRANSPARENT);
quad.setStroke(Color.BLACK);
quad.setStrokeWidth(STROKE_WIDTH);
quad.setOnMousePressed(e -> {
lastMouseX = e.getSceneX();
lastMouseY = e.getSceneY();
lastTranslateX = quad.getTranslateX();
lastTranslateY = quad.getTranslateY();
});
quad.setOnMouseDragged(e -> followMouse(quad, e));
CubicCurve cubic = new CubicCurve(0, 300, 100, 0, 300, 0, 300, 300);
cubic.setFill(Color.TRANSPARENT);
cubic.setStroke(Color.BLACK);
cubic.setStrokeWidth(STROKE_WIDTH);
cubic.setOnMousePressed(e -> {
lastMouseX = e.getSceneX();
lastMouseY = e.getSceneY();
lastTranslateX = cubic.getTranslateX();
lastTranslateY = cubic.getTranslateY();
});
cubic.setOnMouseDragged(e -> followMouse(cubic, e));
Group root = new Group(quad, cubic);
Scene scene = new Scene(root, 500, 500);
window.setScene(scene);
window.show();
}
private void followMouse(Node node, MouseEvent e)
{
double deltaX = e.getSceneX() - lastMouseX;
double deltaY = e.getSceneY() - lastMouseY;
node.setTranslateX(deltaX + lastTranslateX);
node.setTranslateY(deltaY + lastTranslateY);
}
public static void main(String[] args) {
launch(args);
}
}
Instead of using a transparent color in your first scenario, you should explicitly set the fill color of your curves to null and set pickOnBounds to false. A transparent color will still catch the mouse events but null will not and when pickOnBounds if false the mouse events will be caught only if you are exactly over the colored parts of your shape.
Creating the QuadCurve and the CubicCurve using Paths seems to work fine for me. Here is a complete example :
import javafx.application.Application;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.shape.CubicCurveTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.scene.shape.QuadCurveTo;
import javafx.stage.Stage;
public class SampleForStackOverflow extends Application {
private final double STROKE_WIDTH = 5;
private double lastMouseX;
private double lastMouseY;
private double lastTranslateX;
private double lastTranslateY;
#Override
public void start(Stage window) {
Path quad = initQuadCurve(100, 200, 200, 200, 150, 50);
Path cubic = initCubicCurve(0, 300, 300, 300, 100, 0, 300, 0);
Pane root = new Pane(cubic, quad);
Scene scene = new Scene(root, 500, 500);
window.setScene(scene);
window.show();
}
private Path initQuadCurve(int xStart, int yStart, int xEnd, int yEnd, int controlX, int controlY) {
Path curvePath = new Path();
curvePath.setStrokeWidth(STROKE_WIDTH);
MoveTo moveTo = new MoveTo(xStart, yStart);
QuadCurveTo quadTo = new QuadCurveTo();
quadTo.setControlX(controlX);
quadTo.setControlY(controlY);
quadTo.setX(xEnd);
quadTo.setY(yEnd);
curvePath.getElements().addAll(moveTo, quadTo);
curvePath.setOnMousePressed(e -> {
lastMouseX = e.getSceneX();
lastMouseY = e.getSceneY();
lastTranslateX = curvePath.getTranslateX();
lastTranslateY = curvePath.getTranslateY();
});
curvePath.setOnMouseDragged(e -> followMouse(curvePath, e));
return curvePath;
}
private Path initCubicCurve(int xStart, int yStart, int xEnd, int yEnd, int x1Control, int y1Control, int x2Control,
int y2Control) {
Path curvePath = new Path();
curvePath.setStrokeWidth(STROKE_WIDTH);
MoveTo moveTo = new MoveTo(xStart, yStart);
CubicCurveTo cubicTo = new CubicCurveTo();
cubicTo.setControlX1(x1Control);
cubicTo.setControlY1(y1Control);
cubicTo.setControlX2(x2Control);
cubicTo.setControlY2(y2Control);
cubicTo.setX(xEnd);
cubicTo.setY(yEnd);
curvePath.getElements().addAll(moveTo, cubicTo);
curvePath.setOnMousePressed(e -> {
lastMouseX = e.getSceneX();
lastMouseY = e.getSceneY();
lastTranslateX = curvePath.getTranslateX();
lastTranslateY = curvePath.getTranslateY();
});
curvePath.setOnMouseDragged(e -> followMouse(curvePath, e));
return curvePath;
}
private void followMouse(Node node, MouseEvent e) {
double deltaX = e.getSceneX() - lastMouseX;
double deltaY = e.getSceneY() - lastMouseY;
node.setTranslateX(deltaX + lastTranslateX);
node.setTranslateY(deltaY + lastTranslateY);
}
public static void main(String[] args) {
launch(args);
}
}
To be honest my first attempt was to call quad.setPickOnBounds(false); (and for the cubic as well ) as suggested on both post below :
Mouse Events get Ignored on the Underlying Layer
JavaFX Pass MouseEvents through Transparent Node to Children
But its not working or I miss something, but if creating the path by yourself works find its unnecessary to complicate thing more. In any case I recommend to have a look on the links if you want to follow the first approach. In case you are going to follow the second approach manipulating the paths is not going to be very difficult in my opinion.
I'm learning about JavaFX by trying to build a (very) small platforming game. I thought it might be a good idea to simulate jumping by using PathTransition along an Arc shape. Like this:
import javafx.util.Duration;
import javafx.animation.PathTransition;
import javafx.scene.shape.Arc;
public void jump() {
PathTransition jump = new PathTransition();
Arc path = new Arc(
figure().getX() + 20, //figure is a custom shape
figure().getY() + figure().getHeight() / 2,
20, 80, 360, 180);
jump.setPath(path);
jump.setNode(figure);
jump.setAutoReverse(false);
jump.setDuration(Duration.millis(3000));
jump.setCycleCount(1);
jump.play();
}
Event handler:
figure.setOnKeyPressed((e) -> {
if (e.getCode() == KeyCode.SPACE) {
jump();
}
});
I discovered that doing this actually makes my figure jump BACKWARDS because the animation starts at the right end of the arc and ends at the left end. I tried looking through the documentation for Arc and PathTransition but couldn't find anything to help me solve the problem. What am I missing here?
The last two values you provide to the Arc constructor are the start angle and angular length of the arc (in degrees). Angles are measured relative to the positive x-axis in a counter-clockwise direction. So you are starting at 360 degrees (which of course is equivalent to 0 degrees: i.e. (centerX + radiusX, centerY)) and heading counter-clockwise (up and left) for a half circle. To jump right, I think you want the start angle to be 180 and the angular length to be -180 (negative to rotate clockwise, a half-ellipse).
Note the PathTransition moves the center of the node along the path, by changing its translateX and translateY properties. So to do a half-turn clockwise from the negative x-axis to the positive x-axis, you need centerX to be the initial horizontal center of the shape, plus the radiusX, and centerY to be the initial vertical center of the shape.
Here's a SSCCE that jumps the way you want (I think...). I used set methods on the Arc instead of the constructor, just for clarity as what each value is doing. You can achieve the same with the constructor if you prefer.
import javafx.animation.PathTransition;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Arc;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class ArcTransitionTest extends Application {
private final double xJumpRadius = 20 ;
private final double yJumpRadius = 80 ;
#Override
public void start(Stage primaryStage) {
Rectangle rect = new Rectangle(50, 200, 50, 100);
rect.setFill(Color.CORNFLOWERBLUE);
Button left = new Button("<");
left.setOnAction(e -> jumpLeft(rect));
Button right = new Button(">");
right.setOnAction(e -> jumpRight(rect));
HBox controls = new HBox(5, left, right);
controls.setPadding(new Insets(10));
controls.setAlignment(Pos.CENTER);
Pane pane = new Pane(rect);
BorderPane root = new BorderPane(pane, null, null, controls, null);
primaryStage.setScene(new Scene(root, 600, 600));
primaryStage.show();
}
private void jumpRight(Rectangle rect) {
jump(rect, 180, -180, getHorizontalCenter(rect) + xJumpRadius, getVerticalCenter(rect));
}
private void jumpLeft(Rectangle rect) {
jump(rect, 0, 180, getHorizontalCenter(rect) - xJumpRadius, getVerticalCenter(rect));
}
private void jump(Rectangle rect, double startAngle, double angularLength, double centerX, double centerY) {
Arc arc = new Arc();
arc.setCenterX(centerX);
arc.setCenterY(centerY);
arc.setRadiusX(xJumpRadius);
arc.setRadiusY(yJumpRadius);
arc.setStartAngle(startAngle);
arc.setLength(angularLength);
PathTransition transition = new PathTransition(Duration.seconds(1), arc, rect);
transition.playFromStart();
}
private double getHorizontalCenter(Rectangle rect) {
return rect.getX() + rect.getTranslateX() + rect.getWidth() / 2 ;
// Alternatively:
// Bounds b = rect.getBoundsInParent();
// return (b.getMinX() + b.getMaxX()) / 2 ;
}
private double getVerticalCenter(Rectangle rect) {
return rect.getY() + rect.getTranslateY() + rect.getHeight() / 2 ;
// Alternatively:
// Bounds b = rect.getBoundsInParent();
// return (b.getMinY() + b.getMaxY()) / 2 ;
}
public static void main(String[] args) {
launch(args);
}
}
i have a draggable container in JavaFX. This Container is implemented in a PopUp. I can drag the Container, but if i drag it, the mouse-event hasn't a constant coordinate. There switchs the mouse position very fast between 2 fix Positions.
Thats my code:
container.setOnMouseDragged(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent me) {
// TODO Auto-generated method stub
if(dragAct==true){
//Set the Position of the PopUp to the Position of the Mouse
setX(me.getX());
setY(me.getY());
}
}
});
The containe is a VBox. The Main-Class is a extended Version of the PopUp-Class.
JavaFX Container Draggable
The setX and setY methods you call set the position of the Popup in screen coordinates. The calls to me.getX() and me.getY() give you the coordinates of the mouse relative to the container. When you move the popup, the container also moves, so the position of the mouse has changed relative to the container. So your calculations are not going to be consistent from one dragging event to the next.
The fix is to compute the positions relative to something that is fixed. Since you are moving the popup, which is a window, the fixed coordinate system is the screen coordinate system. MouseEvent has getScreenX and getScreenY methods you can use to easily get these.
I like to implement dragging by saving the last mouse location and then computing the distance moved on drag. There are other (possibly less verbose) ways to do this but to me this is clearest:
import javafx.application.Application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.geometry.Point2D;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Popup;
import javafx.stage.Stage;
public class DraggingPopup extends Application {
#Override
public void start(Stage primaryStage) {
Button button = new Button("Show popup");
button.setOnAction(event -> showDraggablePopup(primaryStage));
StackPane root = new StackPane(button);
Scene scene = new Scene(root, 250, 75);
primaryStage.setScene(scene);
primaryStage.show();
}
private void showDraggablePopup(Stage owner) {
Popup popup = new Popup();
Button closeButton = new Button("Close");
closeButton.setOnAction(event -> popup.hide());
StackPane container = new StackPane(closeButton);
container.setStyle("-fx-background-color: steelblue;");
container.setMinWidth(300);
container.setMinHeight(125);
// Dragging implementation:
ObjectProperty<Point2D> mouseLocation = new SimpleObjectProperty<>();
container.setOnMousePressed(event ->
mouseLocation.set(new Point2D(event.getScreenX(), event.getScreenY())));
container.setOnMouseDragged(event -> {
if (mouseLocation.get() != null) {
double x = event.getScreenX();
double deltaX = x - mouseLocation.get().getX() ;
double y = event.getScreenY();
double deltaY = y - mouseLocation.get().getY() ;
//in case of 2 or more computer screens this help me to avoid get stuck on 1 screen
if(Math.abs(popup.getX()-x)>popup.getWidth()){
popup.setX(x);
popup.setY(y);
}else {
popup.setX(popup.getX() + deltaX);
popup.setY(popup.getY() + deltaY);
}
mouseLocation.set(new Point2D(x, y));
}
});
container.setOnMouseReleased(event -> mouseLocation.set(null));
popup.getScene().setRoot(container);
popup.show(owner);
}
public static void main(String[] args) {
launch(args);
}
}