如何分别在组合图(条形和折线)中将x轴与条形对齐?

时间:2018-11-15 17:39:28

标签: javascript d3.js

我有一个基于 D3.js 的组合/条形图。 x轴域包含最小和最大日期,条形图基于值。但是最后一个柱(矩形)在图表之外。我可以通过强制将其引入(手动),但它不会反映数据。

var data = [
    {
        fcst_valid_local: "2018-11-13T14:00:00-0600",
        pop: 20,
        rh: 67,
        temp: 38,
        wspd: 7
    },
    {
        fcst_valid_local: "2018-11-14T15:00:00-0600",
        pop: 15,
        rh: 50,
        temp: 39,
        wspd: 8
    },
    {
        fcst_valid_local: "2018-11-15T16:00:00-0600",
        pop: 10,
        rh: 90,
        temp: 40,
        wspd: 9
    }
];

// Margins, width and height.
var margin = {top: 20, right: 20, bottom: 30, left: 50},
    width = 500 - margin.left - margin.right,
    height = 200 - margin.top - margin.bottom;

// Date parsing.
const parseDate = d3.timeParse("%Y-%m-%dT%H:%M:%S%Z");
data.forEach(function (d) {
    d.date = parseDate(d.fcst_valid_local);
});

// Set scale domains.
var x = d3.scaleTime().range([0, width])
    .domain(d3.extent(data, function (d) {
        return d.date;
    }));

var y0 = d3.scaleLinear().range([height, 0]).domain([0, 100]);
const y1 = d3.scaleLinear()
    .range([height, 0])
    .domain([0, d3.max(data, (d) => d.pop)]);

// Construct our SVG object.
const svg = d3.select('svg')
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .append('g').attr('class', 'container')
    .attr("transform",
        "translate(" + margin.left + "," + margin.top + ")");

// Set x, y-left and y-right axis.
var xAxis = d3.axisBottom(x)
    .ticks(d3.timeDay.every(1))
    // .tickFormat(d3.timeFormat('%b %d, %H:%M'))
    .tickSize(0).tickPadding(10);
var y0Axis = d3.axisLeft(y0)
    .ticks(5).tickSize(0);
var y1Axis = d3.axisRight(y1).ticks(5).tickSize(0);

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

// Draw bars.
var bars = svg.selectAll(".precips")
    .data(data);

bars.exit().remove();

bars.enter().append("rect")
    .attr("class", "precip")
    .attr("width", width / data.length - 50)
    .attr("x", function (d) {
        return x(d.date);
    })
    .attr("y", height)
    .transition().duration(1000)
    .attr("y", function (d) {
        return y0(d.pop);
    })
    .attr("height", function (d) {
        return height - y0(d.pop);
    });

const lineRH = d3.line()
    .x((d) => x(d['date']))
    .y(d => y0(d['rh']));

svg.append('path')
    .datum(data)
    .attr('class', 'line')
    .attr('fill', 'none')
    .attr('stroke', 'red')
    .attr('stroke-linejoin', 'round')
    .attr('stroke-linecap', 'round')
    .attr('stroke-width', 1.5)
    .attr('d', lineRH);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg></svg>

2 个答案:

答案 0 :(得分:1)

添加一个比最后一个项目晚一点的虚拟数据项目

这里完成了硬编码,但是您可以根据最后一项的日期动态添加它

var data = [
    {
        fcst_valid_local: "2018-11-13T14:00:00-0600",
        pop: 20,
        rh: 67,
        temp: 38,
        wspd: 7
    },
    {
        fcst_valid_local: "2018-11-14T15:00:00-0600",
        pop: 15,
        rh: 50,
        temp: 39,
        wspd: 8
    },
    {
        fcst_valid_local: "2018-11-15T16:00:00-0600",
        pop: 10,
        rh: 90,
        temp: 40,
        wspd: 9
    },
    {
        fcst_valid_local: "2018-11-16T01:00:00-0600"
    }
];

