在24小时时间尺度上计算条形的起始位置和宽度(D3 JS)

时间:2018-05-24 12:33:54

标签: d3.js

我想制作一个条形图,我可以在其中显示由条形图表示的特定时间段。

$(document).ready(function() {
  render_chart();
});

function render_chart() {
  var dataset = {
    "colors": [
      "#1f77b4",
      "#aec7e8",
      "#ff7f0e",
    ],
    "gates": [
      "Test-Gate"
    ],
    "operators": [
      "Operator1",
      "Operator2",
      "Operator3",
    ],
    "layers": [
      [{
        "fromHours": "14:20:00",
        "toHours": "23:00:00",
        "gate": "Test-Gate"
      }],
      [{
        "fromHours": "08:30:00",
        "toHours": "11:20:00",
        "gate": "Test-Gate"
      }],
      [{
        "fromHours": "16:00:00",
        "toHours": "18:00:00",
        "gate": "Test-Gate"
      }]
    ]
  };

  n = dataset["operators"].length;

  var today = new Date();
  today.setHours(0, 0, 0, 0);
  todayMillis = today.getTime();
  var layersData = dataset["layers"];
  console.log("BEFORE", layersData[0][0]);
  layersData.forEach(function(layer) {
    layer.forEach(function(innerLayer) {
      var fromHourParts = innerLayer.fromHours.split(/:/);
      var toHourParts = innerLayer.toHours.split(/:/);

      innerLayer.fromHours = new Date();
      innerLayer.fromHours.setTime(todayMillis + getTimePeriodMillis(fromHourParts));

      innerLayer.toHours = new Date();
      innerLayer.toHours.setTime(todayMillis + getTimePeriodMillis(toHourParts));
    })
  });
  console.log("AFTER", dataset["layers"][0][0]);

  function getTimePeriodMillis(parts) {
    return (parseInt(parts[0], 10) * 60 * 60 * 1000) +
      (parseInt(parts[1], 10) * 60 * 1000) +
      (parseInt(parts[2], 10) * 1000);
  }

  function appendExtraZeroToSingleValues(inputValue) {
    if (inputValue === 0) {
      return inputValue + "0";
    }
    return inputValue;
  }

  var parseTime = d3.timeParse("%H:%M");
  var margin = {
      top: 50,
      right: 50,
      bottom: 50,
      left: 100
    },
    width = 600 - margin.left - margin.right,
    height = 300 - margin.top - margin.bottom;

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

  var tomorrow = new Date();
  tomorrow.setHours(0, 0, 0, 0);
  tomorrow.setDate(today.getDate());
  var xScale = d3.scaleTime()
    .domain([new Date(), new Date()])
    .nice(d3.timeDay, 1)
    .range([0, width - margin.left]);

  var yScale = d3.scaleBand()
    .domain(dataset["gates"])
    .rangeRound([0, height])
    .padding(.08);

  var xAxis = d3.axisBottom(xScale)
    .ticks(24)
    .tickSize(-height)
    .tickFormat(d3.timeFormat("%H"));
  console.log("X-DOMAIN", xScale.domain());
  console.log("X-RANGE", xScale.range());
  var yAxis = d3.axisLeft(yScale)
    .tickSize(-(width - margin.left))
    .tickPadding(5);

  var layer = svg.selectAll(".layer")
    .data(dataset["layers"])
    .enter().append("g")
    .attr("class", "layer");

  var rect = layer.selectAll("rect")
    .data(function(d, i) {
      d.map(function(b) {
        b.colorIndex = i;
        return b;
      });
      return d;
    })
    .enter()
    .append("rect")
    .transition()
    .duration(500)
    .delay(function(d, i) {
      return i * 10;
    })
    .attr("y", function(d, i, j) {
      var k = Array.prototype.indexOf.call(j[i].parentNode.parentNode.childNodes, j[i].parentNode);
      return yScale(d.gate) + yScale.bandwidth() / n * k;
    })
    .attr("height", yScale.bandwidth() / n)
    .transition()
    .attr("x", function(d) {
      console.log(xScale(d.fromHours));
      return xScale(d.fromHours);
    })
    .attr("width", function(d) {
      console.log(xScale(d.toHours - d.fromHours));
      return xScale(Math.abs(d.toHours - d.fromHours / 36e5));
    })
    .attr("class", "bar")
    .style("fill", function(d) {
      return dataset["colors"][d.colorIndex];
    });

  svg.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + height + ")")
    .call(xAxis);

  svg.select("g")
    .attr("class", "y axis")
    .call(yAxis);

  var legend = svg.append("g")
    .attr("class", "legend");

  legend.selectAll('text')
    .data(dataset["operators"])
    .enter()
    .append("rect")
    .attr("x", function(d, i) {
      return (i * 120) + (width / 3);
    })
    .attr("y", width / 2.8)
    .attr("width", 10)
    .attr("height", 10)
    .style("fill", function(d, i) {
      return dataset["colors"][i];
    })

  legend.selectAll('text')
    .data(dataset["operators"])
    .enter()
    .append("text")
    .attr("x", function(d, i) {
      return (i * 120) + (width / 3) + 12;
    })
    .attr("y", (width / 2.8) + 10)
    .text(function(d) {
      return d;
    });

  var tooltip = d3.select("body")
    .append('div')
    .attr('class', 'tooltip');

  tooltip.append('div')
    .attr('class', 'gate');
  tooltip.append('div')
    .attr('class', 'tempRange');

  svg.selectAll("rect")
    .on('mouseover', function(d, i) {
      if (!d.gate) return null;

      tooltip.select('.gate').html("<b>" + dataset["operators"][i] + "</b>");
      tooltip.select('.tempRange').html(appendExtraZeroToSingleValues(d.fromHours.getHours()) + ":" +
        appendExtraZeroToSingleValues(d.fromHours.getMinutes()) + " Hours to " +
        appendExtraZeroToSingleValues(d.toHours.getHours()) + ":" +
        appendExtraZeroToSingleValues(d.toHours.getMinutes()) + " Hours");

      tooltip.style('display', 'block');
      tooltip.style('opacity', 2);

    })
    .on('mousemove', function(d) {

      if (!d.gate) return null;

      tooltip.style('top', (d3.event.layerY + 10) + 'px')
        .style('left', (d3.event.layerX - 25) + 'px');
    })
    .on('mouseout', function() {
      tooltip.style('display', 'none');
      tooltip.style('opacity', 0);
    });

}
.axis .tick line {
        stroke-width: 1;
        stroke: rgba(0, 0, 0, 0.2);
    }
    .axis path,
    .axis line {
      fill: none;
      font: 10px sans-serif;
      stroke: #000;
      shape-rendering: crispEdges;
    }

    .legend {
        padding: 5px;
        font-size: 15px;
        font-family: 'Roboto', sans-serif;
        background: yellow;
        box-shadow: 2px 2px 1px #888;
    }

    .tooltip {
        background: #eee;
        box-shadow: 0 0 5px #999999;
        color: #333;
        font-size: 12px;
        left: 130px;
        padding: 10px;
        position: absolute;
        text-align: center;
        top: 95px;
        z-index: 10;
        display: block;
        opacity: 0;
    }
