2016-06-07 65 views
2

我是很新的D3,並通過努力把事情做好了工作。我正在嘗試配置此示例here以適當更新新數據和轉換。如何在更新d3.js圖表​​之前乾淨地刪除數據?

下面是代碼筆我已經配置(點擊提交更新) http://codepen.io/anon/pen/pbjLRW?editors=1010

從我可以收集,使用.exit的一些變化()需要一個乾淨的數據轉換,但看完後一些教程我仍然很難知道它是如何工作的。我已經看到了在調用draw函數之前簡單地移除容器的例子,但是在我的經驗有限的情況下,它會在更改數據時導致閃爍,所以我不確定這是否是最佳實踐?

現在,我不知道爲什麼數據是不是在我codepen正確更新,但我主要關注的是試圖獲得過渡權。理想情況下,我想知道如何在更改數據時移動針頭,因此它會從90> 40例如90而不是90> 0> 40.

但是,我一定會找出原因一旦點擊鏈接的codepen中的提交,它就不會在同一位置重繪自己。

這裏是我的更新功能;

function updateGuage() { 
    d3.selectAll("text").remove() 
    d3.selectAll('.needle').remove() 
    chart.remove() 
    name = "qwerty"; 
    value = "25"; 
    drawGuage(); 
} 

初始繪製;

