更新模式:更新更改每个节点

时间:2018-10-25 00:13:46

标签: javascript d3.js

我一直在尝试着如何仅更新那些数据已更改但仍不正确的d3节点。在下面的小测试示例中,我仍然在改变它的所有外观。

我这样做是完全错误还是只是有些错误?

在示例中,单击形状可将形状切换为圆形或正方形,并更新“ clickCnt”属性。然后重新绘制数据。有点工作,但似乎正在重画所有内容。另外,由于某种原因,点击“红色”形状也无法正常工作,但是代码完全相同。

任何见解或技巧都将受到欢迎-我整天都被困在这

var dataArray = [];
dataArray.push({ "label": 'red', "shape": "circle", "clickCnt": 0, x: 30, y: 100 });
dataArray.push({ "label": 'orange', "shape": "square", "clickCnt": 0, x: 110, y: 100 });
dataArray.push({ "label": 'yellow', "shape": "circle", "clickCnt": 0, x: 210, y: 100 });
dataArray.push({ "label": 'green', "shape": "square", "clickCnt": 0, x: 310, y: 100 });
dataArray.push({ "label": 'blue', "shape": "circle", "clickCnt": 0, x: 30, y: 200 });
dataArray.push({ "label": 'indigo', "shape": "square", "clickCnt": 0, x: 110, y: 200 });
dataArray.push({ "label": 'violet', "shape": "circle", "clickCnt": 0, x: 210, y: 200 });
dataArray.push({ "label": 'white', "shape": "square", "clickCnt": 0, x: 310, y: 200 });

var width = 400;
var height = 400;

d3.select("div#svg-container").select("svg").remove();
var svg = d3.select("#svg-container").append("svg")
    .attr("width", width)
    .attr("height", height);

var content = svg.append("g")

function create(data) {
    var groups = content.selectAll("g")
        .data(data, function (d) {
            return d;
        });

    groups.exit().remove();

    groups.enter()
        .append("g")
        .attr('transform', function (d, i) {
            return 'translate(' + d.x + ',' + d.y + ')'
        })
        .each(function (d) {
            var e = d3.select(this);
            e.append("text")
                .classed("small-text", true)
                .classed("label", true)
                .text(function (d) {
                    return d.label;
                })
                .style("fill", function (d) {
                    return d.label;
                });

            e.append("text")
                .classed("small-text", true)
                .classed("clickCnt", true)
                .attr("y", 20)
                .text(function (d) {
                    return d.clickCnt;
                })
                .style("fill", function (d) {
                    return d.label;
                })

            if (d.shape == "circle") {
                e.append("circle")
                    .attr("class", "circle")
                    .attr("r", 15)
                    .attr("cx", 10)
                    .attr("cy", -40)
                    .on("click", iconClicked)
                    .style("cursor", "pointer");

            } else if (d.shape == "square") {
                e.append("rect")
                    .attr("class", "square")
                    .attr("width", 30)
                    .attr("height", 30)
                    .attr("x", 0)
                    .attr("y", -55)
                    .on("click", iconClicked)
                    .style("cursor", "pointer");
            }
        });
}


create(dataArray);

function iconClicked(evt) {
    if (evt.shape == "circle") {
        evt.shape = "square"
    } else if (evt.shape == "square") {
        evt.shape = "circle"
    }
    evt.clickCnt++;

    document.getElementById('output').innerHTML = "item clicked: " + evt.label + " " + evt.clickCnt;
    
    create(dataArray);
}
    .circle {
      stroke: red;
      stroke-width: 2px;
      
    }
    .square {
      stroke: blue;
      stroke-width: 2px;
    }

    #timeline-background {
      background: slategray;
    }

    .label {
      fill: blue;
    }
    .small-text {
      font-size: 16px;
      
    }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

<body>
  <label id="output">out</label>
  <div id="timeline-background" style="width: 100%; height: 100%;">
    <div id="svg-container"></div>
  </div>
</body>

1 个答案:

答案 0 :(得分:0)

绑定数据时,这里的问题是key function

