2015-11-27 2 views
2

Я хочу перетащить круг или панель по заданному пути SVG в JavaFX. Он должен работать как слайдер. Так что, если круг находится в самом начале пути, то «слайдер» должен возвращать 0, если пользователь перетаскивает круг в конце концов, «слайдер» должен возвращать 100.JavaFX Как перетаскивать объект по заданному пути SVG - Как слайдер

см.прикрепленную графика: Link to image

Путь должен быть любым SVG-контуром с началом и концом.

Я нашел этот пример, но я не могу «преобразовать» его в JavaFX.

Link to example

var D = document.createElement('div'); 
TweenMax.set('svg',{overflow:"visible"}) 
TweenMax.set('.knob',{x:10,y:80}) 

var tl = new TimelineMax({paused:true}) 
.from("#path2",1,{drawSVG:"0%",stroke:'orange',ease:Linear.easeNone}) 
.to('.knob',1,{bezier:{type:"quadratic",values:[{x:10,y:80},{x:150,y:0},{x:300,y:80}]},ease:Linear.easeNone},0); 

Draggable.create(D,{trigger:".knob", 
type:'x', 
throwProps:true, 
bounds:{minX:0,maxX:300}, 
onDrag:Update, 
onThrowUpdate:Update}); 
function Update(){tl.progress(Math.abs(this.x/300))}; 

TweenMax.to('#path1',0.5,{strokeDashoffset:-10,repeat:-1,ease:Linear.easeNone}) 

Я признателен за любую помощь. Thx.

ответ

1

Я нашел этот очень полезный пример от Йорна Хеймистера, который использует библиотеку Apache Batik для анализа пути SVG. JavaFX - Alien Rallye with SVGPath and PathTransition

С его JavaFXPathElementHandler я смог преобразовать путь SVG в путь JavaFX.

Я создал анимацию пути с длительностью 500 секунд. Все позиции (x, y) круга, движущиеся по этому пути, сохраняются внутри массива.

При перетаскивании круга функция выполняет поиск точки с кратчайшим расстоянием до мыши в массиве. Индексом этой позиции является время (в секундах) в анимации, которая затем отображается.

Это решение отлично подходит для меня. Может быть, есть большой потенциал оптимизации, но в моем случае этого было достаточно.

Вам понадобятся следующие библиотеки: батик parser.jar батик util.jar

Они находятся канд здесь: Apache Batik Library

SVGPathSlider.java

package svgpathslider; 

import javafx.scene.paint.Color; 
import java.util.ArrayList; 
import java.util.List; 
import javafx.animation.PathTransition; 
import javafx.application.Application; 
import javafx.event.EventHandler; 
import javafx.geometry.Point2D; 
import javafx.scene.Scene; 
import javafx.scene.input.MouseEvent; 
import javafx.scene.layout.Pane; 
import javafx.scene.shape.Circle; 
import javafx.scene.shape.Path; 
import javafx.stage.Stage; 
import javafx.util.Duration; 

import org.apache.batik.parser.PathParser; 


public class SVGPathSlider extends Application { 

    private final static double WIDTH = 1488; 
    private final static double HEIGHT = 840; 

    private final static int SLIDER_MIN = 0; 
    private final static int SLIDER_MAX = 100; 

    private final static double ANIMATION_DURATION = 500.0d; 

    private double _initX; 
    private double _initY; 
    private Point2D _dragAnchor; 

    private PathTransition _pathTransition; 
    private Circle _circle; 

    private List<Point2D> _pathPointList; 

    private int _actIndex; 

    private int _sliderIndex = 0; 

