Make multiple line with animation from the last position to the new positions

╄→гoц情女王★ 提交于 2019-12-31 07:37:13

问题


In my real problem I'm consuming a web service that returns an array with points in x and y. this is the structure of the array that I can receive.

  var dataSet=[
    [
      { "voltaje":  10, "corriente": Math.random() * 130 + 10},
      { "voltaje":  40, "corriente": Math.random() * 130 + 10},
      { "voltaje": 70, "corriente": Math.random() * 130 + 10}
    ],
     [
      { "voltaje": 100, "corriente": Math.random() * 130 + 10},
      { "voltaje":  130, "corriente": Math.random() * 130 + 10},
      { "voltaje":  160, "corriente": Math.random() * 130 + 10},
      { "voltaje":  190, "corriente": Math.random() * 130 + 10}
    ]
  ];

I currently have this code that works for a structure like this:

var dataset=  [{
  x: d * 30 + 10,
  y: Math.random() * 130 + 10
},
{
  x: d * 30 + 10,
  y: Math.random() * 130 + 10
}
]

and in this code I get what I want. some lines are painted with an animation. and it is simulated as if the web service returns some points, then these new points are added and the animation of the line continues from the last point to the new point.

I want to have the points and the animation of each array separately, for example:

I would like to adapt my new structure to this code and make it separated to multiple lines, but I have not been able to. How can I do it?

http://jsfiddle.net/3ok8uuj3/

  var svg = d3.select('svg');

  var backLayer = svg.append("g");
  var frontLayer = svg.append("g");

  var dataSet = d3.range(10).map(function(d) {
    return {
      x: d * 30 + 10,
      y: Math.random() * 130 + 10
    }
  });

  var lineGenerator = d3.svg.line()
    .x(function(d) {
      return d.x
    })
    .y(function(d) {
      return d.y
    })
    .interpolate("monotone")

  function displayLine(data) {
    var line = backLayer.selectAll(null)
      .data(data)
      .enter()
      .append("path")
      .attr({
        d: lineGenerator(data),
        fill: 'none',
        stroke: "red",
        "stroke-width": "3px"
      });

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

    line.attr("stroke-dasharray", totalLength + " " + totalLength)
      .attr("stroke-dashoffset", totalLength)
      .transition()
      .duration(2000)
      .ease("linear")
      .attr("stroke-dashoffset", 0);
  }

  displayLine(dataSet)

  setTimeout(function() {
   setInterval(function() {
   var newData = { x: (dataSet.length) * 30 + 10, y: Math.random() * 130 + 10 };
   displayLine([dataSet[dataSet.length - 1], newData]);
   dataSet.push(newData);
  }, 2000);
  }, 2000);

回答1:


Is it something like this that your are looking for?

There is a variable that stores the last data points for each series (if it exists, or create a new entry in the lastData array if there is no last data point for this series) and starts the new paths from these values. So even if you add new series of data, this should take care of creating a new line automatically.

Note: this uses D3.v4 (and not D3.v3 as in your example), since v3 is not maintained anymore.

var svg = d3.select('svg');
var backLayer = svg.append("g");

//variable to store last data points the paths will have to start from
let lastData = []

//color scale
const colorScale = d3.scaleOrdinal()
	.domain([0, 1, 2])
  .range(["blue", "red", "green"])

//line generator
let lineGenerator = d3.line()
    .x(function(d) {
      return d.corriente
    })
    .y(function(d) {
      return d.voltaje
    })
    .curve(d3.curveBasis)



function extractLastPoint(data){
  let lastDataPoint = []
  data.forEach(d => {
    lastDataPoint.push(d[d.length-1])
  })
  return lastDataPoint
}


function displayLine(data) {

  //adjust lastData array if discrepency of size
  if (lastData.length<data.length) {
    let diffSize = data.length - lastData.length
    let firstPos = data.length - diffSize

    for (let i=firstPos; i<data.length; i++){
      lastData.push(data[i][0])
    }
  }

  //add a path for each series and animate it
  data.forEach((d, i) => {

    d.unshift(lastData[i])

    let line = backLayer.append("path")
      .datum(d)
      .attr("d", p => lineGenerator(p))
      .attr("class", `line${i}`)
      .attr("fill", "none")
      .attr("stroke", colorScale(i))
      .attr("stroke-width", "3px") 
  
    let totalLength = line.node().getTotalLength();
  
    line
      .attr("stroke-dasharray", totalLength + " " + totalLength)
      .attr("stroke-dashoffset", totalLength)
      .transition()
      .duration(2000)
      .ease(d3.easeLinear)
      .attr("stroke-dashoffset", 0);
  })

  //save new last points of data to start next path from it
  lastData = extractLastPoint(data)
}


//initial line
let dataSet=[
      [
        { "voltaje":  Math.random() * 200 + 10, "corriente": Math.random() * 550 + 10},
        { "voltaje":  Math.random() * 200 + 10, "corriente": Math.random() * 550 + 10},
        { "voltaje":  Math.random() * 200 + 10, "corriente": Math.random() * 550 + 10}
      ],
       [
        { "voltaje":  Math.random() * 200 + 10, "corriente": Math.random() * 550 + 10},
        { "voltaje":  Math.random() * 200 + 10, "corriente": Math.random() * 550 + 10},
        { "voltaje":  Math.random() * 200 + 10, "corriente": Math.random() * 550 + 10},
        { "voltaje":  Math.random() * 200 + 10, "corriente": Math.random() * 550 + 10}
      ]
    ]

  displayLine(dataSet)

//add new data every 3 sec
d3.interval(function() {
  let dataSet=[
      [
        { "voltaje":  Math.random() * 200 + 10, "corriente": Math.random() * 550 + 10},
        { "voltaje":  Math.random() * 200 + 10, "corriente": Math.random() * 550 + 10},
        { "voltaje":  Math.random() * 200 + 10, "corriente": Math.random() * 550 + 10}
      ],
       [
        { "voltaje":  Math.random() * 200 + 10, "corriente": Math.random() * 550 + 10},
        { "voltaje":  Math.random() * 200 + 10, "corriente": Math.random() * 550 + 10},
        { "voltaje":  Math.random() * 200 + 10, "corriente": Math.random() * 550 + 10},
        { "voltaje":  Math.random() * 200 + 10, "corriente": Math.random() * 550 + 10}
      ]
    ]

  displayLine(dataSet)

}, 3000)
* {
  margin: 0;
  padding: 0;
  border: 0;
}

body {
  background: #ffd;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="800" height="800"></svg>



回答2:


first the example without your data the data is

dataSet = [{x,y},{x,y}]
newData =  [last x and y, new data]

you want to change to

  var dataSet=[
    [
      { "voltaje":  10, "corriente": Math.random() * 130 + 10},
      { "voltaje":  40, "corriente": Math.random() * 130 + 10},
      { "voltaje": 70, "corriente": Math.random() * 130 + 10}
    ],
     [
      { "voltaje": 100, "corriente": Math.random() * 130 + 10},
      { "voltaje":  130, "corriente": Math.random() * 130 + 10},
      { "voltaje":  160, "corriente": Math.random() * 130 + 10},
      { "voltaje":  190, "corriente": Math.random() * 130 + 10}
    ]
  ];

newData =  [last voltaje and corriente, new data]

create new data holder like dataSet1, push all new variable there, when redraw set the parameter to point on your data, you can use index, then draw it

      var svg = d3.select('svg');

      var backLayer = svg.append("g");
      var frontLayer = svg.append("g");
  var dataSet=[
    [
      { "voltaje":  10, "corriente": Math.random() * 130 + 10},
      { "voltaje":  40, "corriente": Math.random() * 130 + 10},
      { "voltaje": 70, "corriente": 30}
    ],
     [
      { "voltaje": 70, "corriente": 30},
      { "voltaje":  130, "corriente": Math.random() * 130 + 10},
      { "voltaje":  160, "corriente": Math.random() * 130 + 10},
      { "voltaje":  190, "corriente": 30}
    ],
         [
      { "voltaje": 190, "corriente": 30},
      { "voltaje":  160, "corriente": Math.random() * 130 + 10},
      { "voltaje":  200, "corriente": 30}
    ],
    
             [
      { "voltaje": 200, "corriente":30},
      { "voltaje":  130, "corriente": Math.random() * 130 + 10},
      { "voltaje":  160, "corriente": Math.random() * 130 + 10},
      { "voltaje":  190, "corriente": Math.random() * 130 + 10}
    ]
  ];
  
  var dataSet1=[]
  console.log(dataSet[1][1])
  /*
      var dataSet = d3.range(1).map(function(d) {
        return {
          x: d * 30 + 10,
          y: Math.random() * 130 + 10
        }
      });
*/
      var lineGenerator = d3.svg.line()
        .x(function(d) {
          return d.voltaje
        })
        .y(function(d) {
          return d.corriente
        })
        .interpolate("monotone")

      function displayLine(data) {
        var line = backLayer.selectAll(null)
          .data(data)
          .enter()
          .append("path")
          .attr({
            d: lineGenerator(data),
            fill: 'none',
            stroke: "red",
            "stroke-width": "3px"
          });

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

        line.attr("stroke-dasharray", totalLength + " " + totalLength)
          .attr("stroke-dashoffset", totalLength)
          .transition()
          .duration(2000)
          .ease("linear")
          .attr("stroke-dashoffset", 0);
      }
		dataSet1.push(dataSet[0])
     displayLine(dataSet[0])
var i =0
var l = dataSet.length
console.log(i)
      setTimeout(function() {
  
       setInterval(function() {
      i++
       if (i<l){
       
       var newData = dataSet[i];
       }else{
       var newData = [{ 'voltaje': Math.random() * 30 + 10, 'corriente': Math.random() * 130 + 10 }]
       }
  console.log(i,newData,dataSet1,dataSet1[i-1] )
       newData.unshift(dataSet1[i-1][dataSet1[i-1].length - 1])
         displayLine(newData);;
       dataSet1.push(newData);
      }, 2000);
 
      }, 2000);
* {
  margin: 0;
  padding: 0;
  border: 0;
}

body {
  background: #ffd;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg width="800" height="800"></svg>



回答3:


maybe this, you must simple it i make it not simple cos i want you understand, it can make with single svg if you know the min max value of axis, but i doing on 3 svg i hope you can learn and make it what you want

var dataSet=[
    [
      { "voltaje":  10, "corriente": Math.random() * 130 + 10},
      { "voltaje":  40, "corriente": Math.random() * 130 + 10}
    ],
         [
      { "voltaje":  10, "corriente": Math.random() * 130 + 10},
      { "voltaje":  40, "corriente": Math.random() * 130 + 10},
      { "voltaje": 50, "corriente": 30}
    ],
            [
      { "voltaje":  10, "corriente": Math.random() * 130 + 10}
    ],
  ];
  
  dataSet.forEach(function(d,i){
     var svg = d3.select('#dataset'+(i+1));
      var backLayer = svg.append("g").attr('id','back'+i);
      var frontLayer = svg.append("g").attr('id','front'+i);
  })
  var dataSet1=[]
  var dataSet2=[]
  var dataSet3=[]
  console.log(dataSet[1][1])
  /*
      var dataSet = d3.range(1).map(function(d) {
        return {
          x: d * 30 + 10,
          y: Math.random() * 130 + 10
        }
      });
*/


      function displayLine(data,svg,i) {
      
      var lineGenerator = d3.svg.line()
        .x(function(d) {
          return d.voltaje
        })
        .y(function(d) {
          return d.corriente
        })
        .interpolate("monotone")
        var line = d3.select('#back'+i).selectAll(null)
          .data(data)
          .enter()
          .append("path")
          .attr({
            d: lineGenerator(data),
            fill: 'none',
            stroke: "red",
            "stroke-width": "3px"
          });

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

        line.attr("stroke-dasharray", totalLength + " " + totalLength)
          .attr("stroke-dashoffset", totalLength)
          .transition()
          .duration(2000)
          .ease("linear")
          .attr("stroke-dashoffset", 0);
      }
      
      
    dataSet.forEach(function(d,i){
      if (i==0){
      var data = dataSet1
      
      }else if(i==1){
      var data = dataSet2
      }else{
      var data = dataSet3
      }
       var svg = d3.select('#dataset'+(i+1));
    	data.push(dataSet[i])
      displayLine(dataSet[i],svg,i)
    })
	
var i1=(-1)
var i2=(-1)
var i3=(-1)
var t = 1
      setTimeout(function() {
 
       setInterval(function() {
       t++
   		dataSet.forEach(function(d,i){
    var newData = [{ 'voltaje': 10 + 50*t + 100, 'corriente': Math.random() * 130 + 10*1 }]
    	if (i==0){
      i1++
      var data = dataSet1
       newData.unshift(data[i1][data[i1].length - 1])
       console.log(i,data[i])
      }else if(i==1){
      i2++
      var data = dataSet2
       newData.unshift(data[i2][data[i2].length - 1])
       console.log(i,data[i])
      }else{
      i3++
      var data = dataSet3
       newData.unshift(data[i3][data[i3].length - 1])
       console.log(i,data[i])
      }
     
      
      var svg = d3.select('#dataset'+(i+1));
       displayLine(newData,svg,i);
       data.push(newData);
      })

      }, 2000);
 
      }, 2000);
* {
  margin: 0;
  padding: 0;
  border: 0;
}
svg{
display:block;
}
body {
  background: #ffd;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg width="500" height="200" id='dataset1'></svg>
<svg width="500" height="200" id='dataset2'></svg>
<svg width="500" height="200" id='dataset3'></svg>


来源:https://stackoverflow.com/questions/46410466/make-multiple-line-with-animation-from-the-last-position-to-the-new-positions

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!