【问题标题】:SVG path movement on mousedown event to mouse click pointmousedown 事件上的 SVG 路径移动到鼠标单击点
【发布时间】:2019-04-07 06:16:07
【问题描述】:

我需要创建一个圆并在 mousedown 事件时将其平滑地移动到 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;
                }
            }

我需要在动画完成之前让它移动,如果我点击不止一次,圆圈会向一个方向正确移动,但是当我在它移动的同时点击反方向时,它会失去控制并开始闪烁。

【问题讨论】:

    标签: javascript html d3.js


    【解决方案1】:

    最简单的解决方案(无需过多重构代码)是清除当前的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 版。您几乎找不到任何文档。

    【讨论】:

      猜你喜欢
      • 2019-08-25
      • 1970-01-01
      • 1970-01-01
      • 2014-10-10
      • 2013-07-30
      • 1970-01-01
      • 2013-09-09
      • 1970-01-01
      • 2021-10-27
      相关资源
      最近更新 更多