d3js进入,更新,退出混乱:请协助

迈克·詹姆斯

我真的很讨厌问这个问题,特别是因为我知道已经问过很多遍了-我已经阅读了所有帖子。但是我的问题仍然存在-我根本不了解这种机制的工作原理。我是d3js的新手,正在流星中使用v3.x我已经遍历了教程,并且可以正常工作,但是无法使用新数据进行更新。同样,我很抱歉再次提出这个建议,但我所读过的其他文章都没有解决这个问题。

这是一个代码片段,我将所有无关紧要的内容都去了,专注于核心功能:

var w = 800;
var h = 800;
var intensity = 25;
var margin = {
    top: 75,
    right: 100,
    bottom: 75,
    left: 60
};

var svg = d3.select('#heatmap')
    .append('svg')
    .attr('width', w + margin.left + margin.right)
    .attr('height', h + margin.top + margin.bottom)
    .append('g')
    .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');

// get csv data, x & y coords, etc...

createHeatmap = function(csv, x, y) {
    var data = d3.csv.parseRows(csv).map(function(row) {
        return row.map(function(d) {
            return +d;
        });
    });

    // set some values
    var min = 0;
    var max = d3.max(data, function(d, i) {
        return i + 1;
    });
    var rectSize = 4;

    // set the scales for both axes
    ...

    // set up the axes
    ...

    // define colorScale
    ...

    // create heatmap
    svg.selectAll('g')
        .data(data)
        .enter()
        .append('g')
        .selectAll('rect')
        .data(function(d, i, j) {
            return d;
        })
        .enter() // start drawing rects
        .append('rect')
        .attr('x', function(d, i, j) {
            return (i * rectSize);
        })
        .attr('y', function(d, i, j) {
            return (j * rectSize);
        })
        .attr('width', w / max)
        .attr('height', h / max)
        .style('fill', function(d, i, j) {
            return colorScale(d * intensity);
        });

    // append axes, scales, labels, etc.
}

// create heatmap
createHeatmap(csv, x, y);

我的问题是,当我将新数据传递到createHeatmap()时,为什么图表不更新热图,所以我不明白。

我在调试器中逐步进行了调试,一切工作均与最初创建热图(可以正确渲染)所期望的一样。当我发送新数据时,便是谜团开始的时刻。调试器在d3js本身(不在我的代码中)深处显示,enter()具有完整的od null值数组,而不是我要传递的数据。该数据一直存在。因此,当d3js处理空数据时,它自然会返回一个空对象,因此不会发生更新。

显然,我没有正确地执行更新,但是对于纠正它所需要做的事情一无所知。

任何建议,不胜感激。

谢谢!

更新:安德鲁,感谢您的答复。我尝试了您的第一个建议,修改了您的示例以适合我的数据,但是它不会随新数据一起更新。

