我的分组条形图如下所示,带有刻度线。
$(document).ready(function() {
render_chart();
});
function render_chart() {
var dataset = {
"colors": [
"#1f77b4",
"#ffbb78",
"#e74c3c",
"#2ecc71",
"#aec7e8",
"#ff7f0e",
"#2ca02c",
"#98df8a",
"#9467bd",
"#3498db",
],
"operators": [
"Client1",
"Client2",
"Client3",
"Client4"
],
"days": [
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday"
],
"schedules": [{
"fromHours": "00:00:00",
"toHours": "08:00:00",
"operator": "Client1",
"day": "Monday",
"color": "#1f77b4"
},
{
"fromHours": "00:00:00",
"toHours": "08:00:00",
"operator": "Client2",
"day": "Monday",
"color": "#ffbb78"
},
{
"fromHours": "08:00:00",
"toHours": "14:00:00",
"operator": "Client2",
"day": "Monday",
"color": "#ffbb78"
},
{
"fromHours": "14:00:00",
"toHours": "18:00:00",
"operator": "Client3",
"day": "Monday",
"color": "#e74c3c"
},
{
"fromHours": "18:00:00",
"toHours": "23:59:59",
"operator": "Client4",
"day": "Monday",
"color": "#2ecc71"
},
{
"fromHours": "02:00:00",
"toHours": "16:00:00",
"operator": "Client4",
"day": "Tuesday",
"color": "#2ecc71"
},
{
"fromHours": "16:00:00",
"toHours": "20:00:00",
"operator": "Client3",
"day": "Tuesday",
"color": "#e74c3c"
},
{
"fromHours": "16:00:00",
"toHours": "23:59:59",
"operator": "Client1",
"day": "Tuesday",
"color": "#1f77b4"
},
{
"fromHours": "00:00:00",
"toHours": "17:00:00",
"operator": "Client4",
"day": "Wednesday",
"color": "#2ecc71"
},
{
"fromHours": "17:00:00",
"toHours": "23:59:59",
"operator": "Client3",
"day": "Wednesday",
"color": "#e74c3c"
}
]
};
var schedulesDataset = dataset["schedules"];
var daysDataset = dataset["days"];
dataset.layers = new Array();
var mondayData = new Array();
var tuesdayData = new Array();
var wednesdayData = new Array();
var thursdayData = new Array();
var fridayData = new Array();
var saturdayData = new Array();
var sundayData = new Array();
schedulesDataset.forEach(function(schedule) {
switch (schedule.day) {
case "Monday":
mondayData.push({
"fromHours": schedule.fromHours,
"toHours": schedule.toHours,
"operator": schedule.operator,
"day": schedule.day,
"color": schedule.color
});
break;
case "Tuesday":
tuesdayData.push({
"fromHours": schedule.fromHours,
"toHours": schedule.toHours,
"operator": schedule.operator,
"day": schedule.day,
"color": schedule.color
});
break;
case "Wednesday":
wednesdayData.push({
"fromHours": schedule.fromHours,
"toHours": schedule.toHours,
"operator": schedule.operator,
"day": schedule.day,
"color": schedule.color
});
break;
case "Thursday":
thursdayData.push({
"fromHours": schedule.fromHours,
"toHours": schedule.toHours,
"operator": schedule.operator,
"day": schedule.day,
"color": schedule.color
});
break;
case "Friday":
fridayData.push({
"fromHours": schedule.fromHours,
"toHours": schedule.toHours,
"operator": schedule.operator,
"day": schedule.day,
"color": schedule.color
});
break;
case "Saturday":
saturdayData.push({
"fromHours": schedule.fromHours,
"toHours": schedule.toHours,
"operator": schedule.operator,
"day": schedule.day,
"color": schedule.color
});
break;
case "Sunday":
sundayData.push({
"fromHours": schedule.fromHours,
"toHours": schedule.toHours,
"operator": schedule.operator,
"day": schedule.day,
"color": schedule.color
});
break;
}
});
dataset.layers.push(mondayData);
dataset.layers.push(tuesdayData);
dataset.layers.push(wednesdayData);
dataset.layers.push(thursdayData);
dataset.layers.push(fridayData);
dataset.layers.push(saturdayData);
dataset.layers.push(sundayData);
var numberOfOperators = dataset["operators"].length;
var today = new Date();
today.setHours(0, 0, 0, 0);
todayMillis = today.getTime();
var layersData = dataset["layers"];
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));
})
});
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";
}
var parsedInputValue = inputValue.toString();
if (parsedInputValue.length === 1) {
return "0" + inputValue;
}
return inputValue;
}
var offsetWidth = 1000;
var margin = {
top: 50,
right: 50,
bottom: 50,
left: 100
},
width = offsetWidth - margin.left - margin.right,
height = 600 - 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 xScale = d3.scaleTime()
.domain([new Date(), new Date()])
.nice(d3.timeDay, 1)
.range([0, width - margin.left]);
var yScale = d3.scaleBand()
.domain(dataset["days"])
.rangeRound([0, height])
.padding(.08);
var xAxis = d3.axisBottom(xScale)
.ticks(24)
.tickSize(-height)
.tickFormat(d3.timeFormat("%H"));
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) {
return d;
})
.enter()
.append("rect")
.transition()
.duration(500)
.delay(function(d, i) {
return i * 100;
})
.attr("y", function(d, i) {
return yScale(d.day) + yScale.bandwidth() / 7 * i;
})
.attr("height", yScale.bandwidth() / 7)
.transition()
.attr("x", function(d) {
return xScale(d.fromHours)
})
.attr("width", function(d) {
return xScale(Math.abs(d.toHours)) - xScale(Math.abs(d.fromHours));
})
.attr("class", "bar")
.style("fill", function(d, i) {
return d.color;
});
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 * (offsetWidth / 10)) + (width / numberOfOperators);
})
.attr("y", function(d, i) {
return height + 30;
})
.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 * (offsetWidth / 10)) + (width / numberOfOperators) + 12;
})
.attr("y", function(d, i) {
return height + 40;
})
.text(function(d) {
return d;
});
var tooltip = d3.select("body")
.append('div')
.attr('class', 'tooltip');
tooltip.append('div')
.attr('class', 'operator');
tooltip.append('div')
.attr('class', 'timeRange');
svg.selectAll("rect")
.on('mouseover', function(d, i) {
if (!d.day) return null;
tooltip.select('.operator').html("<b>" + d.operator + "</b>");
tooltip.select('.timeRange').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.day) 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>
由于某些条形图不是从0位置开始,因此它看起来不像是一个组。我想将这些y轴刻度线向上移动half of the distance between current tick position and the next tick position
,因此分组的条形显示在2条黑线内。我认为在这种情况下看起来会更好,也许我们可以用一些css来区分每个相邻的组。
我不知道该怎么做,所以我四处寻找并没有找到类似的东西。有可能实现吗?
非常感谢任何帮助:)
答案 0 :(得分:1)
这些线对应于轴'刻度线,它们由轴生成器自动创建和定位。
如果没有太多的重构或自定义,您可以在创建它们之后将其移动到:
d3.selectAll(".y.axis .tick line").each(function(){
d3.select(this)
.attr("transform", "translate(0," + yScale.bandwidth()/2 + ")")
});
以下是具有该更改的代码:
$(document).ready(function() {
render_chart();
});
function render_chart() {
var dataset = {
"colors": [
"#1f77b4",
"#ffbb78",
"#e74c3c",
"#2ecc71",
"#aec7e8",
"#ff7f0e",
"#2ca02c",
"#98df8a",
"#9467bd",
"#3498db",
],
"operators": [
"Client1",
"Client2",
"Client3",
"Client4"
],
"days": [
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday"
],
"schedules": [{
"fromHours": "00:00:00",
"toHours": "08:00:00",
"operator": "Client1",
"day": "Monday",
"color": "#1f77b4"
},
{
"fromHours": "00:00:00",
"toHours": "08:00:00",
"operator": "Client2",
"day": "Monday",
"color": "#ffbb78"
},
{
"fromHours": "08:00:00",
"toHours": "14:00:00",
"operator": "Client2",
"day": "Monday",
"color": "#ffbb78"
},
{
"fromHours": "14:00:00",
"toHours": "18:00:00",
"operator": "Client3",
"day": "Monday",
"color": "#e74c3c"
},
{
"fromHours": "18:00:00",
"toHours": "23:59:59",
"operator": "Client4",
"day": "Monday",
"color": "#2ecc71"
},
{
"fromHours": "02:00:00",
"toHours": "16:00:00",
"operator": "Client4",
"day": "Tuesday",
"color": "#2ecc71"
},
{
"fromHours": "16:00:00",
"toHours": "20:00:00",
"operator": "Client3",
"day": "Tuesday",
"color": "#e74c3c"
},
{
"fromHours": "16:00:00",
"toHours": "23:59:59",
"operator": "Client1",
"day": "Tuesday",
"color": "#1f77b4"
},
{
"fromHours": "00:00:00",
"toHours": "17:00:00",
"operator": "Client4",
"day": "Wednesday",
"color": "#2ecc71"
},
{
"fromHours": "17:00:00",
"toHours": "23:59:59",
"operator": "Client3",
"day": "Wednesday",
"color": "#e74c3c"
}
]
};
var schedulesDataset = dataset["schedules"];
var daysDataset = dataset["days"];
dataset.layers = new Array();
var mondayData = new Array();
var tuesdayData = new Array();
var wednesdayData = new Array();
var thursdayData = new Array();
var fridayData = new Array();
var saturdayData = new Array();
var sundayData = new Array();
schedulesDataset.forEach(function(schedule) {
switch (schedule.day) {
case "Monday":
mondayData.push({
"fromHours": schedule.fromHours,
"toHours": schedule.toHours,
"operator": schedule.operator,
"day": schedule.day,
"color": schedule.color
});
break;
case "Tuesday":
tuesdayData.push({
"fromHours": schedule.fromHours,
"toHours": schedule.toHours,
"operator": schedule.operator,
"day": schedule.day,
"color": schedule.color
});
break;
case "Wednesday":
wednesdayData.push({
"fromHours": schedule.fromHours,
"toHours": schedule.toHours,
"operator": schedule.operator,
"day": schedule.day,
"color": schedule.color
});
break;
case "Thursday":
thursdayData.push({
"fromHours": schedule.fromHours,
"toHours": schedule.toHours,
"operator": schedule.operator,
"day": schedule.day,
"color": schedule.color
});
break;
case "Friday":
fridayData.push({
"fromHours": schedule.fromHours,
"toHours": schedule.toHours,
"operator": schedule.operator,
"day": schedule.day,
"color": schedule.color
});
break;
case "Saturday":
saturdayData.push({
"fromHours": schedule.fromHours,
"toHours": schedule.toHours,
"operator": schedule.operator,
"day": schedule.day,
"color": schedule.color
});
break;
case "Sunday":
sundayData.push({
"fromHours": schedule.fromHours,
"toHours": schedule.toHours,
"operator": schedule.operator,
"day": schedule.day,
"color": schedule.color
});
break;
}
});
dataset.layers.push(mondayData);
dataset.layers.push(tuesdayData);
dataset.layers.push(wednesdayData);
dataset.layers.push(thursdayData);
dataset.layers.push(fridayData);
dataset.layers.push(saturdayData);
dataset.layers.push(sundayData);
var numberOfOperators = dataset["operators"].length;
var today = new Date();
today.setHours(0, 0, 0, 0);
todayMillis = today.getTime();
var layersData = dataset["layers"];
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));
})
});
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";
}
var parsedInputValue = inputValue.toString();
if (parsedInputValue.length === 1) {
return "0" + inputValue;
}
return inputValue;
}
var offsetWidth = 1000;
var margin = {
top: 50,
right: 50,
bottom: 50,
left: 100
},
width = offsetWidth - margin.left - margin.right,
height = 600 - 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 xScale = d3.scaleTime()
.domain([new Date(), new Date()])
.nice(d3.timeDay, 1)
.range([0, width - margin.left]);
var yScale = d3.scaleBand()
.domain(dataset["days"])
.rangeRound([0, height])
.padding(.08);
var xAxis = d3.axisBottom(xScale)
.ticks(24)
.tickSize(-height)
.tickFormat(d3.timeFormat("%H"));
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) {
return d;
})
.enter()
.append("rect")
.transition()
.duration(500)
.delay(function(d, i) {
return i * 100;
})
.attr("y", function(d, i) {
return yScale(d.day) + yScale.bandwidth() / 7 * i;
})
.attr("height", yScale.bandwidth() / 7)
.transition()
.attr("x", function(d) {
return xScale(d.fromHours)
})
.attr("width", function(d) {
return xScale(Math.abs(d.toHours)) - xScale(Math.abs(d.fromHours));
})
.attr("class", "bar")
.style("fill", function(d, i) {
return d.color;
});
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.select("g")
.attr("class", "y axis")
.call(yAxis);
d3.selectAll(".y.axis .tick line").each(function(){
d3.select(this).attr("transform", "translate(0," + yScale.bandwidth()/2 + ")")
});
var legend = svg.append("g")
.attr("class", "legend")
legend.selectAll('text')
.data(dataset["operators"])
.enter()
.append("rect")
.attr("x", function(d, i) {
return (i * (offsetWidth / 10)) + (width / numberOfOperators);
})
.attr("y", function(d, i) {
return height + 30;
})
.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 * (offsetWidth / 10)) + (width / numberOfOperators) + 12;
})
.attr("y", function(d, i) {
return height + 40;
})
.text(function(d) {
return d;
});
var tooltip = d3.select("body")
.append('div')
.attr('class', 'tooltip');
tooltip.append('div')
.attr('class', 'operator');
tooltip.append('div')
.attr('class', 'timeRange');
svg.selectAll("rect")
.on('mouseover', function(d, i) {
if (!d.day) return null;
tooltip.select('.operator').html("<b>" + d.operator + "</b>");
tooltip.select('.timeRange').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.day) 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>
如果你采用这种方法,最后一个(星期日)线看起来不会很好......但你可以简单地删除它。