<!DOCTYPE html>
<html>
	<head>
    <meta charset="UTF-8">
		<title>Bar Graph</title>

    <script
  src="https://code.jquery.com/jquery-3.3.1.min.js"
  integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
  crossorigin="anonymous"></script>
    <script src="https://d3js.org/d3.v4.min.js"></script>
	</head>
	<body>
    <div id="groupchart" class="chart"></div>
	</body>
</html>

尝试如上,但无法弄清楚如何正确地做到这一点。我搞砸了,酒吧不会以某种方式结束在图表中。 x轴域全部搞乱,并且还有宽度。如果我采取另一种规模,它工作正常。但是在时间尺度上,我无法使其发挥作用。

我做错了什么?

1 个答案:

答案 0 :(得分:1)

而不是将值的差异传递给比例......

scale(a - b)

...将每个值传递给比例并减去它们:

scale(a) - scale(b).

在你的情况下:

return xScale(Math.abs(d.toHours)) - xScale(Math.abs(d.fromHours));

以下是具有该更改的代码:

&#13;
&#13;
$(document).ready(function() {
  render_chart();
});

function render_chart() {
  var dataset = {
    "colors": [
      "#1f77b4",
      "#aec7e8",
      "#ff7f0e",
    ],
    "gates": [
      "Test-Gate"
    ],
    "operators": [
      "Operator1",
      "Operator2",
      "Operator3",
    ],
    "layers": [
      [{
        "fromHours": "14:20:00",
        "toHours": "23:00:00",
        "gate": "Test-Gate"
      }],
      [{
        "fromHours": "08:30:00",
        "toHours": "11:20:00",
        "gate": "Test-Gate"
      }],
      [{
        "fromHours": "16:00:00",
        "toHours": "18:00:00",
        "gate": "Test-Gate"
      }]
    ]
  };

  n = dataset["operators"].length;

  var today = new Date();
  today.setHours(0, 0, 0, 0);
  todayMillis = today.getTime();
  var layersData = dataset["layers"];
  console.log("BEFORE", layersData[0][0]);
  layersData.forEach(function(layer) {
    layer.forEach(function(innerLayer) {
      var fromHourParts = innerLayer.fromHours.split(/:/);
      var toHourParts = innerLayer.toHours.split(/:/);

      innerLayer.fromHours = new Date();
      innerLayer.fromHours.setTime(todayMillis + getTimePeriodMillis(fromHourParts));

      innerLayer.toHours = new Date();
      innerLayer.toHours.setTime(todayMillis + getTimePeriodMillis(toHourParts));
    })
  });
  console.log("AFTER", dataset["layers"][0][0]);

  function getTimePeriodMillis(parts) {
    return (parseInt(parts[0], 10) * 60 * 60 * 1000) +
      (parseInt(parts[1], 10) * 60 * 1000) +
      (parseInt(parts[2], 10) * 1000);
  }

  function appendExtraZeroToSingleValues(inputValue) {
    if (inputValue === 0) {
      return inputValue + "0";
    }
    return inputValue;
  }

  var parseTime = d3.timeParse("%H:%M");
  var margin = {
      top: 50,
      right: 50,
      bottom: 50,
      left: 100
    },
    width = 600 - margin.left - margin.right,
    height = 300 - margin.top - margin.bottom;

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

  var tomorrow = new Date();
  tomorrow.setHours(0, 0, 0, 0);
  tomorrow.setDate(today.getDate());
  var xScale = d3.scaleTime()
    .domain([new Date(), new Date()])
    .nice(d3.timeDay, 1)
    .range([0, width - margin.left]);

  var yScale = d3.scaleBand()
    .domain(dataset["gates"])
    .rangeRound([0, height])
    .padding(.08);

  var xAxis = d3.axisBottom(xScale)
    .ticks(24)
    .tickSize(-height)
    .tickFormat(d3.timeFormat("%H"));
  console.log("X-DOMAIN", xScale.domain());
  console.log("X-RANGE", xScale.range());
  var yAxis = d3.axisLeft(yScale)
    .tickSize(-(width - margin.left))
    .tickPadding(5);

  var layer = svg.selectAll(".layer")
    .data(dataset["layers"])
    .enter().append("g")
    .attr("class", "layer");

  var rect = layer.selectAll("rect")
    .data(function(d, i) {
      d.map(function(b) {
        b.colorIndex = i;
        return b;
      });
      return d;
    })
    .enter()
    .append("rect")
    .transition()
    .duration(500)
    .delay(function(d, i) {
      return i * 10;
    })
    .attr("y", function(d, i, j) {
      var k = Array.prototype.indexOf.call(j[i].parentNode.parentNode.childNodes, j[i].parentNode);
      return yScale(d.gate) + yScale.bandwidth() / n * k;
    })
    .attr("height", yScale.bandwidth() / n)
    .transition()
    .attr("x", function(d) {
      console.log(xScale(d.fromHours));
      return xScale(d.fromHours);
    })
    .attr("width", function(d) {
      console.log(xScale(d.toHours - d.fromHours));
      return xScale(Math.abs(d.toHours)) - xScale(Math.abs(d.fromHours));
    })
    .attr("class", "bar")
    .style("fill", function(d) {
      return dataset["colors"][d.colorIndex];
    });

  svg.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + height + ")")
    .call(xAxis);

  svg.select("g")
    .attr("class", "y axis")
    .call(yAxis);

  var legend = svg.append("g")
    .attr("class", "legend");

  legend.selectAll('text')
    .data(dataset["operators"])
    .enter()
    .append("rect")
    .attr("x", function(d, i) {
      return (i * 120) + (width / 3);
    })
    .attr("y", width / 2.8)
    .attr("width", 10)
    .attr("height", 10)
    .style("fill", function(d, i) {
      return dataset["colors"][i];
    })

  legend.selectAll('text')
    .data(dataset["operators"])
    .enter()
    .append("text")
    .attr("x", function(d, i) {
      return (i * 120) + (width / 3) + 12;
    })
    .attr("y", (width / 2.8) + 10)
    .text(function(d) {
      return d;
    });

  var tooltip = d3.select("body")
    .append('div')
    .attr('class', 'tooltip');

  tooltip.append('div')
    .attr('class', 'gate');
  tooltip.append('div')
    .attr('class', 'tempRange');

  svg.selectAll("rect")
    .on('mouseover', function(d, i) {
      if (!d.gate) return null;

      tooltip.select('.gate').html("<b>" + dataset["operators"][i] + "</b>");
      tooltip.select('.tempRange').html(appendExtraZeroToSingleValues(d.fromHours.getHours()) + ":" +
        appendExtraZeroToSingleValues(d.fromHours.getMinutes()) + " Hours to " +
        appendExtraZeroToSingleValues(d.toHours.getHours()) + ":" +
        appendExtraZeroToSingleValues(d.toHours.getMinutes()) + " Hours");

      tooltip.style('display', 'block');
      tooltip.style('opacity', 2);

    })
    .on('mousemove', function(d) {

      if (!d.gate) return null;

      tooltip.style('top', (d3.event.layerY + 10) + 'px')
        .style('left', (d3.event.layerX - 25) + 'px');
    })
    .on('mouseout', function() {
      tooltip.style('display', 'none');
      tooltip.style('opacity', 0);
    });

}
&#13;
.axis .tick line {
        stroke-width: 1;
        stroke: rgba(0, 0, 0, 0.2);
    }
    .axis path,
    .axis line {
      fill: none;
      font: 10px sans-serif;
      stroke: #000;
      shape-rendering: crispEdges;
    }

    .legend {
        padding: 5px;
        font-size: 15px;
        font-family: 'Roboto', sans-serif;
        background: yellow;
        box-shadow: 2px 2px 1px #888;
    }

    .tooltip {
        background: #eee;
        box-shadow: 0 0 5px #999999;
        color: #333;
        font-size: 12px;
        left: 130px;
        padding: 10px;
        position: absolute;
        text-align: center;
        top: 95px;
        z-index: 10;
        display: block;
        opacity: 0;
    }
&#13;
<!DOCTYPE html>
<html>
	<head>
    <meta charset="UTF-8">
		<title>Bar Graph</title>

    <script
  src="https://code.jquery.com/jquery-3.3.1.min.js"
  integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
  crossorigin="anonymous"></script>
    <script src="https://d3js.org/d3.v4.min.js"></script>
	</head>
	<body>
    <div id="groupchart" class="chart"></div>
	</body>
</html>
&#13;
&#13;
&#13;