我的变更:

    var w = 800;
    var h = 800;
    var intensity = 25;
    var margin = {
        top: 75,
        right: 100,
        bottom: 75,
        left: 60
    };

    var svg = d3.select('#heatmap')
        .append('svg')
        .attr('width', w + margin.left + margin.right)
        .attr('height', h + margin.top + margin.bottom)
        .append('g')
        .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');

    // get csv data, x & y coords, etc...

    createHeatmap = function(csv, x, y) {
        var data = d3.csv.parseRows(csv).map(function(row) {
            return row.map(function(d) {
                return +d;
            });
        });

        // set some values
        var min = 0;
        var max = d3.max(data, function(d, i) {
            return i + 1;
        });
        var rectSize = 4;

        // set the scales for both axes
        ...

        // set up the axes
        ...

        // define colorScale
        ...    

        // append group of svg elements bound to data
        var rows = svg.selectAll('g')
            .data(data);

        // enter new rows where needed
        rows.enter().append('g');

        // select all rects
        var rects = rows.selectAll('rect')
            .data(function(d, i, j) {
                return d;
            });

        // enter new rects:
        rects.enter().append('rect')
            .attr('x', function(d, i, j) {
                return (i * rectSize);
            })
            .attr('y', function(d, i, j) {
                return (j * rectSize);
            })
            .attr('width', w / max)
            .attr('height', h / max)
            .style('fill', function(d, i, j) {
                return colorScale(d * intensity);
            });

新增的代码段:

var csv = "'3, 6, 0, 8'\n'1, 9, 0, 4'\n'3, 0, 1, 8'\n'4, 0, 2, 7";
csv = csv.replace(/'/g,'');

var button = d3.select('button')
    .on('click', function() {
        createHeatmap(update());
    });

var w = 120;
var h = 120;
var intensity = 10;
var margin = {
    top: 25,
    right: 25,
    bottom: 25,
    left: 25
};

var svg = d3.select('#heatmap')
    .append('svg')
    .attr('width', w + margin.left + margin.right)
    .attr('height', h + margin.top + margin.bottom)
    .append('g')
    .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');

createHeatmap(csv); 

function createHeatmap(csv) {
    console.log(csv);
    var data = d3.csv.parseRows(csv).map(function(row) {
        return row.map(function(d) {
            return +d;
        });
    });

    var min = 0;
    var max = d3.max(data, function(d, i) {
        return i + 1;
    });
    var rectSize = 30;

    // define a colorScale with domain and color range
    var colorScale = d3.scale.linear()
        .domain([0,0.5,1])
        .range(['red', 'green', 'blue']);

    // append group of svg elements bound to data
    var rows = svg.selectAll('g')
        .data(data);

    // enter new rows where needed
    rows.enter().append('g');

    // select all rects
    var rects = rows.selectAll('rect')
        .data(function(d, i, j) {
            return d;
        });

    // enter new rects:
    rects.enter().append('rect')
        .attr('x', function(d, i, j) {
            return (i * rectSize);
        })
        .attr('y', function(d, i, j) {
            return (j * rectSize);
        })
        .attr('width', w / max)
        .attr('height', h / max)
        .style('fill', function(d, i, j) {
            return colorScale(d * intensity);
        });
}

function update() {
    var data = "'0, 1, 9, 5'\n'4, 0, 7, 2'\n'6, 3, 0, 8'\n'5, 3, 7, 0";
    data = data.replace(/'/g,'');
    return data;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<button>Update</button>
<div id="heatmap"></div>

安德鲁·里德(Andrew Reid)

问题出在您的方法链接中。

首次运行时,应按预期运行:

// create heatmap
svg.selectAll('g')  // 1. select all g elements
 .data(data)        // 2. assign data
 .enter()           // 3. enter and append a g for each item in the data array 
 .append('g')       //    that doesn't have a corresponding element in the DOM (or more accurately, the selection)                     
 .selectAll('rect') // 4. For each newly entered g, select child rectangles
 .data(function(d, i, j) { // 5. assign data to child selection.
    return d;
 })
 .enter()            // 6. Enter and append a rect for each item in the child g's data array 
 .append("rect")     //    that doesn't have a corresponding element in the DOM.
 ....                // 7. Style

在第一次运行时,我们选择了所有gs,没有一个,因此enter选择将为数据数组中的每个项目都包含一个元素:我们正在输入所有内容。与子矩形相同:选择时不存在子矩形,因此在子数据数组中输入所有内容。

在使用的第二次运行中,svg.selectAll("g")您选择g了第一次创建的所有s-如果数据数组的项目数相同,则无需输入任何内容。您不想添加任何内容:enter().append()第二次(不是在任何情况下都使用.append()附加更多元素)。

基本上在第二遍时,您正在修改一个空选择。

相反,您想更新。当回车选择在第二遍为空时,更新选择具有所有现有g的。

有几种方法可以做到这一点,一种是断开链接:

这是第3版解决方案:

var rows = svg.selectAll("g")
    .data(data);

// enter new rows where needed
rows.enter().append("g")

// Select all rects
var rects = rows.selectAll("rect")
  .data(function(d) { return d; })

// Enter new rects:
rects.enter().append("rect")

// Update rects (all rects, not just the newly entered):
rects.attr()...

下面的代码段使用此模式,rectg根据需要输入new然后更新所有rects和gs。这利用了d3v3中的魔力,其中更新选择和回车选择在内部合并,而d3v4,v5中不是这种情况,我将在下面显示。

var button = d3.select("button")
  .on("click", function() {
    update(random());
  })
  
var svg = d3.select("div")
  .append("svg");
  
var color = d3.scale.linear()
  .domain([0,0.5,1])
  .range(["red","orange","yellow"])
  
update(random());

function update(data) {
    var rows = svg.selectAll("g")
        .data(data);
 
    // enter new rows where needed
    rows.enter()
      .append("g")
      .attr("transform", function(d,i) {
        return "translate("+[0,i*22]+")";
      })
       
    // Select all rects:
    var rects = rows.selectAll("rect")
      .data(function(d) { return d; })

    // Enter new rects:
    rects.enter().append("rect")

    // Update rects:
    rects.attr("fill", function(d) {
        return color(d);
      })
      .attr("x", function(d,i) { return i*22; })
      .attr("width", 20)
      .attr("height", 20);
 
 
    console.log("entered rows:" + rows.enter().size());
    console.log("entered rects:" + rects.enter().size());

}

function random() {
  return d3.range(5).map(function() {
    return d3.range(5).map(function() {
      return Math.random();
    })
 })
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<button>Update</button>
<div></div>

v4 / v5

对于我建议升级到的v4 / v5,此模式有些不同,因为您必须显式合并enter和update选择:

var button = d3.select("button")
  .on("click", function() {
    update(random());
  })
  
var svg = d3.select("div")
  .append("svg");
  
var color = d3.scaleLinear()
  .domain([0,0.5,1])
  .range(["red","orange","yellow"])
  
update(random());

function update(data) {
    var rows = svg.selectAll("g")
        .data(data);
 
    // enter new rows where needed
    rows = rows.enter()
      .append("g")
      .merge(rows)  // merge with existing rows
      .attr("transform", function(d,i) {
        return "translate("+[0,i*22]+")";
      })

    // Select all rects:
    var rects = rows.selectAll("rect")
      .data(function(d) { return d; })

    // Enter new rects:
    rects = rects.enter().append("rect")
      .merge(rects);

    // Update rects:
    rects.attr("fill", function(d) {
        return color(d);
      })
      .attr("x", function(d,i) { return i*22; })
      .attr("width", 20)
      .attr("height", 20);
 
}

function random() {
  return d3.range(5).map(function() {
    return d3.range(5).map(function() {
      return Math.random();
    })
 })
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<button>Update</button>
<div></div>

更新资料

您的代码段几乎包含了所有更改,但是您仍然需要分解第二个选择,即矩形选择,以便输入新矩形,然后更新所有矩形:

var csv = "'3, 6, 0, 8'\n'1, 9, 0, 4'\n'3, 0, 1, 8'\n'4, 0, 2, 7";
csv = csv.replace(/'/g,'');

var button = d3.select('button')
    .on('click', function() {
        createHeatmap(update());
    });

var w = 120;
var h = 120;
var intensity = 10;
var margin = {
    top: 25,
    right: 25,
    bottom: 25,
    left: 25
};

var svg = d3.select('#heatmap')
    .append('svg')
    .attr('width', w + margin.left + margin.right)
    .attr('height', h + margin.top + margin.bottom)
    .append('g')
    .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');

createHeatmap(csv); 

function createHeatmap(csv) {
    console.log(csv);
    var data = d3.csv.parseRows(csv).map(function(row) {
        return row.map(function(d) {
            return +d;
        });
    });

    var min = 0;
    var max = d3.max(data, function(d, i) {
        return i + 1;
    });
    var rectSize = 30;

    // define a colorScale with domain and color range
    var colorScale = d3.scale.linear()
        .domain([0,0.5,1])
        .range(['red', 'green', 'blue']);

    // append group of svg elements bound to data
    var rows = svg.selectAll('g')
        .data(data);

    // enter new rows where needed
    rows.enter().append('g');

    // select all rects
    var rects = rows.selectAll('rect')
        .data(function(d, i, j) {
            return d;
        });

    // enter new rects:
    rects.enter().append('rect');
    
    // CHANGES HERE:
    // Broke chain so that update actions aren't carried out on the enter selection:
    rects.attr('x', function(d, i, j) {
            return (i * rectSize);
        })
        .attr('y', function(d, i, j) {
            return (j * rectSize);
        })
        .attr('width', w / max)
        .attr('height', h / max)
        .style('fill', function(d, i, j) {
            return colorScale(d * intensity);
        });
}

function update() {
    var data = "'0, 1, 9, 5'\n'4, 0, 7, 2'\n'6, 3, 0, 8'\n'5, 3, 7, 0";
    data = data.replace(/'/g,'');
    return data;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<button>Update</button>
<div id="heatmap"></div>

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章