    @Override 
    public void start(Stage primaryStage) { 
     Pane root = new Pane(); 

     // Parse the SVG Path with Apache Batik and create a Path 
     PathParser parser = new PathParser(); 
     JavaFXPathElementHandler handler = new JavaFXPathElementHandler("track"); 
     parser.setPathHandler(handler); 

     // SVG Path 
     parser.parse("M1411.789,59.381c-32.315,6.982-60.416,21.861-85.014,43.752c-19.922,17.731-35.061,38.869-45.943,63.058\n" + 
"  c-5.333,11.853-9.46,24.204-12.018,36.946c-1.175,5.859-2.027,11.836-2.615,17.849c-0.784,8.039-1.604,16.047-1.405,24.125\n" + 
"  c0.544,22.257,4.783,43.825,12.943,64.524c3.956,10.034,8.61,19.806,13.214,29.573c10.041,21.306,20.268,42.525,30.387,63.795\n" + 
"  c5.109,10.741,10.271,21.46,15.19,32.288c7.075,15.575,14.958,30.839,19.51,47.438c3.979,14.508,6.568,29.219,7.169,44.322\n" + 
"  c0.331,8.324,0.435,16.646-0.246,24.884c-1.532,18.565-5.443,36.725-12.43,54.038c-8.359,20.721-19.532,39.866-34.275,56.79\n" + 
"  c-11.257,12.923-24.016,24.286-38.271,33.754c-15.338,10.186-31.729,18.501-49.484,23.784c-9.592,2.853-19.303,5.104-29.216,6.816\n" + 
"  c-15.327,2.646-30.695,2.349-46.054,2.007c-9.38-0.208-18.705-2.263-28.096-2.838c-6.393-0.393-12.754-1.123-19.09-1.821\n" + 
"  c-8.197-0.902-16.423-1.712-24.613-2.781c-5.534-0.722-11.162-1.225-16.739-1.823c-5.861-0.631-11.759-1.022-17.578-2.167\n" + 
"  c-2.655-0.522-5.449-0.437-8.188-0.75c-8.846-1.013-17.698-1.924-26.519-3.171c-5.554-0.786-11.189-0.973-16.764-1.633\n" + 
"  c-7.814-0.928-15.595-2.138-23.412-3.036c-2.847-0.327-5.725-0.423-8.588-0.699c-8.938-0.864-17.943-1.748-26.899-1.554\n" + 
"  c-15.911,0.347-31.625,2.248-47.251,5.876c-15.535,3.606-31.159,6.96-46.725,10.469c-14.081,3.174-28.128,6.604-42.203,9.777\n" + 
"  c-16.611,3.743-33.229,7.521-49.856,11.208c-9.739,2.16-19.507,4.166-29.301,5.996c-5.747,1.073-11.674,1.388-17.537,1.594\n" + 
"  c-7.402,0.261-14.82,0.113-22.231,0.052c-15.293-0.127-30.188-2.85-44.857-6.989c-21.598-6.095-40.763-16.693-58.57-30.253\n" + 
"  c-27.463-20.914-55.199-41.478-83.072-61.846c-15.905-11.621-34.119-18.604-53.016-23.727c-9.574-2.596-19.33-4.402-29.226-5.603\n" + 
"  c-11.292-1.37-22.576-2.289-33.947-2.248c-11.527,0.041-22.741,2.374-33.923,4.73c-11.105,2.342-22.015,5.769-33.198,7.525\n" + 
"  c-9.26,1.455-18.411,3.436-27.664,4.864"); 

     Path path = handler.getPath(); 
     root.getChildren().add(path); 

     // Moving image 
     _circle = new Circle(20); 
     _circle.setFill(Color.RED); 

     root.getChildren().add(_circle); 

     // Path Transition 
     _pathTransition = new PathTransition(); 
     _pathTransition.setDuration(Duration.seconds(ANIMATION_DURATION)); 
     _pathTransition.setPath(path); 
     _pathTransition.setNode(_circle); 
     _pathTransition.setOrientation(PathTransition.OrientationType.ORTHOGONAL_TO_TANGENT); 
     _pathTransition.setCycleCount(1); 
     _pathTransition.playFromStart(); 
     _pathTransition.pause(); 
     _pathTransition.jumpTo("end"); 

     // Save the circle positions on the path 
     _pathPointList = new ArrayList<>(); 
     savePositions(); 


     // Mouse presssed handler 
     _circle.setOnMousePressed(new EventHandler<MouseEvent>() { 
      public void handle(MouseEvent me) { 
       // Store initial position 
       _initX = _circle.getTranslateX(); 
       _initY = _circle.getTranslateY(); 
       _dragAnchor = new Point2D(me.getSceneX(), me.getSceneY());     
      } 
     }); 


     // Mouse dragged handler 
     _circle.setOnMouseDragged(new EventHandler<MouseEvent>() { 
      public void handle(MouseEvent me) { 
       double dragX = me.getSceneX() - _dragAnchor.getX(); 
       double dragY = me.getSceneY() - _dragAnchor.getY(); 

       // Calculate new position of the circle 
       double newXPosition = _initX + dragX; 
       double newYPosition = _initY + dragY; 

       // Get the nearest index (= second) of the animation 
       _actIndex = getAnimationIndex(newXPosition, newYPosition); 

       // Show animation at the given second 
       _pathTransition.jumpTo(Duration.seconds(_actIndex));         

       // Get slider index 
       _sliderIndex = remap(_actIndex, ANIMATION_DURATION, 0, SLIDER_MIN, SLIDER_MAX); 
       System.out.println(_sliderIndex); 
      } 
     }); 

     primaryStage.setTitle("JavaFX SVG Path Slider"); 
     primaryStage.setScene(new Scene(root, WIDTH, HEIGHT)); 
     primaryStage.show(); 
    } 

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


