使用D3.js,如何检测到动画线上的某个点何时到达?

时间:2016-12-02 17:09:54

标签: javascript d3.js

使用D3我想检测我的动画线何时改变方向(或到达某个点),并在此时间点附加一个圆圈。因此,一个圆圈将一次追加而不是全部追加,这就是目前正在发生的事情。

var points = [
  [480, 200],
  [580, 400],
  [680, 100],
  [780, 300],
  [180, 300],
  [280, 100],
  [380, 400]
];

var path = svg.append("path")
    .data([points])
    .attr("d", d3.svg.line()
    .tension(0) ); //); // Catmull–Rom 
    //.interpolate("cardinal-closed"));

var totalLength = path.node().getTotalLength();

path
    .attr("stroke-dasharray", totalLength + " " + totalLength)
    .attr("stroke-dashoffset", totalLength)
    .transition()
    .duration(10000)
    .ease("linear")
    .attr("stroke-dashoffset", 0);


svg.selectAll(".point", function(d,i){
        console.log(d,i)
    })
    .data(points)
    .enter().append("circle")
    .transition()
    .duration(2000)
    .ease("linear")
    .style("fill", "url(#image)")
    .attr("r", 20)
    .attr("transform", function(d) { return "translate(" + d + ")"; })

FIDDLE

我尝试测量前一个x和y的前一个x和y,但是如果它们继续向正方向或负方向移动,则不会轻易注意到差异。我还需要测量坡度变化。我希望有一个更聪明的方法?

