2017-05-30 96 views
1

我有this graph of nodes and shapes created with D3.js,我在動畫時遇到問題。我想要做的是過渡路徑的長度,但是當我這樣做時,的marker-end消失。使用D3.js中的標記過渡路徑長度會使標記消失

我已經成功地append()svg:animate元素來完成幾乎同樣的事情,但我想這是最好使用D3的transition()實際設置的pathd屬性。任何人都可以告訴我,爲什麼當他們用transition()動畫時,標記會從路徑中消失?

var graph = d3.select('.graph') 
    .append('svg') 
    .attr('width', window.innerWidth) 
    .attr('height', window.innerHeight); 

var defs = graph.append('svg:defs'); 

var blueGradient = defs.append('svg:linearGradient') 
    .attr('id', 'b') 
    .attr('x1', 0) 
    .attr('y1', 0) 
    .attr('x2', 0) 
    .attr('y2', 1) 
    .attr('spreadMethod', 'pad'); 

blueGradient.append('svg:stop') 
    .attr('offset', '0%') 
    .attr('stop-color', '#e4f5fc') 
    .attr('stop-opacity', 1); 

blueGradient.append('svg:stop') 
    .attr('offset', '100%') 
    .attr('stop-color', '#2ab0ed') 
    .attr('stop-opacity', 1); 

var orangeGradient = defs.append('svg:linearGradient') 
    .attr('id', 'o') 
    .attr('x1', 0) 
    .attr('y1', 0) 
    .attr('x2', 0) 
    .attr('y2', 1) 
    .attr('spreadMethod', 'pad'); 

orangeGradient.append('svg:stop') 
    .attr('offset', 0) 
    .attr('stop-color', '#f6e6b4') 
    .attr('stop-opacity', 1); 

orangeGradient.append('svg:stop') 
    .attr('offset', '100%') 
    .attr('stop-color', '#ed9017') 
    .attr('stop-opacity', 1); 

var arrow = defs.append('svg:marker') 
    .attr('id', 'arrow') 
    .attr('orient', 'auto') 
    .attr('markerWidth', 2) 
    .attr('markerHeight', 4) 
    .attr('refX', 0.1) 
    .attr('refY', 2); 

arrow.append('path') 
    .attr('d', 'M0,0 V4 L2,2 Z') 
    .attr('fill', '#aaa'); 

var line = d3.line() 
    .x(function(d) { console.log(d); return d.x; }) 
    .y(function(d) { console.log(d); return d.y; }); 

var appended = false; 
var step = -1; 

document.addEventListener('keydown', drawGraph); 
document.addEventListener('click', drawGraph); 

drawGraph(); 