    /** 
    * Save the position of the circle for every second of the animation in 
    * a list. 
    */ 
    private void savePositions() { 

     if (_pathPointList == null) 
      return; 

     for (int i=0; i<=(int)ANIMATION_DURATION; i++) { 
      _pathTransition.jumpTo(Duration.seconds(i)); 

      _pathPointList.add(new Point2D(_circle.getTranslateX(), _circle.getTranslateY())); 
     } 

    } 

    /** 
    * Returns the index 
    * @param mousePosX 
    * @param mousePosY 
    * @return 
    */ 
    private int getAnimationIndex(double mousePosX, double mousePosY) { 

     int nearestIndex = 0; 

     int i = 0; 
     double dx; 
     double dy; 
     double old_dist = Double.MAX_VALUE; 
     double act_dist; 

     for (Point2D pathPos : _pathPointList) { 

      // Get distance between mouse position and saved position on path 
      // with pythagoras 
      dx = mousePosX - pathPos.getX(); 
      dy = mousePosY - pathPos.getY(); 
      act_dist = Math.sqrt(dx * dx + dy * dy); 

      if (act_dist < old_dist) { 
       old_dist = act_dist; 
       nearestIndex = i; 
      } 

      i++;    
     } 

     return nearestIndex; 
    } 

    /** 
    * Remaps the given value from one to anoter range 
    * 
    * @param value 
    * @param from1 
    * @param to1 
    * @param from2 
    * @param to2 
    * @return 
    */ 
    private int remap (int value, double from1, double to1, double from2, double to2) { 
     double tmp = (value - from1)/(to1 - from1) * (to2 - from2) + from2;   
     return (int)tmp; 
    } 
} 

JavaFXPathElementHandler.java (Автор: j. Hameister)

package svgpathslider; 

import javafx.scene.shape.ArcTo; 
import javafx.scene.shape.ClosePath; 
import javafx.scene.shape.CubicCurveTo; 
import javafx.scene.shape.LineTo; 
import javafx.scene.shape.MoveTo; 
import javafx.scene.shape.Path; 

import org.apache.batik.parser.ParseException; 
import org.apache.batik.parser.PathHandler; 

/** 
* 
* @see PathHandler Interface 
* 
* http://www.w3.org/TR/SVG/paths.html 
* 
* @author j.hameister 
* 
*/ 
public class JavaFXPathElementHandler implements PathHandler { 

    private Path path = new Path(); 

    private float lastX = 0; 
    private float lastY = 0; 

    public JavaFXPathElementHandler(String pathStyling) { 
     path.getStyleClass().add(pathStyling); 
    } 

    @Override 
    public void arcAbs(float rx, float ry, float xAxisRotation, boolean largeArcFlag, boolean sweepFlag, float x, float y) throws ParseException { 
     lastX = x; 
     lastY = y; 
     path.getElements().add(new ArcTo(rx, ry, xAxisRotation, x, y, largeArcFlag, sweepFlag)); 
    } 

    @Override 
    public void arcRel(float rx, float ry, float xAxisRotation, boolean largeArcFlag, boolean sweepFlag, float x, float y) throws ParseException { 
     lastX = x; 
     lastY = y; 

     path.getElements().add(new ArcTo(rx, ry, xAxisRotation, x, y, largeArcFlag, sweepFlag)); 
    } 

    @Override 
    public void closePath() throws ParseException { 
     path.getElements().add(new ClosePath()); 
    } 

