鼠标按下事件时SVG路径移动到鼠标单击点

时间:2019-04-06 04:09:34

标签: javascript html d3.js

我需要创建一个圆,并在发生鼠标按下事件时将其平滑地移动到SVG路径中的最近点

检查:http://jsfiddle.net/azvheobu/

代码:

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);
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){
            document.getElementById('test').innerHTML = 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, 3);
               }
            } else {
                currentIndex -= 1;
                lastIndex = currentIndex;
                if (scan.x > finishPos){
                    setTimeout(animate, 3);
                }
            }
       }
            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 = p.x - point[0],dy = p.y - point[1];
                    return dx * dx + dy * dy;
                }
            }

我需要使它甚至在动画结束之前就移动,如果单击多次,圆会沿一个方向正确移动,但是当我在移动时单击相反的方向时,它已经脱离了计数,开始闪烁。

1 个答案:

答案 0 :(得分:0)

最简单的解决方案(无需过多重构代码)是清除当前的setTimeout。例如:

let timer;

function getAnimate(pLength, path, currentIndex, finishPos, forward) {
  let animate = function() {
    clearTimeout(timer);
    //etc...
    if (forward) {
      if (scan.x < finishPos) {
        timer = setTimeout(animate, 50);
      }
    } else {
      if (scan.x > finishPos) {
        timer = setTimeout(animate, 50);
      }
    }
  }
  return animate;
}

这是您所做的更改的代码:

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);
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)();
}

let timer;

function getAnimate(pLength, path, currentIndex, finishPos, forward) {
  let animate = function() {
    clearTimeout(timer);
    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) {
        timer = setTimeout(animate, 50);
      }
    } else {
      currentIndex -= 1;
      lastIndex = currentIndex;
      if (scan.x > finishPos) {
        timer = 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 = p.x - point[0],
      dy = p.y - 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>

最后,只有一个提示:不要使用D3 v2,这不是2011年。您几乎找不到有关它的任何文档。