简体   繁体   English

移至鼠标单击SVG路径(d3.js)

[英]Move to Mouse Click in SVG path (d3.js)

I need to create a circle and move it to the closest point in an SVG path on mousedown event 我需要创建一个圆,并在mousedown事件中将其移动到SVG路径中的最近点

here is the jsfiddle 这是jsfiddle

The Code: 编码:

        var points = [[180,300],[234,335],[288,310],[350,290],[405,300],[430,305],[475,310],[513,300],[550,280]];

        var width = 1000, height = 600;
        var line = d3.svg.line().interpolate("cardinal");
        var svg = d3.select("#Con").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("cx", -10).attr("cy", -10).attr("r", 3.5);
        svg.append("rect").attr("width", width).attr("height", height).on("mousedown", mouseclick);
        function mouseclick() {
            var m = d3.mouse(this),p = closestPoint(path.node(), m);
            circle.attr("cx", p[0]).attr("cy", p[1]);
        }
        function closestPoint(pathNode, point) {
            var pathLength = pathNode.getTotalLength(),precision = 8,best,bestLength,bestDistance = Infinity;
            for (var scan, scanLength = 0, scanDistance; scanLength <= pathLength; scanLength += precision) {
                if ((scanDistance = distance2(scan = pathNode.getPointAtLength(scanLength))) < bestDistance) {
                    best = scan, bestLength = scanLength, bestDistance = scanDistance;
                }
            }
            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 = p.x - point[0],dy = p.y - point[1];
                return dx * dx + dy * dy;
            }
        }

I need to move the circle to the closest point in the path when I click in the SVG space 在SVG空间中单击时,我需要将圆移动到路径中最接近的点

In my code the circle moves without animation and I need to animate it so that it moves on the path from point to point 在我的代码中,圆没有动画移动,我需要对其进行动画处理,以便它在点到点的路径上移动

I need it to always move with one speed whether it moves a large or small distance 无论长距离还是短距离,我都需要始终保持一个速度移动

like this: 像这样:

https://a.top4top.net/p_11885szd41.gif https://a.top4top.net/p_11885szd41.gif

I'm not using your code but I hope you'll get the idea. 我没有使用您的代码,但希望您能理解。

Instead of using a circle I'm using the track: 我没有使用圆圈,而是使用轨道:

<use id="theUse" xlink:href="#track"

This track has a stroke-dasharray of ".1 398.80" This means a dash of .1 (very very small) and a gap of 398.80 as long as the track. 该轨迹的stroke-dasharray".1 398.80"这意味着虚线为.1 (非常小),并且与该轨迹的间距为398.80 The stroke-width is 7 with stroke-linecap= "round" and this is transforming the dash into a circle. stroke-width为7, stroke-linecap= "round" ,这将短划线转换为圆形。 I'm changing the position of the dash (the circle) using stroke-dashoffset and in order to animate the change I'm using transition: stroke-dashoffset 1s; 我正在使用stroke-dashoffset更改破折号(圆圈)的位置,并且为了使更改具有动画效果,我正在使用transition: stroke-dashoffset 1s; in the css. 在CSS中。

I hope it helps. 希望对您有所帮助。

 let m; let L = track.getTotalLength(); let _start = {x:180,y:30} let _end = {x:550,y:280} let l = dist(_start, _end); theUse.setAttributeNS(null,"stroke-dashoffset",L); svg.addEventListener("click",(e)=>{ m = oMousePosSVG(e) let pos = mx - _start.x; let theDistance = map(pos,_start.x,_end.x,0,L) let s_dof = constrain(L-theDistance, .5, L-.5) theUse.setAttributeNS(null,"stroke-dashoffset",s_dof) }) function oMousePosSVG(e) { var p = svg.createSVGPoint(); px = e.clientX; py = e.clientY; var ctm = svg.getScreenCTM().inverse(); var p = p.matrixTransform(ctm); return p; } function dist(p1, p2) { let dx = p2.x - p1.x; let dy = p2.y - p1.y; return Math.sqrt(dx * dx + dy * dy); } function map(n, a, b, _a, _b) { let d = b - a; let _d = _b - _a; let u = _d / d; return _a + n * u; } function constrain(n, low, high) { return Math.max(Math.min(n, high), low); }; 
 svg { border: 1px solid; } path { fill: none; } #theUse { transition: stroke-dashoffset 1s; } 
 <svg id="svg" viewBox="150 250 450 100"> <defs> <path id="track" d="M180,300Q223.2,334,234,335C250.2,336.5,270.6,316.75,288,310S332.45,291.5,350,290S393,297.75,405,300S419.5,303.5,430,305S462.55,310.75,475,310S501.75,304.5,513,300Q520.5,297,550,280"></path> </defs> <use xlink:href="#track" stroke="black" /> <use id="theUse" xlink:href="#track" stroke-width="7" stroke-dasharray = ".1 398.80" stroke="red" stroke-linecap= "round" /> </svg> 