如果您查看文档,将会看到:

  

可以通过计算每个数据和元素的 string 标识符,指定一个关键功能来控制将哪个数据分配给哪个元素,从而替换默认的按索引联接。 (强调我的)

但是,在您的情况下,不是使用字符串,而是返回了整个对象:

var groups = content.selectAll("g")
    .data(data, function (d) {
        return d;
        //     ^--- this is an object
    });

当然,这是行不通的。

因此,我们具有您描述的行为:您的exit选择包含所有组,并且它们都被删除。然后,enter选择包含所有元素,并且它们都被重新绘制。

让我们看一下,单击元素,然后查看控制台:

var dataArray = [];
dataArray.push({ "label": 'red', "shape": "circle", "clickCnt": 0, x: 30, y: 100 });
dataArray.push({ "label": 'orange', "shape": "square", "clickCnt": 0, x: 110, y: 100 });
dataArray.push({ "label": 'yellow', "shape": "circle", "clickCnt": 0, x: 210, y: 100 });
dataArray.push({ "label": 'green', "shape": "square", "clickCnt": 0, x: 310, y: 100 });
dataArray.push({ "label": 'blue', "shape": "circle", "clickCnt": 0, x: 30, y: 200 });
dataArray.push({ "label": 'indigo', "shape": "square", "clickCnt": 0, x: 110, y: 200 });
dataArray.push({ "label": 'violet', "shape": "circle", "clickCnt": 0, x: 210, y: 200 });
dataArray.push({ "label": 'white', "shape": "square", "clickCnt": 0, x: 310, y: 200 });

var width = 400;
var height = 400;

d3.select("div#svg-container").select("svg").remove();
var svg = d3.select("#svg-container").append("svg")
    .attr("width", width)
    .attr("height", height);

var content = svg.append("g")

function create(data) {
    var groups = content.selectAll("g")
        .data(data, function (d) {
            return d;
        });
        
    console.log("The exit selection size is: " + groups.exit().size())

    groups.exit().remove();

    groups.enter()
        .append("g")
        .attr('transform', function (d, i) {
            return 'translate(' + d.x + ',' + d.y + ')'
        })
        .each(function (d) {
            var e = d3.select(this);
            e.append("text")
                .classed("small-text", true)
                .classed("label", true)
                .text(function (d) {
                    return d.label;
                })
                .style("fill", function (d) {
                    return d.label;
                });

            e.append("text")
                .classed("small-text", true)
                .classed("clickCnt", true)
                .attr("y", 20)
                .text(function (d) {
                    return d.clickCnt;
                })
                .style("fill", function (d) {
                    return d.label;
                })

            if (d.shape == "circle") {
                e.append("circle")
                    .attr("class", "circle")
                    .attr("r", 15)
                    .attr("cx", 10)
                    .attr("cy", -40)
                    .on("click", iconClicked)
                    .style("cursor", "pointer");

            } else if (d.shape == "square") {
                e.append("rect")
                    .attr("class", "square")
                    .attr("width", 30)
                    .attr("height", 30)
                    .attr("x", 0)
                    .attr("y", -55)
                    .on("click", iconClicked)
                    .style("cursor", "pointer");
            }
        });
}


create(dataArray);

function iconClicked(evt) {
    if (evt.shape == "circle") {
        evt.shape = "square"
    } else if (evt.shape == "square") {
        evt.shape = "circle"
    }
    evt.clickCnt++;

    document.getElementById('output').innerHTML = "item clicked: " + evt.label + " " + evt.clickCnt;
    
    create(dataArray);
}
.as-console-wrapper { max-height: 20% !important;}
.circle {
      stroke: red;
      stroke-width: 2px;
      
    }
    .square {
      stroke: blue;
      stroke-width: 2px;
    }

    #timeline-background {
      background: slategray;
    }

    .label {
      fill: blue;
    }
    .small-text {
      font-size: 16px;
      
    }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

<body>
  <label id="output">out</label>
  <div id="timeline-background" style="width: 100%; height: 100%;">
    <div id="svg-container"></div>
  </div>
