使用新数据重绘 d3 图

Redraw d3 graph with a new data

我是 d3 的新手。你能帮我吗,为什么我在按下按钮时生成新数据后我的图表没有重绘?它绘制一次,但在我按下按钮后,它只将新数据数组记录到控制台。我是否错误地删除了旧数据?

以防万一我单独导入 d3 函数,这就是为什么写成 select 而不是 d3.select 的原因。除了更新图形外一切正常。

提前谢谢你。这里有一个类似的问题,我尝试以类似的方式进行,但我不明白我做错了什么。

const randomValue = randomInt(0, 100);
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sen', 'Oct', 'Nov', 'Dec'];

let data = months.map(m => ({
    label: m,
    value: randomValue()
}))

let values = data.map(m => m.value)

const yScale = scaleLinear().domain([0, 100]).range([0, height]);
const colorScale = scaleLinear().domain([0, 100]).range(['#d22626', '#086cdc']);
const $chartContainer = $root.append('g')
    .classed('.mz-chart__container', true)
    .attr('transform', `translate(${padding}, ${100})`);    

const xScale = scaleBand().domain(months).range([0, width]).paddingInner(0.2);
const hAxis = axisBottom().scale(xScale);
$root.append("g")
    .attr('transform', `translate(${padding}, ${height+100})`)
    .call(hAxis);


const vAxisScale = scaleLinear().domain([0, 100]).range([height, 0]);
const vAxis = axisLeft()
    .scale(vAxisScale)
    .tickSize(-width, 0, 0)
    .tickFormat((y, x) => y);

$root.append("g")
    .attr("transform", "translate(40, 100)")
    .call(vAxis);

function updateChart() {
    data = months.map(m => ({
        label: m,
        value: randomValue()
    }))

    console.log(data);
    let chartbars = $chartContainer.selectAll('.mz-chart__bar').data(data)

    chartbars
        .exit()
        .remove()

    chartbars
        .enter()
        .append('g')
        .classed('mz-chart__bar', true)
        .append('rect')
        .attr('x', (d, i) => xScale(d.label))
        .attr('y', (d, i) => height - yScale(d.value))
        .attr('height', (d, i) => yScale(d.value))
        .attr('width', (d, i) => xScale.bandwidth())
        .attr('fill', d => colorScale(d.value));
}

select(".mz-button").on("click", updateChart)
updateChart()

您有一个进入 selection 和一个退出 selection,但您没有使用更新 selection。

selection.enter() 仅在 selected 元素少于数据数组中的项目时才创建元素:它用于创建所有开始的元素,因为 none 存在。输入 selection 不包含 pre-existing 元素但是,这些元素在更新 selection (chartbars).

我们可以通过合并现有的和新的条形图然后设置它们的样式来强制它们具有适当的属性:

let newbars = chartbars
    .enter()
    .append('g')
    .classed('mz-chart__bar', true)
    .append('rect');

chartbars.select('rect').merge(newbars)  
    .attr('x', (d, i) => xScale(d.label))
    .attr('y', (d, i) => height - yScale(d.value))
    .attr('height', (d, i) => yScale(d.value))
    .attr('width', (d, i) => xScale.bandwidth())
    .attr('fill', d => colorScale(d.value));

例如:

const randomValue = Math.random;
const height = 300;
const width = 500;
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sen', 'Oct', 'Nov', 'Dec'];
const $root = d3.select("body").append("svg").attr("width",width).attr("height",height);
const padding = {left: 50, right: 10, top: 40, bottom: 40}

let data = months.map(m => ({
    label: m,
    value: randomValue()
}))

let values = data.map(m => m.value)

const $chartContainer = $root.append("g")
    .attr("transform", `translate(${padding.left},${padding.top} )`)
    .classed('.mz-chart__container', true)
   
const plotWidth = width - padding.left - padding.right;
const plotHeight = height - padding.top - padding.bottom;

const yScale = d3.scaleLinear().domain([0, 100]).range([plotHeight,0]);
const xScale = d3.scaleBand().domain(months).range([0,plotWidth]).paddingInner(0.2);
const colorScale = d3.scaleLinear().domain([0, 100]).range(['#d22626', '#086cdc']);

const hAxis = d3.axisBottom().scale(xScale);
const vAxis = d3.axisLeft()
    .scale(yScale)
    .tickSize(-plotWidth, 0, 0)
    .tickFormat((y, x) => y);
    
$chartContainer.append("g")
    .attr('transform', `translate(0, ${plotHeight})`)
    .call(hAxis);

$chartContainer.append("g")
    .call(vAxis);

function updateChart() {
    data = months.map(m => ({
        label: m,
        value: randomValue() * 100
    }))

  //  console.log(data);
    let chartbars = $chartContainer.selectAll('.mz-chart__bar').data(data)

    chartbars
        .exit()
        .remove()

    let newbars = chartbars
        .enter()
        .append('g')
        .classed('mz-chart__bar', true)
        .append('rect');
        
    chartbars.select('rect').merge(newbars)  // merge the update and enter selections
        .attr('x', (d, i) => xScale(d.label))
        .attr('y', (d, i) => plotHeight - yScale(d.value))
        .attr('height', (d, i) => yScale(d.value))
        .attr('width', (d, i) => xScale.bandwidth())
        .attr('fill', d => colorScale(d.value));
        
}

d3.select("body").on("click", updateChart)
updateChart()
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

因为我们有一个嵌套元素添加了 enter().append() 但我们 selecting 父 g 元素,我们需要 select 用 chartbars.select('rect') 更新 selection 中每个元素的子矩形,然后将其与 newbars 合并(这是新添加的矩形的 selection) .

我在间距和大小方面重新编写了您的代码 - 我也不确定,但我相信您希望红色条更大(这需要切换 y 刻度中的值范围)。最后,我删除了多余的 y 刻度。