// Margins, width and height.
var margin = {top: 20, right: 20, bottom: 30, left: 50},
    width = 500 - margin.left - margin.right,
    height = 200 - margin.top - margin.bottom;

// Date parsing.
const parseDate = d3.timeParse("%Y-%m-%dT%H:%M:%S%Z");
data.forEach(function (d) {
    d.date = parseDate(d.fcst_valid_local);
});

// Set scale domains.
var x = d3.scaleTime().range([0, width])
    .domain(d3.extent(data, function (d) {
        return d.date;
    }));

var y0 = d3.scaleLinear().range([height, 0]).domain([0, 100]);
const y1 = d3.scaleLinear()
    .range([height, 0])
    .domain([0, d3.max(data, (d) => d.pop)]);

// Construct our SVG object.
const svg = d3.select('svg')
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .append('g').attr('class', 'container')
    .attr("transform",
        "translate(" + margin.left + "," + margin.top + ")");

// Set x, y-left and y-right axis.
var xAxis = d3.axisBottom(x)
    .ticks(d3.timeDay.every(1))
    // .tickFormat(d3.timeFormat('%b %d, %H:%M'))
    .tickSize(0).tickPadding(10);
var y0Axis = d3.axisLeft(y0)
    .ticks(5).tickSize(0);
var y1Axis = d3.axisRight(y1).ticks(5).tickSize(0);

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

// Draw bars.
var bars = svg.selectAll(".precips")
    .data(data);

bars.exit().remove();

bars.enter().append("rect")
    .attr("class", "precip")
    .attr("width", width / data.length - 50)
    .attr("x", function (d) {
        return x(d.date);
    })
    .attr("y", height)
    .transition().duration(1000)
    .attr("y", function (d) {
        return y0(d.pop);
    })
    .attr("height", function (d) {
        return height - y0(d.pop);
    });

const lineRH = d3.line()
    .x((d) => x(d['date']))
    .y(d => y0(d['rh']));

svg.append('path')
    .datum(data)
    .attr('class', 'line')
    .attr('fill', 'none')
    .attr('stroke', 'red')
    .attr('stroke-linejoin', 'round')
    .attr('stroke-linecap', 'round')
    .attr('stroke-width', 1.5)
    .attr('d', lineRH);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg></svg>

答案 1 :(得分:1)

尽管答案已经被接受,但我想告诉您,您不必操纵<?xml version="1.0" encoding="utf-8"?> <manifest ...> <uses-permission android:name="android.permission.INTERNET" /> <application ... android:usesCleartextTraffic="true" ...> ... </application> </manifest> (因为它也可以从API中获取),但是您可以在<UserControl.Resources> <Style BasedOn="{StaticResource {x:Type Controls:Tile}}" TargetType="Controls:Tile" x:Key="ClosableTabButton"> <Setter Property="Width" Value="17"/> <Setter Property="Height" Value="17"/> <Setter Property="ContentTemplate"> <Setter.Value> <DataTemplate> <iconPacks:PackIconFontAwesome Margin="0,0,0,0" Width="15" Height="15" Kind="PlusCircleSolid" Rotation="45" RenderTransformOrigin="0.5,0.5" Foreground="#FFFFFF"> </iconPacks:PackIconFontAwesome> </DataTemplate> </Setter.Value> </Setter> <Style.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="ContentTemplate"> <Setter.Value> <DataTemplate> <iconPacks:PackIconFontAwesome Margin="0,0,0,0" Width="15" Height="15" Kind="PlusCircleSolid" Rotation="45" RenderTransformOrigin="0.5,0.5" Foreground="#ff0000"> </iconPacks:PackIconFontAwesome> </DataTemplate> </Setter.Value> </Setter> </Trigger> </Style.Triggers> </Style> </UserControl.Resources> ,因为这就是在此处设置正确的的原因。

  1. 尝试使用d3 time_nice完善时标域
  2. 使用d3 time方法来更改日期(这里有很多)