This answer modifies your code to move a circle from the start of the line or from the last mouse click to the current mouse click. 此答案修改您的代码,以将直线从行的开头或最后单击的鼠标移到当前的鼠标单击。 It animates the movement of the circle by calling an animate method with setTimeout until the circle has moved from the beginning of the line to the point where the mouse was clicked. 它通过使用setTimeout调用动画方法来动画化圆的运动,直到圆从行的开始移动到单击鼠标的点为止。

The interesting code is here: 有趣的代码在这里:

        // getAnimate returns a function that is within a closure
       function getAnimate(pLength, path, currentIndex, finishPos, forward){
              let animate = function (){
                let scan = path.node().getPointAtLength(currentIndex);
                if (scan.x < finishPos || !forward && scan.x > finishPos){
                  circle.attr("cx", scan.x).attr("cy", scan.y);
                }
              if (forward){
                currentIndex += 1;
                lastIndex = currentIndex;
                if (scan.x < finishPos){
                  setTimeout(animate, 50);
                  }
                } else {
                    currentIndex -= 1;
                    lastIndex = currentIndex;
                if (scan.x > finishPos){
                  setTimeout(animate, 50);
                }
              }
            }
             return animate;
           }

  var points = [[80,100],[134,115],[188,130],[250,120],[305,120],[330,101],[375,103],[413,100],[550,90]]; var width = 500, height = 200; var line = d3.svg.line().interpolate("cardinal"); var svg = d3.select("#Con").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("cx", -10).attr("cy", -10).attr("r", 3.5); svg.append("rect").attr("width", width).attr("height", height).on("mousedown", mouseclick); var lastIndex = 0; function mouseclick() { let m = d3.mouse(this); let p = closestPoint(path.node(), m); let forward = true; let currentPoint = path.node().getPointAtLength(lastIndex); if (p[0] < currentPoint.x){ forward = false; } let pathLength = path.node().getTotalLength(); getAnimate(pathLength, path, lastIndex, p[0], forward)(); } function getAnimate(pLength, path, currentIndex, finishPos, forward){ let animate = function (){ let scan = path.node().getPointAtLength(currentIndex); if (scan.x < finishPos || !forward && scan.x > finishPos){ circle.attr("cx", scan.x).attr("cy", scan.y); } if (forward){ currentIndex += 1; lastIndex = currentIndex; if (scan.x < finishPos){ setTimeout(animate, 50); } } else { currentIndex -= 1; lastIndex = currentIndex; if (scan.x > finishPos){ setTimeout(animate, 50); } } } return animate; } function closestPoint(pathNode, point) { var pathLength = pathNode.getTotalLength(),precision = 8,best,bestLength,bestDistance = Infinity; for (var scan, scanLength = 0, scanDistance; scanLength <= pathLength; scanLength += precision) { if ((scanDistance = distance2(scan = pathNode.getPointAtLength(scanLength))) < bestDistance) { best = scan, bestLength = scanLength, bestDistance = scanDistance; } } 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; } } 
  * { margin: 0; padding: 0; } #Con { border: 1px solid black; margin: auto; width: 1000px; height: 600px; } #Map{ z-index: -1000; position: absolute; } #Cha { position: absolute; } path { z-index: 1000; fill: none; stroke: #000; stroke-width: 1.5px; } line { fill: none; stroke: red; stroke-width: 1.5px; } circle { fill: red; } rect { fill: none; pointer-events: all; } 
  <script type="text/javascript" src="http://mbostock.github.com/d3/d3.js?2.5.0"></script> <div id="Con"></div> 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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