d3的enter和exit
網上有很多blog講解。說的還湊合的見:https://blog.csdn.net/nicolecc/article/details/50786661
如何把自己的rude繪圖代碼,進行精致化(update)
?不多比比,上代碼示例:
d3.selectAll('.circle_group').children().remove(); var circle_group = d3.selectAll('.circle_group').data(circleData).enter().append('g').attr('class', 'circle_group brushNode').attr('transform',function (d) {return 'translate(' + d.x + ',' + d.y + ')';}).on('click', function (d) {if (shiftKey) {//標記當前點d3.select(this).classed("selected", function (d) {d.selected = !d.selected;d.previouslySelected = !d.selected;return d.selected;})//阻止事件冒泡 d3.event.stopPropagation();}}).on("contextmenu", function (node) {contextmenu("Rmenu");})circle_group.append('circle').attr('r', function (d) {return d.r;}).style("fill", function (d) {return color20(d.index);}); circle_group.append('text').attr("dy", ".35em").attr("text-anchor", "middle")//在圓圈中加上數據.style('fill', function (node) {return '#555';}).attr("y", -7).text(d => d.text);circle_group.call(d3.drag() //定義了元素拖拽行為的原點,設置為圓的圓心位置可以避免明顯的元素跳動, 與d3v3中的origin方法類似。.subject(function () {var thisData = d3.select(this);return {x: thisData.datum().x,y: thisData.datum().y};}).on("start", dragstarted).on("drag", dragmove).on("end", dragended));
?很明顯,新手圖省事都是這么繪圖的。就繪圖結果來看,如果你不加動畫一點問題都沒有,只要一加動畫過渡動畫,所有的圖形都是從無到有的過程,而我們想看的是,如果點更新的話,能看到他從哪(位置)更新到哪(位置)。(這里就不加動畫過渡了,就一行代碼的事.transition().duration(300) )。
改造如下:
//這部分代碼是有則改之,無則添加的功能 const circle_data=d3.selectAll('.circle_group').data(circleData) //更新部分,如果你數據的數目沒變,那circle_data.size()=你數據內容改變的數目,你可以把circle_data考慮成update部分就行,這樣編代碼準沒錯.attr('transform', //首次運行的時候,因為沒有元素circle_data.size()=0, 所以這個transform不會運行到function (d) {return 'translate(' + d.x + ',' + d.y + ')';}); const circle_enter=circle_data.enter().append('g') //add 部分,首次運行的時候,circle_enter.size()=全部元素,所以在circle_enter進行所有的初始化設置操作.attr('class', 'circle_group brushNode').attr('transform',function (d) {return 'translate(' + d.x + ',' + d.y + ')';}); circle_data.exit().remove(); // 當你刪一些數據的時候, .exit().size()>0 circle_enter.on('click', function (d) {if (shiftKey) {//標記當前點d3.select(this).classed("selected", function (d) {d.selected = !d.selected;d.previouslySelected = !d.selected;return d.selected;})//阻止事件冒泡 d3.event.stopPropagation();} }).on("contextmenu", function (node) {contextmenu("Rmenu");})circle_enter.append('circle') //add.attr('r', d=>d.r).style("fill", function (d) {return color20(d.index);}); circle_data.select('circle') //update,這里不要append ,因為元素已經在那了(enter().append()過了)。當然首次運行(視圖首次顯示)的時候,這幾句代碼是運行不到的。.attr('r', d=>d.r).style("fill", function (d) {return color20(d.index);});circle_enter.append('text') //add.attr("dy", ".35em").attr("text-anchor", "middle")//在圓圈中加上數據.style('fill', function (node) {return '#555';}).attr("y", -7).text(d => d.text); circle_data.select('text') //update 不要append .text(d => d.text);const drag=d3.drag() //定義了元素拖拽行為的原點,設置為圓的圓心位置可以避免明顯的元素跳動, 與d3v3中的origin方法類似。.subject(function () {var thisData = d3.select(this);return {x: thisData.datum().x,y: thisData.datum().y};}).on("start", dragstarted).on("drag", dragmove).on("end", dragended); circle_enter.call(drag); circle_data.call(drag); //update 注意,這里一定要重新綁定一下,這里涉及到drag的初始化(subject用于初始化drag拖動點的初始位置)
?
敲黑板
1、更新部分的所有與位置有關的事件(比如d3.drag()的初始位置)要重新綁定,否則會出現不可預料的結果。
2、update部分與數據有關的attr,style要重新設置,這時就不用append了。(因為這個元素既然有了,你已經在之前的.enter().append().append(其他元素)添加過了,這里只需要更新一下即可。)。一些固定的attr,style就不用重復設置了(之前enter已經綁定過了)