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浮动条形图中的多色条形图由时间计算确定的主要内容,如果未能解决你的问题,请参考以下文章
对条形图中的 x 轴使用序数刻度 ('d3.scale.ordinal')