代码之家  ›  专栏  ›  技术社区  ›  tic

D3中代码链转换的一种简洁方法

  •  2
  • tic  · 技术社区  · 7 年前

    我必须应用两个长的链式转换序列,它们主要在某些元素的转换顺序上有所不同,我正在寻找一种紧凑的编码方式。

    orange -&燃气轮机; purple blue -&燃气轮机; yellow ;

    第二种颜色是:

    蓝色 黄色的 -&燃气轮机; 橙色 -&燃气轮机; 紫色 .

    我试过下面的代码(fiddle here ),但不起作用。实现这一点最简单的方法是什么?

        var svg = d3.select('svg');
    
        var dataSet = [20, 20];
    
        var group=svg.append("g");
        var circles = group.selectAll('circle')
        .data(dataSet)
        .enter()
        .append('circle')
        .attr("r",function(d){ return d })
        .attr("cx",function(d, i){ return i * 100 + 50 })
        .attr("cy",50)
        .attr("fill",'black');
    
        var t1 = d3
        .transition()
        .duration(1000)
        .attr("fill","orange")
        .transition()
        .duration(1000)
        .attr("fill","purple");
    
        var t2 = d3
        .transition()
        .duration(1000)
        .attr("fill","blue")
        .transition()
        .duration(1000)
        .attr("fill","yellow");
    
        group.select(":nth-child(1)")
        .transition(t1).transition(t2); 
        group.select(":nth-child(2)")
        .transition(t2).transition(t1); 
    
    2 回复  |  直到 7 年前
        1
  •  3
  •   Andrew Reid    7 年前

    实现这一点的方法有很多种。正如在另一个答案中所指出的,使用函数来实现这一点将使代码保持紧凑。就我个人而言,我倾向于使用transition的end事件来触发transition函数中的下一个transition。

    这类函数的一般形式如下:

    function transition() {
       // ....  optional logic here.
       d3.select(this) // do transition:
         .transition()
         .attr("fill", ... )
         .on("end", transition); // and repeat.
    }
    

    它可以用 selection.each(transition)

    管理循环中当前颜色/过渡的一种方法是使用自定义属性。下面我用 .attr("i") 要跟踪:

    var data = [
      ["orange","purple","blue","yellow"],
      ["blue","yellow","orange","purple"]
    ];
    
    var svg = d3.select("svg");
    var circles = svg.selectAll()
      .data(data)
      .enter()
      .append("circle")
      .attr("r", 20)
      .attr("cx", function(d,i) { return i * 50 + 50; })
      .attr("cy", 50)
      .attr("fill", function(d) { return d[0]; })
      .attr("i",0)
      .each(transition);
    
    
    // cycle endlessly:
    function transition() {
      var selection = d3.select(this);
      // keep track of current value:
      var i = selection.attr("i")
      selection
        .attr("i",i = ++i%4)
        .transition()  
        .duration(1000)
        .attr("fill", function(d) { return d[i] })
        .on("end", transition);
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
    <svg width="500" height="300"></svg>

    如果变换的顺序(起点)不同,则可以稍微修改上述方法:

    var data = [50,75,100,125,150,175,200,225];
    var colors = ["orange","purple","blue","yellow"];
    
    var svg = d3.select("svg");
    var circles = svg.selectAll()
      .data(data)
      .enter()
      .append("circle")
      .attr("r", 20)
      .attr("cx", function(d) { return d; })
      .attr("cy", 50)
      .attr("fill", function(d,i) { return colors[i%4]; }) // set start fill
      .attr("i", function(d,i) { return i%4;  })   // record start position.
      .each(transition);
    
    function transition() {
      var selection = d3.select(this);
      var i = selection.attr("i");
      i = ++i%colors.length;
      selection.transition()
          .duration(1000)
          .attr("i",i)
          .attr("fill", colors[i])
          .on("end", transition);
    }
    <脚本src=“https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js“></脚本>
    <svg width=“500”height=“300”></svg>

    第一个我使用颜色集作为基准,第二个我只使用位置访问颜色 i


    上述方法使用自定义属性,但我们也可以使用数据。这让我们在某些方面更加熟悉D3。

    function transition() {
      d3.select(this).transition()
          .duration(1000)
          .attr("fill", function(d) { return colors[++d.color%colors.length]; })
          .on("end", transition);
    }
    

    var data = d3.range(8).map(function(d) { return {x: d*25+50}; })
    
    var colors = ["orange","purple","blue","yellow"];
    
    var svg = d3.select("svg");
    var circles = svg.selectAll()
      .data(data)
      .enter()
      .append("circle")
      .attr("r", 20)
      .attr("cx", function(d) { return d.x; })
      .attr("cy", 50)
      .attr("fill", function(d,i) { return colors[i%4]; }) // set start fill
      .each(function(d,i) { d.color = i%4; })              // record start position.
      .each(transition);
    
    function transition() {
      d3.select(this).transition()
          .duration(1000)
          .attr("fill", function(d) { return colors[++d.color%colors.length]; })
          .on("end", transition);
    }
    <

    如果您希望变换重复x次,甚至所有变换都以相同的颜色结束,我们也可以通过向基准添加一个新属性来跟踪完成的循环,从而相当容易地做到这一点:

    var data = d3.range(8).map(function(d) { return {x: d*25+50}; })
    
    var colors = ["orange","purple","blue","yellow"];
    
    var svg = d3.select("svg");
    var circles = svg.selectAll()
      .data(data)
      .enter()
      .append("circle")
      .attr("r", 20)
      .attr("cx", function(d) { return d.x; })
      .attr("cy", 50)
      .attr("fill", function(d,i) { return colors[i%4]; }) // set start fill
      .each(function(d,i) { d.color = d.start = i%4; })              // record start position.
      .each(transition);
      
    var n = 8; // n cycles
    
    function transition() {
      d3.select(this).transition()
        .duration(1000)
        .attr("fill", function(d) { return colors[++d.color%colors.length]; })
        .on("end", function(d) { if(d.color - d.start < n) transition.apply(this); else return null; });
    }
    <脚本src=“https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js“></脚本>
    <svg width=“500”height=“300”></svg>

    有一些更懒惰的方法可以破坏性地修改数据(或部分数据),例如在这个只能运行一次的转换周期中使用shift:

    var data = [
      ["orange","purple","blue","yellow"],
      ["blue","yellow","orange","purple"]
    ];
    
    var svg = d3.select("svg");
    var circles = svg.selectAll()
      .data(data)
      .enter()
      .append("circle")
      .attr("r", 20)
      .attr("cx", function(d,i) { return i * 50 + 50; })
      .attr("cy", 50)
      .attr("fill", function(d) { return d.shift(); })
      .each(transition);
    
    function transition() {
      var selection = d3.select(this);
      if(selection.datum().length) {
        selection.transition()
          .duration(1000)
          .attr("fill", function(d) { return d.shift() })
          .on("end", transition);
      }
    }
    <脚本src=“https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js“></脚本>
    <svg width=“500”height=“300”></svg>

    您可能会看到,我的代码段中没有一个使用子选择器:这可以通过自定义属性、数据属性或更简单地使用 .attr("fill",function(d,i){

        2
  •  2
  •   i alarmed alien    7 年前

    /**
     * Apply a transition with the appropriate delay to a selection
     *
     * @param sel: a d3 selection
     * @param fill: the fill colour
     * @param position: the position of the colour in the set of transitions
     */
    
    function tr(sel, fill, position) {
        sel.transition()
        .duration(1000)
        .delay(1000 * position)
        .ease(d3.easeLinear)
        .attr("fill", fill);
    }
    
    // example of use:
    tr(group.select(":nth-child(1)"), 'blue', 0)
    tr(group.select(":nth-child(2)"), 'red', 2)
    

    行动中:

    function tr(sel, fill, pos) {
    	sel.transition()
    	.duration(1000)
        .delay(1000 * pos)
    	.ease(d3.easeLinear)
    	.attr("fill", fill);
    }
    
    var svg = d3.select('svg');
    
    var dataSet = [20, 20];
    
    var group = svg.append("g");
    var circles = group.selectAll('circle')
        .data(dataSet)
        .enter()
        .append('circle')
        .attr("r",function(d){ return d })
        .attr("cx",function(d, i){ return i * 100 + 50 })
        .attr("cy",50)
        .attr("fill",'black');
    
    var colors = {
      1: [ 'orange', 'purple', 'blue', 'yellow' ],
      2: [ 'deepskyblue', 'deeppink', 'goldenrod', 'magenta']
    };
    
    Object.keys(colors).forEach(function(ix){
       var el = group.select(":nth-child(" + ix + ")");
       colors[ix].forEach( function(c,i) { tr(el, c, i); });
    })
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <svg></svg>