简体   繁体   中英

Drag along path in d3

Using d3 and React I have drawn a path. On this path I have multiple circles which are draggable only along that path. However, my current implementation only (sort of) works when there is one circle on that path.

(On dragStart it moves to length 0 on the path regardless of position, and whenever I'm dragging a second circle it starts of the the previous circle's position).

My question is: How can I drag multiple circles (or we) along a path in d3? Is there a way to get the currentLength position on the path based on cx and cy of the current circle?

 var currentLength = 0; class MyComponent extends Component { constructor(props) { super(props) currentLength = 0; } componentDidMount() { var drag = d3.behavior.drag() .on('drag', this.move); var g = d3.select(this._base); var circle = g.selectAll('circle').data(this.props.data); var onEnter = circle.enter(); onEnter.append('circle') .attr({ r: 10, cx: (d) => dx, cy: (d) => dy }) .style('fill', 'blue') .call(drag); } move(d) { currentLength += d3.event.dx + d3.event.dy if (currentLength < 0) { currentLength = 0 } var pointAtCurrentLength = d3.select('#path').node().getPointAtLength(currentLength) this.cx.baseVal.value = pointAtCurrentLength.x; this.cy.baseVal.value = pointAtCurrentLength.y; } render() { return <g ref={(c)=>this._base=c}></g> } } 

Something similar to this, only draggable and multiple circles: http://bl.ocks.org/mbostock/1705868

Here's a quick modification to this example , which makes the circles draggable:

 <!DOCTYPE html> <meta charset="utf-8"> <style> path { fill: none; stroke: #000; stroke-width: 1.5px; } line { fill: none; stroke: red; stroke-width: 1.5px; } circle { fill: red; } </style> <body> <script src="//d3js.org/d3.v3.min.js"></script> <script> var points = [[600,276],[586,393],[378,388],[589,148],[346,227],[365,108]]; var width = 960, height = 500; var line = d3.svg.line() .interpolate("cardinal"); var drag = d3.behavior.drag() .on("drag", dragged); var svg = d3.select("body").append("svg") .attr("width", width) .attr("height", height); var path = svg.append("path") .datum(points) .attr("d", line); var line = svg.append("line"); var circle = svg.append("circle") .attr("transform", "translate(" + points[0] + ")") .attr("r", 7) .call(drag); svg.append("circle") .attr("transform", "translate(" + points[5] + ")") .attr("r", 7) .call(drag); function dragged(d) { var m = d3.mouse(svg.node()), p = closestPoint(path.node(), m); d3.select(this) .attr("transform", "translate(" + p[0] + "," + p[1] + ")") } function closestPoint(pathNode, point) { var pathLength = pathNode.getTotalLength(), precision = 8, best, bestLength, bestDistance = Infinity; // linear scan for coarse approximation for (var scan, scanLength = 0, scanDistance; scanLength <= pathLength; scanLength += precision) { if ((scanDistance = distance2(scan = pathNode.getPointAtLength(scanLength))) < bestDistance) { best = scan, bestLength = scanLength, bestDistance = scanDistance; } } // binary search for precise estimate precision /= 2; while (precision > 0.5) { var before, after, beforeLength, afterLength, beforeDistance, afterDistance; if ((beforeLength = bestLength - precision) >= 0 && (beforeDistance = distance2(before = pathNode.getPointAtLength(beforeLength))) < bestDistance) { best = before, bestLength = beforeLength, bestDistance = beforeDistance; } else if ((afterLength = bestLength + precision) <= pathLength && (afterDistance = distance2(after = pathNode.getPointAtLength(afterLength))) < bestDistance) { best = after, bestLength = afterLength, bestDistance = afterDistance; } else { precision /= 2; } } best = [best.x, best.y]; best.distance = Math.sqrt(bestDistance); return best; function distance2(p) { var dx = px - point[0], dy = py - point[1]; return dx * dx + dy * dy; } } </script> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM