首页 > 解决方案 > 一个接一个的过渡圈

问题描述

我有以下html结构。

<div id = 'divTest'>
  <div id = 'divSVG'>
  </div>
  <button id ='bStep' type="button">Step</button> 
  <button id = 'bRun'  type="button">Entire run</button> 
 </div>

使用 D3,我在一个假想的正方形内创建了 10 个随机定位的圆,其左上角位于 (5, 5) 且长边为 10。

 const n = 10; // number of circles
 //Creating random circles inside a square whose l = 10
 //The top left corner of the square is at (5, 5)
 const x_ini = 5;
 const y_ini = 5;
 const x_length = 10;
 const y_length = 10
 const dataset = [];

 for(let i = 0; i < n; i ++) {
   const randomNumberX = Math.random()
   const x = randomNumberX * x_length + x_ini;
   const randomNumberY = Math.random()
   const y = randomNumberY * y_length + y_ini;
   const pair = [x, y];
   dataset[i] = pair;
 }

 const svg = d3.select('#divSVG')
               .append('svg')
               .attr('width', 300)
               .attr('height', 300);

 const circles = svg.selectAll('.circleTest')
                    .data(dataset)
                    .enter()
                    .append('circle')
                    .attr('cx', d => d[0])
                    .attr('cy', d => d[1])
                    .attr('class', 'circleTest')
                    .attr('r', 1)
                    .attr('fill', 'black');

在 step 按钮中,我添加了一个函数,可以将一个圆圈移动到另一个虚构的圆圈,并更改它的类。

//Moves one circle to an imaginary square whose top left corner is at (100, 5)
 d3.select('#bStep')
   .on('click', () => {
     const x_ini = 100;
     const y_ini = 5;
     const x_length = 10;
     const y_length = 10;
     const randomNumberX = Math.random()
     const x = randomNumberX * x_length + x_ini;
     const randomNumberY = Math.random()
     const y = randomNumberY * y_length + y_ini;
     const circle = d3.select('.circleTest')
                      .transition()
                      .duration(1000)
                      .attr('cx', x)
                      .attr('cy', y)
                      .attr('fill', 'red')
                      .attr('class', 'circleTest2')

});

我希望通过单击“整个运行”按钮,所有圆圈不会同时移动,而是根据一些输入数据一一转换。例如,基于“指令”向量,在第一次转换中只移动一个圆圈,然后移动三个圆圈,依此类推。我怎样才能做到这一点?

d3.select('#bRun')
  .on('click', () => {
    const instructions = [1, 3, 0, 2, 4, 1]
    // Move all circles based on some input like 'instructions'
  });

这是工作示例: https ://jsfiddle.net/jandraor/91nwpb7a/42/

标签: javascripthtmld3.js

解决方案


这是一种基于不同数组执行这种转换的方法。

const instructions = [1, 3, 0, 2, 4, 1];
var pointer = 0;

function moveCircles () {
    if(!instructions[pointer]) {
    console.log('No circles to be transitioned');
    setTimeout(function () { moveCircles(); }, 1000);
    return ;
  }
     // Move all circles based on some input like 'instructions'
   if(pointer > instructions.length-1) {
    console.log("No more instructions.");
    return ;
   }
   if(!d3.selectAll('circle.circleTest:not(.transitioned)').size()) {
    console.log('No more circles to be transitioned');
    return ;
   }

   // transition circles
   var circles = d3.selectAll('circle.circleTest:not(.transitioned)')
      .filter(function (d, i) {
           return i < instructions[pointer];
      });

   circles.transition()
     .duration(1000).on('end', function (d, i) {
     if(i === circles.size()-1) {
        pointer++;
        moveCircles();
      }
     }).attr('cx', x)
     .attr('cy', y)
     .attr('fill', 'red')
     .attr('class', 'transitioned');
}    

d3.select('#bRun')
 .on('click', () => {
        moveCircles();
 });