function drawGraph(event) { 
    var nodes = [ 
     { 
      x: 200, 
      y: 50, 
      c: 'b', 
     }, 
     { 
      x: 400, 
      y: 50, 
      c: 'b' 
     }, 
     { 
      x: 600, 
      y: 50, 
      c: 'b' 
     }, 
     { 
      x: 725, 
      y: 175, 
      c: 'b' 
     }, 
     { 
      x: 600, 
      y: 300, 
      c: 'o' 
     }, 
     { 
      x: 400, 
      y: 300, 
      c: 'o' 
     }, 
     { 
      x: 200, 
      y: 300, 
      c: 'o' 
     }, 
     { 
      x: 75, 
      y: 175, 
      c: 'o' 
     } 
    ]; 

    switch (step) { 
     case -1: 
      var paths; 

      if (!appended) { 
       paths = graph.selectAll('path.nodes') 
        .data(nodes) 
        .enter() 
        .append('path') 
        .attr('stroke', '#aaa') 
        .attr('stroke-width', 10) 
        .attr('fill', 'none') 
        .attr('marker-end', 'url(#arrow)'); 

       graph.selectAll('circle.nodes') 
        .data(nodes) 
        .enter() 
        .append('svg:circle') 
        .attr('cx', function(d) { 
         return d.x; 
        }) 
        .attr('cy', function(d) { 
         return d.y; 
        }) 
        .attr('r', 19) 
        .attr('fill', function(d) { 
         return 'url(#' + d.c + ')'; 
        }) 
        .attr('stroke', function(d) { 
         switch (d.c) { 
          case 'b': 
           return '#2E75B6'; 
          case 'o': 
           return '#BF9000'; 
         } 
        }); 
      } else { 
       paths = graph.selectAll('path') 
        .transition(); 
      } 

      paths.attr('d', function(currentNode, i) { 
       if (!currentNode) { 
        return; 
       } 

       if (appended) { 
        // For some reason, the index is base 0 when the elements 
        // are appended, then 1 based afterwards. 
        i--; 
       } 

       currentNode = nodes[i]; 

       var nextNode = i < nodes.length - 1 ? 
        nodes[i + 1] : 
        nodes[0]; 

       startPath = { 
        x: currentNode.x, 
        y: currentNode.y, 
        c: currentNode.c 
       }; 

       endPath = { 
        x: nextNode.x, 
        y: nextNode.y, 
        c: nextNode.c 
       }; 

       return line([startPath, endPath]); 
      }); 

      appended = true; 
      step++; 
      break; 

     case 0: 
      graph.selectAll('path') 
       .transition() 
       .attr('d', function(currentNode, i) { 
        if (!currentNode) { 
         return; 
        } 

        // For some reason, the index is suddenly base 1, not 0. 
        i--; 

        var nextNode = i < nodes.length - 1 ? 
         nodes[i + 1] : 
         nodes[0]; 

        startPath = { 
         x: currentNode.x, 
         y: currentNode.y, 
         c: currentNode.c 
        }; 

        endPath = { 
         x: nextNode.x, 
         y: nextNode.y, 
         c: nextNode.c 
        }; 
        var diff = { 
         x: nextNode.x - currentNode.x, 
         y: nextNode.y - currentNode.y 
        }; 

        var margins = { 
         current: { 
          x: 0, 
          y: 0 
         }, 
         next: { 
          x: 0, 
          y: 0 
         } 
        }; 

        if (diff.x > 0 && diff.y === 0) { 
         margins.current.x = 30; 
        } else if (diff.x < 0 && diff.y === 0) { 
         margins.current.x = -30; 
        } else if (diff.x > 0) { 
         margins.current.x = 20; 
        } else if (diff.x < 0) { 
         margins.current.x = -20; 
        } 

        if (diff.y > 0 && diff.x === 0) { 
         margins.current.y = 30; 
        } else if (diff.y < 0 && diff.x === 0) { 
         margins.current.y = -30; 
        } else if (diff.y > 0) { 
         margins.current.y = 20; 
        } else if (diff.y < 0) { 
         margins.current.y = -20; 
        } 

        if (margins.current.x != 0) { 
         margins.next.x = margins.current.x < 0 ? 
          Math.abs(margins.current.x * 1.5) : 
          margins.current.x * -1.5; 
        } 

        if (margins.current.y != 0) { 
         margins.next.y = margins.current.y < 0 ? 
          Math.abs(margins.current.y * 1.5) : 
          margins.current.y * -1.5; 
        } 

        startPath.x += margins.current.x; 
        startPath.y += margins.current.y; 
        endPath.x += margins.next.x; 
        endPath.y += margins.next.y; 

        return line([startPath, endPath]); 
       }); 

      step--; 
      break; 
    } 
} 

回答

1

當您在else條件下做到這一點...

paths = graph.selectAll('path') 

...您選擇標記,以及,那是不是你想要的。

解決方案:給你的路的一類...

.attr("class", "nodes") 

...並通過類選擇:

paths = graph.selectAll('.nodes') 

或者,創建一個適當的 「更新」 選項(看以下是Post Scriptum)。

這裏是一個更新的小提琴:https://jsfiddle.net/khukeh13/

PS:我只是做給你看的標記移動緩慢過渡。但是,在代碼中仍然存在很多問題(查看控制檯中的錯誤),與當前問題無關,這可能值得一個新問題。這些錯誤與switchif的奇怪組合有關,我建議您擺脫它。你不需要這樣做:只需創建一個基本的「進入」,「更新」和「退出」選擇,這是D3的慣用方法。

+0

我知道代碼並不理想,但我有'switch'的原因是因爲代碼是從Reveal.js演示文稿中獲取的,其中幻燈片中的每個「片段步驟」都將提前圖形。 另外,我不太明白'.selectAll('path')'如何選擇一個未命名爲'path'的元素。你能解釋一下嗎? 最後,過渡現在將路徑減少爲無。在添加類名前沒有發生這種情況。你能解釋爲什麼嗎? –

+1

但是箭頭**是一條路徑。在你的代碼中看看這個:'arrow.append('path')。attr('d','M0,0 V4 L2,2 Z')。attr('fill','#aaa');'' 。 –

+0

'selectAll(「path」)'選擇所有'path'元素。關於轉換,正如我在PS中告訴你的那樣,這是因爲您在交換機中使用的計數器存在一些問題。 –