function drawGuage() { 
    percToDeg = function(perc) { 
    return perc * 360; 
    }; 

    percToRad = function(perc) { 
    return degToRad(percToDeg(perc)); 
    }; 

    degToRad = function(deg) { 
    return deg * Math.PI/180; 
    }; 

    // Create SVG element 
    svg = el.append('svg').attr('width', width + margin.left + margin.right).attr('height', height + margin.top + margin.bottom); 

    // Add layer for the panel 
    chart = svg.append('g').attr('transform', "translate(" + ((width + margin.left)/2) + ", " + ((height + margin.top)/2) + ")"); 

    chart.append('path').attr('class', "arc chart-first"); 
    chart.append('path').attr('class', "arc chart-second"); 
    chart.append('path').attr('class', "arc chart-third"); 

    arc3 = d3.svg.arc().outerRadius(radius - chartInset).innerRadius(radius - chartInset - barWidth) 
    arc2 = d3.svg.arc().outerRadius(radius - chartInset).innerRadius(radius - chartInset - barWidth) 
    arc1 = d3.svg.arc().outerRadius(radius - chartInset).innerRadius(radius - chartInset - barWidth) 

    repaintGauge = function() { 
     perc = 0.5; 
     var next_start = totalPercent; 
     arcStartRad = percToRad(next_start); 
     arcEndRad = arcStartRad + percToRad(perc/3); 
     next_start += perc/3; 

     arc1.startAngle(arcStartRad).endAngle(arcEndRad); 

     arcStartRad = percToRad(next_start); 
     arcEndRad = arcStartRad + percToRad(perc/3); 
     next_start += perc/3; 

     arc2.startAngle(arcStartRad + padRad).endAngle(arcEndRad); 

     arcStartRad = percToRad(next_start); 
     arcEndRad = arcStartRad + percToRad(perc/3); 

     arc3.startAngle(arcStartRad + padRad).endAngle(arcEndRad); 

     chart.select(".chart-first").attr('d', arc1); 
     chart.select(".chart-second").attr('d', arc2); 
     chart.select(".chart-third").attr('d', arc3); 

    } 
    ///////// 

    var texts = svg.selectAll("text") 
    .data(dataset) 
    .enter(); 

    texts.append("text") 
    .text(function() { 
     return dataset[0].metric; 
    }) 
    .attr('id', "Name") 
    .attr('transform', "translate(" + ((width + margin.left)/6) + ", " + ((height + margin.top)/1.5) + ")") 
    .attr("font-size", 25) 
    .style("fill", "#000000"); 

    var trX = 180 - 210 * Math.cos(percToRad(percent/2)); 
    var trY = 195 - 210 * Math.sin(percToRad(percent/2)); 
    // (180, 195) are the coordinates of the center of the gauge. 

    displayValue = function() { 
    texts.append("text") 
     .text(function() { 
     return dataset[0].value; 
     }) 
     .attr('id', "Value") 
     .attr('transform', "translate(" + trX + ", " + trY + ")") 
     .attr("font-size", 18) 
     .style("fill", '#000000'); 
    } 

    texts.append("text") 
    .text(function() { 
     return 0; 
    }) 
    .attr('id', 'scale0') 
    .attr('transform', "translate(" + ((width + margin.left)/100) + ", " + ((height + margin.top)/2) + ")") 
    .attr("font-size", 15) 
    .style("fill", "#000000"); 

    texts.append("text") 
    .text(function() { 
     return gaugeMaxValue/2; 
    }) 
    .attr('id', 'scale10') 
    .attr('transform', "translate(" + ((width + margin.left)/2.15) + ", " + ((height + margin.top)/30) + ")") 
    .attr("font-size", 15) 
    .style("fill", "#000000"); 

    texts.append("text") 
    .text(function() { 
     return gaugeMaxValue; 
    }) 
    .attr('id', 'scale20') 
    .attr('transform', "translate(" + ((width + margin.left)/1.03) + ", " + ((height + margin.top)/2) + ")") 
    .attr("font-size", 15) 
    .style("fill", "#000000"); 

    var Needle = (function() { 

    //Helper function that returns the `d` value for moving the needle 
    var recalcPointerPos = function(perc) { 
     var centerX, centerY, leftX, leftY, rightX, rightY, thetaRad, topX, topY; 
     thetaRad = percToRad(perc/2); 
     centerX = 0; 
     centerY = 0; 
     topX = centerX - this.len * Math.cos(thetaRad); 
     topY = centerY - this.len * Math.sin(thetaRad); 
     leftX = centerX - this.radius * Math.cos(thetaRad - Math.PI/2); 
     leftY = centerY - this.radius * Math.sin(thetaRad - Math.PI/2); 
     rightX = centerX - this.radius * Math.cos(thetaRad + Math.PI/2); 
     rightY = centerY - this.radius * Math.sin(thetaRad + Math.PI/2); 

     return "M " + leftX + " " + leftY + " L " + topX + " " + topY + " L " + rightX + " " + rightY; 

    }; 

    function Needle(el) { 
     this.el = el; 
     this.len = width/2.5; 
     this.radius = this.len/8; 
    } 

    Needle.prototype.render = function() { 
     this.el.append('circle').attr('class', 'needle-center').attr('cx', 0).attr('cy', 0).attr('r', this.radius); 

     return this.el.append('path').attr('class', 'needle').attr('id', 'client-needle').attr('d', recalcPointerPos.call(this, 0)); 

    }; 

    Needle.prototype.moveTo = function(perc) { 
     var self, 
     oldValue = this.perc || 0; 

     this.perc = perc; 
     self = this; 

     // Reset pointer position 
     this.el.transition().delay(100).ease('quad').duration(200).select('.needle').tween('reset-progress', function() { 
     return function(percentOfPercent) { 
      var progress = (1 - percentOfPercent) * oldValue; 

      repaintGauge(progress); 
      return d3.select(this).attr('d', recalcPointerPos.call(self, progress)); 
     }; 
     }); 

     this.el.transition().delay(300).ease('bounce').duration(1500).select('.needle').tween('progress', function() { 
     return function(percentOfPercent) { 
      var progress = percentOfPercent * perc; 

      repaintGauge(progress); 
      return d3.select(this).attr('d', recalcPointerPos.call(self, progress)); 
     }; 
     }); 

    }; 

    return Needle; 

    })(); 

    needle = new Needle(chart); 
    needle.render(); 
    needle.moveTo(percent); 

    setTimeout(displayValue, 1350); 

} 

