我想在JavaFX中沿给定的SVG路径拖动一个圆圈或窗格。它应该像滑块一样工作。因此,如果圆圈位于路径的开头,那么"滑块"应该返回0,如果用户拖动圆圈到最后,"滑块"应该返回100。
参见附图: Link to image
路径应该是具有开头和结尾的任何SVG路径。
我找到了这个例子,但我无法转换"它是JavaFX。
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。
答案 0 :(得分:2)
我从JörnHameister那里找到了这个非常有用的例子,他使用Apache Batik Library来解析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;
}
}
答案 1 :(得分:0)
您可以计算拖动动作在圆上产生的“力”(作为矢量),而不用获取列表中的最近点,并查看此力是否使圆最接近点中的下一个点或上一个点名单。因为仅计算最近的点会导致圆上的传送,具体取决于路径。但是,如果您不介意的话,就可以了。
(我没有50个声誉,只能添加评论,这就是为什么它是一个答案...对不起)