我正在分配一个过渡到已经过渡的圈子的类,您可以通过您选择的任何方式重置它。

解释:

  1. 指向指令数组的指针。
  2. 单击时,检查指针是否超出指令数组的范围,或者是否没有更多的圆圈要转换,或者指令[指针] === 0。
  3. 选择所有没有transitioned类的圈子,根据它们过滤instructions[pointer]并转换它们。
  4. 在每个场景中增加指针。
  5. #2 和 #3 中的递归调用逻辑。

这是一个工作片段:

              
const n = 10; // number of circles
//Creating random circles inside a square whose l = 10
//The top left corner of the square is at (5, 5)
const x_ini = 5;
const y_ini = 5;
const x_length = 10;
const y_length = 10
const dataset = [];
for(let i = 0; i < n; i ++) {
  const randomNumberX = Math.random()
  const x = randomNumberX * x_length + x_ini;
  const randomNumberY = Math.random()
  const y = randomNumberY * y_length + y_ini;
  const pair = [x, y];
  dataset[i] = pair;
}

const svg = d3.select('#divSVG')
              .append('svg')
              .attr('width', 300)
              .attr('height', 300);
              
const circles = svg.selectAll('.circleTest')
   .data(dataset)
   .enter()
   .append('circle')
   .attr('cx', d => d[0])
   .attr('cy', d => d[1])
   .attr('class', 'circleTest')
   .attr('r', 1)
   .attr('fill', 'black');
 
 		// transition co-ordinates computation
    const x_ini_to = 100;
    const y_ini_to = 5;
    const randomNumberX = Math.random()
    const x = randomNumberX * x_length + x_ini_to;
    const randomNumberY = Math.random()
    const y = randomNumberY * y_length + y_ini_to;
     
 //Moves one circle to an imaginary square whose top left corner is at (100, 5)
 d3.select('#bStep')
   .on('click', () => {
     const circle = d3.select('.circleTest')
                      .transition()
                        .duration(1000)
                        .attr('cx', x)
                        .attr('cy', y)
                        .attr('fill', 'red')
                        .attr('class', 'circleTest2')
   
   });
      
	const instructions = [1, 3, 0, 2, 4, 1];
	var pointer = 0;
  
  function moveCircles () {
  		if(!instructions[pointer]) {
      	pointer++;
        console.log('No circles to be transitioned');
        setTimeout(function () { moveCircles(); }, 1000);
        return ;
      }
         // Move all circles based on some input like 'instructions'
       if(pointer > instructions.length-1) {
       	console.log("No more instructions.");
        return ;
       }
       if(!d3.selectAll('circle.circleTest:not(.transitioned)').size()) {
       	console.log('No more circles to be transitioned');
        return ;
       }
       
       // transition circles
       var circles = d3.selectAll('circle.circleTest:not(.transitioned)').filter(function (d, i) {
       		return i < instructions[pointer];
       });
       
       circles.transition()
         .duration(1000).on('end', function (d, i) {
         if(i === circles.size()-1) {
            pointer++;
            moveCircles();
          }
         }).attr('cx', x)
         .attr('cy', y)
         .attr('fill', 'red')
         .attr('class', 'transitioned');
  }    
  
   d3.select('#bRun')
     .on('click', () => {
     		moveCircles();
     });
<script src="https://d3js.org/d3.v4.min.js"></script>

<div id = 'divTest'>
  <div id = 'divSVG'>
  
  </div>
 <button id ='bStep' type="button">Step</button> 
<button id = 'bRun'  type="button">Entire run</button> 
</div>

JSFIDDLE:https ://jsfiddle.net/shashank2104/91nwpb7a/71/

编辑:我认为每次点击都会转换圆圈但整个运行没有意义,这很糟糕。不管怎样,现在修好了。希望这足够清楚。如果没有,请告诉我。是的,请相应地匹配Step按钮单击以进行正确同步。


推荐阅读