任何幫助/建議是非常讚賞,

感謝

回答

0

知道有可以結合數據後執行三種不同的操作是很重要的。處理添加,刪除和修改沒有改變的事情(或剛剛添加)。

下面是一個例子創建和操作的簡單列表:http://jsbin.com/sekuhamico/edit?html,css,js,output

var update =() => { 

    // bind data to list elements 
    // think of listWithData as a virtual representation of 
    // the array of list items you will later see in the 
    // DOM. d3.js does not handle the mapping from this 
    // virtual structure to the DOM for you. It is your task 
    // to define what is to happen with elements that are 
    // added, removed or updated. 
    var listWithData = ul.selectAll('li').data(listItems); 

    // handle additions 
    // by calling enter() on our virtual list, you get the 
    // subset of entries which need to be added to the DOM 
    // as their are not yet present there. 
    listWithData.enter().append('li').text(i => i.text).on('click', i => toggle(i)); 

    // handle removal 
    // by calling exit() on our virtual list, you get the 
    // subset of entries which need to be removed from the 
    // DOM as they are not longer present in the virtual list. 
    listWithData.exit().remove(); 

    // update existing 
    // acting directly on the virtual list will update any 
    // elements currently present in the DOM. If you would 
    // execute this line before calling exit(), you would 
    // also manipulate those items to be removed. If you 
    // would even call it before calling enter() you would 
    // miss on updating the newly added element. 
    listWithData.attr('class', i => i.active ? 'active' : ''); 

}; 

要知道,在現實中,你可能需要某種形式的ID添加到您的項目。爲了確保正確的項目被刪除,你不會訂購問題。

說明

更新功能一無所知的東西,或者即使事情已經改變。它不知道也不在乎,如果有新的數據元素或者舊數據元素已被刪除。但是這兩件事都可能發生。因此,我們分別通過撥打enter()exit()來處理這兩種情況。 D3的功能enter()exit()爲我們提供了應添加或刪除列表元素的子集。最後,我們需要關注現有數據的變化。

+0

我不明白你如何/爲什麼要調用.exit()。remove();與您要求數據輸入的功能相同? – since095

1

你想什麼退房是How selections work由麥克·博斯托克寫的。讀完這篇文章後,周圍的一切都進入更新退出的選擇將變得更加清晰。

一言以蔽之:

  1. 您創建一個選擇的元素與selectAll('li')
  2. 加入數據選擇陣列通過調用data([...])
  3. 現在D3相比有什麼已經在連接數據的DOM中。以這種方式處理的每個DOM元素具有__data__屬性,該屬性允許D3將數據項綁定到元素。
  4. 你加入後的數據,您將收到通過調用enter()進入選擇。這是每個尚未綁定到所選DOM元素的數據元素。通常你使用輸入選擇來創建新的元素,例如通過append()
  5. 致電exit()您會收到退出選擇。這些都是已經存在的DOM元素,在加入之後不再有關聯的數據項。通常情況下,您使用退出選擇來刪除DOM元素remove()
  6. 所謂的更新選擇是在與data()加入選擇後返回的一件事。您需要將更新選擇存儲在一個變量中,因此即使在調用enter()exit()後也可以訪問它。 注意:當您已經通過輸入選擇添加元素時,更新選項也會包含那些新創建的DOM元素。知道在創建新元素後更新選擇會發生變化是至關重要的。
+1

點(6)是一直困擾我D3的一點的關鍵。你能否確認這在D3版本4中沒有改變?這個評論表明它有:https://github.com/d3/d3-selection/issues/86#issuecomment-291446865 –

+0

確實,它改變了。請參閱master/d3/d3中的[d3/CHANGES.md](https://github.com/d3/d3/blob/master/CHANGES.md#selections-d3-selection) *「另外,請選擇。 append不再將輸入節點合併到更新選擇中;使用selection.merge在數據連接後合併輸入和更新。「* – rmoestl