D3浮动条形图中的多色条形图由时间计算确定

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了D3浮动条形图中的多色条形图由时间计算确定相关的知识,希望对你有一定的参考价值。

当前:

期望:

以下条形图由两个水平条组成。其中一个代表员工安排当天工作的时间跨度。第二个代表员工实际工作的时间。第二个栏根据数据分为3个单独的部分(work_start_1-work_end_1 /员工休息的差距/ work_start_2-work_end_2)。该员工的工作时间比他们预定的要长。如何将第二个栏(工作)上的相应区域设为红色,以提醒员工工作时间过长? D3证明这很困难。

<script src="moment.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<link rel="stylesheet" type="text/css" href="style.css"/>

var all_moments = [];

var scheduled_start = moment("2015-03-25 08:00:00");
var scheduled_end = moment("2015-03-25 17:00:00");

var worked_start_1 =  moment("2015-03-25 08:00:00");
var worked_end_1 =  moment("2015-03-25 12:00:00");

var worked_start_2 =  moment("2015-03-25 13:00:00");
var worked_end_2 =  moment("2015-03-25 19:00:00");

all_moments.push(scheduled_start);
all_moments.push(scheduled_end);
all_moments.push(worked_start_1);
all_moments.push(worked_end_1);
all_moments.push(worked_start_2);
all_moments.push(worked_end_2);

var earliest_moment = moment.min(all_moments);
var latest_moment = moment.min(all_moments);


var data=[
    {"action": "Scheduled", "tooltipTitle": "Scheduled","gap": false,"to": scheduled_end,"from": scheduled_start},
    {"action": "Worked", "tooltipTitle": "Worked", "gap": false,"to": worked_end_1,"from": worked_start_1},
    {"action": "Worked", "tooltipTitle": "Gap", "gap": true, "to": worked_start_2,"from": worked_end_1},
    {"action": "Worked", "tooltipTitle": "Worked", "gap": false, "to": worked_end_2,"from": worked_start_2}
]

var margin = {top: 50, right: 50, bottom: 50, left: 100},
              width = 900 - margin.left - margin.right,
              height = 500 - margin.top - margin.bottom;

var y = d3.scale.ordinal().rangeRoundBands([0, height], .08);

var x = d3.time.scale()
        .domain([earliest_moment,latest_moment])
        .range([0,width]);

y.domain(data.map(function(d) { return d.action; }));
x.domain([d3.min(data,function(d){return d.from;}), d3.max(data,function(d){return d.to;})]);

var customTimeFormat = d3.time.format("%I:%M:%p");  

var xAxis = d3.svg.axis()
            .scale(x)
            .orient("bottom")
            .ticks(10)
            .tickFormat(customTimeFormat);

var yAxis = d3.svg.axis()
            .scale(y)
            .orient("left");

var svg = d3.select("body").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 + ")");

svg.append("g")
    .attr("class", "x axis")
    .style("font", "14px times")
    .attr("transform", "translate(0," + height + ")")
    .call(xAxis)
    .append("text")
    .attr("x", width-75)
    .attr("dx", ".71em")
    .attr("dy", "-.71em");
    //.text("Temperatures (C)");

svg.append("g")
    .attr("class", "y axis")
    .style("font", "18px times")
    .style('font-family', '"Open Sans", sans-serif')
    .call(yAxis);


svg.selectAll(".bar")
    .data(data)
    .enter().append("rect")
    .attr("class", "bar")
    .attr("y", function(d) { return y(d.action); })
    .attr("height", y.rangeBand())
    .attr("x", function(d) { return x(d.from); })
    .attr("width", function(d) { return x(d.to)-x(d.from) })
    .style("fill", function(d,i) {
        if(d.action == "Worked" && d.gap == false) {
            return d3.rgb("#76ff03");
        }
        else if(d.action == "Worked" && d.gap == true) {
            return d3.rgb("#e0e0e0");
        }
        else {
            return d3.rgb("#00e5ff");
        }
    });


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

tooltip.append('div')
    .attr('class', 'tooltipTitle');
    tooltip.append('div')
    .attr('class', 'timeRange');

svg.selectAll(".bar")
    .on('mouseover', function(d) {
        //console.log(m1.format("MMM Do YY"));

        tooltip.select('.tooltipTitle').html("<b>" + d.tooltipTitle + "</b>");
        tooltip.select('.timeRange').html( d.from.format("LT") + " to " + d.to.format("LT"));

        tooltip.style('display', 'block');
        tooltip.style('opacity',2);
    })
    .on('mousemove', function(d) {
        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);
    });

如何将第二个栏(工作)上的相应区域设为红色,以提醒员工工作时间过长?

答案

这可以通过使用线性渐变来实现,您可以根据计划时间的宽度设置停靠点,然后将填充应用于实际工作时间条

使用数组中第一个对象的“to”值设置渐变,假设这将始终是您的“计划”时间。渐变使用userSpaceOnUse,以便使用SVG容器中的坐标,而不是应用它的每个矩形。

let gradient = svg.append("defs")
   .append("linearGradient")
   .attr("gradientUnits", "userSpaceOnUse")
   .attr("id", "gradient")
   .attr("x1", 0)
   .attr("y1", 0)
   .attr("x2", width)
   .attr("y2", 0);