</body>

(部分)解决方案:使用唯一字符串作为返回值,例如label属性:

var groups = content.selectAll("g")
    .data(data, function (d) {
        return d.label;
    });

看看:

var dataArray = [];
dataArray.push({ "label": 'red', "shape": "circle", "clickCnt": 0, x: 30, y: 100 });
dataArray.push({ "label": 'orange', "shape": "square", "clickCnt": 0, x: 110, y: 100 });
dataArray.push({ "label": 'yellow', "shape": "circle", "clickCnt": 0, x: 210, y: 100 });
dataArray.push({ "label": 'green', "shape": "square", "clickCnt": 0, x: 310, y: 100 });
dataArray.push({ "label": 'blue', "shape": "circle", "clickCnt": 0, x: 30, y: 200 });
dataArray.push({ "label": 'indigo', "shape": "square", "clickCnt": 0, x: 110, y: 200 });
dataArray.push({ "label": 'violet', "shape": "circle", "clickCnt": 0, x: 210, y: 200 });
dataArray.push({ "label": 'white', "shape": "square", "clickCnt": 0, x: 310, y: 200 });

var width = 400;
var height = 400;

d3.select("div#svg-container").select("svg").remove();
var svg = d3.select("#svg-container").append("svg")
    .attr("width", width)
    .attr("height", height);

var content = svg.append("g")

function create(data) {
    var groups = content.selectAll("g")
        .data(data, function (d) {
            return d.label;
        });
        
    console.log("The exit selection size is: " + groups.exit().size())

    groups.exit().remove();

    groups.enter()
        .append("g")
        .attr('transform', function (d, i) {
            return 'translate(' + d.x + ',' + d.y + ')'
        })
        .each(function (d) {
            var e = d3.select(this);
            e.append("text")
                .classed("small-text", true)
                .classed("label", true)
                .text(function (d) {
                    return d.label;
                })
                .style("fill", function (d) {
                    return d.label;
                });

            e.append("text")
                .classed("small-text", true)
                .classed("clickCnt", true)
                .attr("y", 20)
                .text(function (d) {
                    return d.clickCnt;
                })
                .style("fill", function (d) {
                    return d.label;
                })

            if (d.shape == "circle") {
                e.append("circle")
                    .attr("class", "circle")
                    .attr("r", 15)
                    .attr("cx", 10)
                    .attr("cy", -40)
                    .on("click", iconClicked)
                    .style("cursor", "pointer");

            } else if (d.shape == "square") {
                e.append("rect")
                    .attr("class", "square")
                    .attr("width", 30)
                    .attr("height", 30)
                    .attr("x", 0)
                    .attr("y", -55)
                    .on("click", iconClicked)
                    .style("cursor", "pointer");
            }
        });
}


create(dataArray);

function iconClicked(evt) {
    if (evt.shape == "circle") {
        evt.shape = "square"
    } else if (evt.shape == "square") {
        evt.shape = "circle"
    }
    evt.clickCnt++;

    document.getElementById('output').innerHTML = "item clicked: " + evt.label + " " + evt.clickCnt;
    
    create(dataArray);
}
.as-console-wrapper { max-height: 20% !important;}
.circle {
      stroke: red;
      stroke-width: 2px;
      
    }
    .square {
      stroke: blue;
      stroke-width: 2px;
    }

    #timeline-background {
      background: slategray;
    }

    .label {
      fill: blue;
    }
    .small-text {
      font-size: 16px;
      
    }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

<body>
  <label id="output">out</label>
  <div id="timeline-background" style="width: 100%; height: 100%;">
    <div id="svg-container"></div>
  </div>
</body>

现在,如您所见,exit选择的大小始终为零。

但是,我写 partial 的原因是:您不再更改元素了!原因是您没有正确的更新选​​择。由于退出选择不再具有任何元素,因此进入选择的大小也为零。这里没有任何更新。

创建此类更新选择超出了此答案的范围,我将把这项工作留给您。