我试过测量一下,看看newX和oldX之间的区别是否保持不变,但它不起作用:( https://jsfiddle.net/hs5a9z5a/3/

2 个答案:

答案 0 :(得分:3)

看看下面的代码是基于你的小提琴。

slope是一个数组,它是x和y的向量。 它是通过减去p.x和最后一个x坐标并将其除以时差来计算的。如果它高于threshold,则会向控制台写入一条消息。

var points = [
  [480, 200],
  [580, 400],
  [680, 100],
  [780, 300],
  [180, 300],
  [280, 100],
  [380, 400]
];

var svg = d3.select("body").append("svg")
    .attr("width", 960)
    .attr("height", 500);


var path = svg.append("path")
	.data([points])
	.attr("d", d3.svg.line()
	.tension(0) ); //); // Catmull–Rom 

var totalLength = path.node().getTotalLength();

path
	.attr("stroke-dasharray", totalLength + " " + totalLength)
	.attr("stroke-dashoffset", totalLength)
	.transition()
	.duration(10000)
	.ease("linear")
	.attr("stroke-dashoffset", 0);

svg.selectAll(".point", function(d,i){})
	.data(points)
	.enter().append("circle")
	.transition()
	.duration(2000)
	.ease("linear")
	.style("fill", "url(#image)")
	.attr("r", 20)
	.attr("transform", function(d) { return "translate(" + d + ")"; })
    
var circle = svg.append("circle")
	.attr("r", 13)
	.attr("transform", "translate(" + points[0] + ")");

transition();

function transition() {
	circle.transition()
	.duration(10000)
	.attrTween("transform", translateAlong(path.node()))
	.each("end");
}

// Returns an attrTween for translating along the specified path element.
var newX = 0, diffX = 0, prevdiffX=0, oldX=0; 
var newY = 0;
var newT = 0;
function translateAlong(path) {
    var l = path.getTotalLength();
    var slope = [];
    slope.push('-1');
    slope.push('-1');
    var threshold = 0.1;



    return function(d, i, a) {
        return function(t) {

            var p = path.getPointAtLength(t * l);
      
            if ((1 - Math.abs(slope[0]/((p.x - newX)  / (t - newT) + 0.001))) > 0.1) {
                console.log('slopeX changed');
            } else if ((1 - Math.abs(slope[1]/((p.y - newY)  / (t - newT) + 0.001))) > 0.1) {
                console.log('slopeY changed');
            }
            slope = [];
            slope.push((p.x - newX)  / (t - newT) + 0.001);
            slope.push((p.y - newY) / (t - newT) + 0.001);
			newX = p.x; 
            newY = p.y;
            newT = t;
            return "translate(" + p.x + "," + p.y + ")";
            };
        };
    }
path {
	fill: none;
	stroke: #000;
	stroke-width: 3px;
}

circle {
	fill: url('http://www.e-pint.com/epint.jpg');

	fill: steelblue;
		/stroke: #fff;
		//stroke-width: 3px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg id="mySvg" width="80" height="80">
		<defs id="mdef">
		<pattern id="image" x="0" y="0" height="40" width="40">
		<image x="0" y="0" width="40" height="40" xlink:href="http://www.e-pint.com/epint.jpg"></image>

		</pattern>
		</defs>	
	</svg>
  <div id="line"></div>

答案 1 :(得分:3)

要知道线路何时更改方向(即命中数据点),您需要控制stroke-dashoffset上的过渡功能。我就是这样做的(我试着好好评论一下,如果你有任何问题,请告诉我):

path
  .attr("stroke-dasharray", totalLength + " " + totalLength)
  .attr("stroke-dashoffset", totalLength)
  .transition()
  .duration(10000)
  .ease("linear")
  .tween("myTween", function() {
    var i = d3.interpolate(totalLength, 0);
    var self = d3.select(this);
    return function(t) {
        // current position of stroke-dashoffset     
        var len = i(t),
        // current position with respect to line length of stroke-dashoffset
        per = (1 - len / totalLength) * totalLength,
        // pixel position of stroke-dashoffset
        p = self.node().getPointAtLength(per);

      // loop our points and look for hit detection
      points.forEach(function(d) {
        // are we one a point?
        if (Math.abs(p.x - d[0]) < 1 && Math.abs(p.y - d[1]) < 1) {
          console.log("point hit");
        }
      });

      // apply stroke-dashoffset
      self.attr("stroke-dashoffset", len);
    };
  });

运行样本:

var points = [
  [480, 200],
  [580, 400],
  [680, 100],
  [780, 300],
  [180, 300],
  [280, 100],
  [380, 400]
];

var svg = d3.select("body").append("svg")
  .attr("width", 960)
  .attr("height", 500);


var path = svg.append("path")
  .data([points])
  .attr("d", d3.svg.line()
    .tension(0)); //); // Catmull–Rom 
//.interpolate("cardinal-closed"));

var totalLength = path.node().getTotalLength();

path
  .attr("stroke-dasharray", totalLength + " " + totalLength)
  .attr("stroke-dashoffset", totalLength)
  .transition()
  .duration(10000)
  .ease("linear")
  .tween("myTween", function() {
    var i = d3.interpolate(totalLength, 0);
    var self = d3.select(this);
    return function(t) {
      var len = i(t),
        per = (1 - len / totalLength) * totalLength,
        p = self.node().getPointAtLength(per);

      points.forEach(function(d) {
        if (Math.abs(p.x - d[0]) < 1 && Math.abs(p.y - d[1]) < 1) {
          console.log("point hit");
        }
      });
      
      self.attr("stroke-dashoffset", i(t));
    };
  });




/*
svg.selectAll(".point", function(d,i){
		//console.log(d,i)
	})
	.data(points)
	.enter().append("circle")
	.transition()
	.duration(2000)
	.ease("linear")
	.style("fill", "url(#image)")
	.attr("r", 20)
	.attr("transform", function(d) { return "translate(" + d + ")"; })
    
var circle = svg.append("circle")
	.attr("r", 13)
	.attr("transform", "translate(" + points[0] + ")");

transition();

function transition() {
	circle.transition()
	.duration(10000)
	.attrTween("transform", translateAlong(path.node()))
	//.each("end", transition);
	.each("end");
}

// Returns an attrTween for translating along the specified path element.
function translateAlong(path) {
  var l = path.getTotalLength();
  return function(d, i, a) {
    return function(t) {
      var p = path.getPointAtLength(t * l);
      return "translate(" + p.x + "," + p.y + ")";
    };
  };
}
*/
path {
  fill: none;
  stroke: #000;
  stroke-width: 3px;
}

circle {
  fill: url('http://www.e-pint.com/epint.jpg');
  fill: steelblue;
  /stroke: #fff;
  //stroke-width: 3px;
}
<script src="http://d3js.org/d3.v3.js"></script>

<svg id="mySvg" width="80" height="80">
  <defs id="mdef">
    <pattern id="image" x="0" y="0" height="40" width="40">
      <image x="0" y="0" width="40" height="40" xlink:href="http://www.e-pint.com/epint.jpg"></image>

    </pattern>
  </defs>
</svg>
<div id="line"></div>