    @Override 
    public void curvetoCubicAbs(float x1, float y1, float x2, float y2, float x, float y) throws ParseException { 
     lastX = x; 
     lastY = y; 

     CubicCurveTo c = new CubicCurveTo(x1,y1,x2,y2,x,y); 
     c.setAbsolute(true); 
     path.getElements().add(c); 
    } 

    @Override 
    public void curvetoCubicRel(float x1, float y1, float x2, float y2, float x, float y) throws ParseException { 
     lastX = x; 
     lastY = y; 
     CubicCurveTo c = new CubicCurveTo(x1,y1,x2,y2,x,y); 
     c.setAbsolute(false); 
     path.getElements().add(c); 
    } 

    @Override 
    public void curvetoCubicSmoothAbs(float x2, float y2, float x, float y) throws ParseException { 
     throw new ParseException("curvetoCubicSmoothAbs is currently not supported.", null); 
    } 

    @Override 
    public void curvetoCubicSmoothRel(float x2, float y2, float x, float y) throws ParseException { 
     throw new ParseException("curvetoCubicSmoothRel is currently not supported.", null); 
    } 

    @Override 
    public void curvetoQuadraticAbs(float x1, float y1, float x, float y) throws ParseException { 
     throw new ParseException("curvetoQuadraticAbs is currently not supported.", null); 
    } 

    @Override 
    public void curvetoQuadraticRel(float x1, float y1, float x, float y) throws ParseException { 
     throw new ParseException("curvetoQuadraticRel is currently not supported.", null); 
    } 

    @Override 
    public void curvetoQuadraticSmoothAbs(float x, float y) throws ParseException { 
     throw new ParseException("curvetoQuadraticSmoothAbs is currently not supported.", null); 
    } 

    @Override 
    public void curvetoQuadraticSmoothRel(float x, float y) throws ParseException { 
     throw new ParseException("curvetoQuadraticSmoothRel is currently not supported.", null); 
    } 

    @Override 
    public void endPath() throws ParseException {} 

    @Override 
    public void linetoAbs(float x, float y) throws ParseException { 
     lastX = x; 
     lastY = y; 

     LineTo lineTo = new LineTo(x,y); 
     lineTo.setAbsolute(true); 
     path.getElements().add(lineTo); 
    } 

    @Override 
    public void linetoHorizontalAbs(float x) throws ParseException { 
     lastX = x; 
     LineTo lineTo = new LineTo(x, lastX); 
     lineTo.setAbsolute(true); 
     path.getElements().add(lineTo); 
    } 

    @Override 
    public void linetoHorizontalRel(float x) throws ParseException { 
     lastX = x; 
     LineTo lineTo = new LineTo(x, lastY); 
     lineTo.setAbsolute(false); 
     path.getElements().add(lineTo); 
    } 

    @Override 
    public void linetoRel(float x, float y) throws ParseException { 
     lastX = x; 
     lastY = y; 
     LineTo lineTo = new LineTo(x, y); 
     lineTo.setAbsolute(false); 
     path.getElements().add(lineTo); 
    } 

    @Override 
    public void linetoVerticalAbs(float y) throws ParseException { 
     lastY = y; 
     LineTo lineTo = new LineTo(lastX, y); 
     lineTo.setAbsolute(true); 
     path.getElements().add(lineTo); 
    } 

    @Override 
    public void linetoVerticalRel(float y) throws ParseException { 
     lastY = y; 
     LineTo lineTo = new LineTo(lastX, y); 
     lineTo.setAbsolute(false); 
     path.getElements().add(lineTo); 
    } 

    @Override 
    public void movetoAbs(float x, float y) throws ParseException { 
     lastX = x; 
     lastY = y; 
     MoveTo moveTo = new MoveTo(x, y); 
     moveTo.setAbsolute(true); 
     path.getElements().add(moveTo); 
    } 

    @Override 
    public void movetoRel(float x, float y) throws ParseException { 
     lastX = x; 
     lastY = y; 
     MoveTo moveTo = new MoveTo(x, y); 
     moveTo.setAbsolute(false); 
     path.getElements().add(moveTo); 
    } 

    @Override 
    public void startPath() throws ParseException { 
    } 

    public Path getPath() { 
     return path; 
    } 

} 
Смежные вопросы