这是一个使用上面第二种方法并设置 x域的示例:

data

说明:这会将数据中的最大日期偏移1天,因此新的x.domain()将会显示为:

var x = d3.scaleTime().range([0, width])
.domain([d3.min(data, function (d) {
    return d.date;
}), d3.timeDay.offset(d3.max(data, function (d) { return d.date; }), 1)]);

其结果如下图所示:

x.domain()
(2) [Tue Nov 13 2018 15:00:00 GMT-0500 (Eastern Standard Time), Fri Nov 16 2018 17:00:00 GMT-0500 (Eastern Standard Time)]

我还尝试了var data = [ { fcst_valid_local: "2018-11-13T14:00:00-0600", pop: 20, rh: 67, temp: 38, wspd: 7 }, { fcst_valid_local: "2018-11-14T15:00:00-0600", pop: 15, rh: 50, temp: 39, wspd: 8 }, { fcst_valid_local: "2018-11-15T16:00:00-0600", pop: 10, rh: 90, temp: 40, wspd: 9 } ]; // Margins, width and height. var margin = {top: 20, right: 20, bottom: 30, left: 50}, width = 500 - margin.left - margin.right, height = 200 - margin.top - margin.bottom; // Date parsing. const parseDate = d3.timeParse("%Y-%m-%dT%H:%M:%S%Z"); data.forEach(function (d) { d.date = parseDate(d.fcst_valid_local); }); // Set scale domains. var x = d3.scaleTime().range([0, width]) .domain([d3.min(data, function (d) { return d.date; }), d3.timeDay.offset(d3.max(data, function (d) { return d.date; }), 1)]); var y0 = d3.scaleLinear().range([height, 0]).domain([0, 100]); const y1 = d3.scaleLinear() .range([height, 0]) .domain([0, d3.max(data, (d) => d.pop)]); //console.log(x.domain()); // Construct our SVG object. const svg = d3.select('svg') .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append('g').attr('class', 'container') .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); // Set x, y-left and y-right axis. var xAxis = d3.axisBottom(x) .ticks(d3.timeDay.every(1)) // .tickFormat(d3.timeFormat('%b %d, %H:%M')) .tickSize(0).tickPadding(10); var y0Axis = d3.axisLeft(y0) .ticks(5).tickSize(0); var y1Axis = d3.axisRight(y1).ticks(5).tickSize(0); svg.append("g") .attr("class", "x-axis axis") .attr("transform", "translate(0," + height + ")") .call(xAxis); svg.append("g") .attr("class", "y-axis axis") .attr("transform", "translate(" + 0 + ", 0)") .call(y0Axis); svg.append("g") .attr("class", "y-axis axis") .attr("transform", "translate(" + width + ", 0)") .call(y1Axis); // Draw bars. var bars = svg.selectAll(".precips") .data(data); bars.exit().remove(); bars.enter().append("rect") .attr("class", "precip") .attr("width", width / data.length - 50) .attr("x", function (d) { return x(d.date); }) .attr("y", height) .transition().duration(1000) .attr("y", function (d) { return y0(d.pop); }) .attr("height", function (d) { return height - y0(d.pop); }); const lineRH = d3.line() .x((d) => x(d['date']) + (width / data.length - 50)/2) .y(d => y0(d['rh'])); svg.append('path') .datum(data) .attr('class', 'line') .attr('fill', 'none') .attr('stroke', 'red') .attr('stroke-linejoin', 'round') .attr('stroke-linecap', 'round') .attr('stroke-width', 1.5) .attr('d', lineRH);,有趣的部分是使用<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script> <svg></svg>内的d3时间间隔。随时与他们一起玩,如果您有任何疑问,请告诉我。

此外,我在行生成器fn中将.nice()(路径)偏移了 barwidth / 2

.nice()

希望这也有帮助。