let offset = x(data[0].to) / width

gradient.append('stop').attr("offset", 0).attr("stop-color", 'green')
gradient.append('stop').attr("offset", offset).attr("stop-color", 'green')
gradient.append('stop').attr("offset", offset).attr("stop-color", 'red')
gradient.append('stop').attr("offset", 1).attr("stop-color", 'red')

然后将渐变应用为rects:

svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("y", function(d) { return y(d.action); })
.attr("height", y.rangeBand())
.attr("x", function(d) { return x(d.from); })
.attr("width", function(d) { return x(d.to)-x(d.from) })
.style("fill", function(d,i) {

if(d.action == "Worked" && d.gap == false) {
return 'url(#gradient)';
}
else if(d.action == "Worked" && d.gap == true) {
return d3.rgb("#e0e0e0");
}
else {
return d3.rgb("#00e5ff");
}

var all_moments = [];

var scheduled_start = moment("2015-03-25 08:00:00");
var scheduled_end = moment("2015-03-25 17:00:00");

var worked_start_1 =  moment("2015-03-25 08:00:00");
var worked_end_1 =  moment("2015-03-25 12:00:00");

var worked_start_2 =  moment("2015-03-25 13:00:00");
var worked_end_2 =  moment("2015-03-25 19:00:00");


all_moments.push(scheduled_start);
all_moments.push(scheduled_end);
all_moments.push(worked_start_1);
all_moments.push(worked_end_1);
all_moments.push(worked_start_2);
all_moments.push(worked_end_2);

var earliest_moment = moment.min(all_moments);
var latest_moment = moment.min(all_moments);


var data=[
{"action": "Scheduled", "tooltipTitle": "Scheduled","gap": false,"to": scheduled_end,"from": scheduled_start},
{"action": "Worked", "tooltipTitle": "Worked", "gap": false,"to": worked_end_1,"from": worked_start_1},
{"action": "Worked", "tooltipTitle": "Gap", "gap": true, "to": worked_start_2,"from": worked_end_1},
{"action": "Worked", "tooltipTitle": "Worked", "gap": false, "to": worked_end_2,"from": worked_start_2}
]

var margin = {top: 50, right: 50, bottom: 50, left: 100},
width = 900 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;

var y = d3.scale.ordinal()
.rangeRoundBands([0, height], .08);

var x = d3.time.scale()
.domain([earliest_moment,latest_moment])
.range([0,width]);

y.domain(data.map(function(d) { return d.action; }));
x.domain([d3.min(data,function(d){return d.from;}), d3.max(data,function(d){return d.to;})]);

var customTimeFormat = d3.time.format("%I:%M:%p");  

var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(10)
.tickFormat(customTimeFormat);

var yAxis = d3.svg.axis()
.scale(y)
.orient("left");

var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)

svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
  
let gradient = svg.append("defs")
   .append("linearGradient")
	.attr("gradientUnits", "userSpaceOnUse")
   .attr("id", "gradient")
   .attr("x1", 0)
   .attr("y1", 0)
   .attr("x2", width)
   .attr("y2", 0);

let offset = x(data[0].to) / width
  
gradient.append('stop').attr("offset", 0).attr("stop-color", 'green')
gradient.append('stop').attr("offset", offset).attr("stop-color", 'green')
gradient.append('stop').attr("offset", offset).attr("stop-color", 'red')
gradient.append('stop').attr("offset", 1).attr("stop-color", 'red')



svg.append("g")
.attr("class", "x axis")
.style("font", "14px times")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("x", width-75)
.attr("dx", ".71em")
.attr("dy", "-.71em");
//.text("Temperatures (C)");

svg.append("g")
.attr("class", "y axis")
.style("font", "18px times")
.style('font-family', '"Open Sans", sans-serif')
.call(yAxis);


svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("y", function(d) { return y(d.action); })
.attr("height", y.rangeBand())
.attr("x", function(d) { return x(d.from); })
.attr("width", function(d) { return x(d.to)-x(d.from) })
.style("fill", function(d,i) {

if(d.action == "Worked" && d.gap == false) {
return 'url(#gradient)';
}
else if(d.action == "Worked" && d.gap == true) {
return d3.rgb("#e0e0e0");
}
else {
return d3.rgb("#00e5ff");

}


});


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

tooltip.append('div')
.attr('class', 'tooltipTitle');
tooltip.append('div')
.attr('class', 'timeRange');

svg.selectAll(".bar")
.on('mouseover', function(d) {

//console.log(m1.format("MMM Do YY"));

tooltip.select('.tooltipTitle').html("<b>" + d.tooltipTitle + "</b>");
tooltip.select('.timeRange').html( d.from.format("LT") + " to " + d.to.format("LT"));

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

})
.on('mousemove', function(d) {
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);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/moment.js"></script>

以上是关于D3浮动条形图中的多色条形图由时间计算确定的主要内容,如果未能解决你的问题,请参考以下文章

d3.js 如何在条形图中添加线条

对条形图中的 x 轴使用序数刻度 ('d3.scale.ordinal')

使用 D3.js 将误差线添加到分组条形图中

如何使用 React Faux DOM 在 D3 分组条形图中呈现矩形?

使D3堆积条形图填充父SVG容器

ggplot2:更改条形图中每个方面的颜色