首页 > 解决方案 > D3 - 不更新数据可视化的单选按钮

问题描述

我创建了一个 D3 图表,我想在用户单击其中一个单选按钮后对其进行更新。按钮的值不是数据的一部分,而是沿 X 轴的时间范围的更新,应该更新以及基于新选择的 timeRange 值的 Y 轴数据(起点是从“现在”开始的 36 小时) . 我可以从 console.log 语句中得知,当用户单击单选按钮时 timeRange 正在更新,但无法弄清楚我需要做什么才能更新该图表(我正在尝试使用 change() 函数实现这一目标。

这是一个小提琴:https ://jsfiddle.net/dtepdc/L1qf0bvk/

这是我的代码:

const dataset = [
{
    start_date: "2019-01-27T14:30:40",
    end_date: "2019-01-27T16:32:25",
    elapsed_time: 130,
    coNum:"CO19044"
},
{
    start_date: "2019-01-27T03:05:40",
    end_date: "2019-01-27T03:32:25",
    elapsed_date: 189,
    coNum:"CO12904"
   },
   {
    start_date: "2019-01-26T22:15:40",
    end_date: "2019-01-26T23:32:25",
    elapsed_time: 89,
    coNum:"CO18345"
    },
    {
    start_date: "2019-01-26T07:00:40",
    end_date: "2019-01-26T07:40:25",
    elapsed_time: 89,
    coNum:"CO12005"
    }

];


const coNumW = window.innerWidth,
coNumH = window.innerHeight,
margin = {top: coNumH * 0.15, right: coNumW * 0.05, bottom: coNumH * 0.12, left: coNumW * 0.12},
w = coNumW - margin.left - margin.right,
h = coNumH - margin.top - margin.bottom;


const xSc = d3.scaleTime().range([0, w]),
  ySc = d3.scaleBand().range([h, 0]),
  xAxis = d3.axisBottom(xSc),
  yAxis = d3.axisLeft(ySc),
  filtered = [],
  dateFormat = d3.timeFormat("%Y-%m-%d %I:%M %p");

const svg = d3.select("body")
.append("svg")
.attr("width", coNumW)
.attr("height", coNumH)
.append("g").classed("no-select", true)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");

let radio = d3.select('input[name="options"]:checked').property("value");
let timeRange = radio;

let start = moment().subtract(timeRange, 'hours').format('LLL');
const end = moment().format('LLL');

timeRange = this.value;
dataset.forEach(function(d, i) {
            console.log('forEach timeRange: ', timeRange);
            d.start_date = new Date(d.start_date);
            d.end_date = new Date(d.end_date);
        if (d.start_date >= new Date(start) && d.end_date <= new Date(end)) {
            filtered.push(d);
            }
        });

xSc.domain([new Date(end), new Date(start)])
.range([0, w]);
ySc.domain(filtered.map(d => d.coNum)).padding(0.1);
console.log('xSc & ySc timeRange: ', timeRange)
svg.append("g")
        .attr("class", "x Axis")
        .attr("transform", "translate(0, " + h + ")")
        .call(xAxis)

svg.append("g")
        .attr("class", "y Axis")
        .call(yAxis);

const tasks = svg.append("g").attr("class", "dataCont")
        .selectAll("g")
        .data(filtered)
        .enter()
        .append("g")
        .on("mouseenter", showData);

tasks.append("rect")
        .attr("x", function(d) {
            return xSc(d.start_date) + 2;  // + 2 is for padding
        })
        .attr("y", function(d) {
        return ySc(d.coNum);
        })
        .attr("width", function(d) {
            return xSc(d.start_date) - xSc(d.end_date) - 2;
        })
        .attr("height", function(d) {
            return ySc.bandwidth();
        })
        .attr("fill", "green");

d3.selectAll("input")
      .on("change", change);

  function change() {

    timeRange = this.value;
    dataset.forEach(function(d, i) {
                console.log('forEach timeRange: ', timeRange);
                d.start_date = new Date(d.start_date);
                d.end_date = new Date(d.end_date);
            if (d.start_date >= new Date(start) && d.end_date <= new Date(end)) {
                filtered.push(d);
                }
            });

    xSc.domain([new Date(end), new Date(start)])
    .range([0, w]);
    ySc.domain(filtered.map(d => d.coNum)).padding(0.1);
    console.log('xSc & ySc timeRange: ', timeRange)
    svg.append("g")
            .attr("class", "x Axis")
            .attr("transform", "translate(0, " + h + ")")
            .call(xAxis)

    svg.append("g")
            .attr("class", "y Axis")
            .call(yAxis);

    const tasks = svg.append("g").attr("class", "dataCont")
            .selectAll("g")
            .data(filtered)
            .enter()
            .append("g")
            .on("mouseenter", showData);

    tasks.append("rect")
            .attr("x", function(d) {
                return xSc(d.start_date) + 2;  // + 2 is for padding
            })
            .attr("y", function(d) {
            return ySc(d.coNum);
            })
            .attr("width", function(d) {
                return xSc(d.start_date) - xSc(d.end_date) - 2;
            })
            .attr("height", function(d) {
                return ySc.bandwidth();
            })
            .attr("fill", "green");
        }

    function showData(d) {
        const dur = (d.end_date - d.start_date)/3600000;
        console.log("-" + d.coNum + "- start_date: " + dateFormat(d.start_date) + " || end_date: " + dateFormat(d.end_date))
    }

标签: d3.js

解决方案


我在一个项目中做过类似的事情。我认为您缺少的主要内容是,当您更改数据时,您需要删除当前图表并绘制一个新图表。你也许可以让 Angular 为你做这件事,但我不知道怎么做。

就个人而言,我会将逻辑重构为类似drawChart(filteredData)and filterData(unfilteredData, HOW TO FILTER)。页面加载时的初始绘制将调用drawChart您想要的任何数据(它可以通过filterData()。您的change()函数将从HOW TO FILTER单选按钮中获取 ,将其传递给filterData(),然后删除图表,然后drawChart使用过滤后的数据进行调用。这种架构使图表的每次绘制都相同,如果您需要更改绘制图表的方式,您可以在一个地方进行。

change新功能示例

change(howToFilter){
  var filteredData = filterData(dataset, howToFilter);
  CODE TO REMOVE THE CHART
  drawChart(filteredData);
}

推荐阅读