Javascript d3js多重堆叠条形图线条光标出现在错误的图表中 我正在创建两个相关的叠置条形图。两个图表都必须包含一个线条光标。顶部图表的光标工作正常,但底部图表中不会显示任何线条光标。更糟糕的是,当我将鼠标移到底部图表上时,顶部图表中的线条光标会移动。要使线条光标出现在底部图表中,并且不影响顶部图表或与顶部图表中的线条光标同步,需要固定哪些内容
注意:在说我不尊重我的代码中的“不要重复你自己的原则”之前,请注意Kent Beck说:“首先,让它工作,然后让它正确,最后让它快速工作”。另外,可以跳过数据创建部分。 这是我的密码:Javascript d3js多重堆叠条形图线条光标出现在错误的图表中 我正在创建两个相关的叠置条形图。两个图表都必须包含一个线条光标。顶部图表的光标工作正常,但底部图表中不会显示任何线条光标。更糟糕的是,当我将鼠标移到底部图表上时,顶部图表中的线条光标会移动。要使线条光标出现在底部图表中,并且不影响顶部图表或与顶部图表中的线条光标同步,需要固定哪些内容,javascript,d3.js,bar-chart,stacked-chart,stacked-area-chart,Javascript,D3.js,Bar Chart,Stacked Chart,Stacked Area Chart,注意:在说我不尊重我的代码中的“不要重复你自己的原则”之前,请注意Kent Beck说:“首先,让它工作,然后让它正确,最后让它快速工作”。另外,可以跳过数据创建部分。 这是我的密码: var app = {}; app.allBarsDatasets = [ { "xAxisTickValue": "10-1", "barValue": 17 }, { "xAxisTickValue"
var app = {};
app.allBarsDatasets = [
{
"xAxisTickValue": "10-1",
"barValue": 17
},
{
"xAxisTickValue": "10-2",
"barValue": 17
},
{
"xAxisTickValue": "10-3",
"barValue": 17
}
];
app.allBarsDatasets2 = [
[
{
"xAxisTickValue": "10-1",
"barValue": 10
},
{
"xAxisTickValue": "10-2",
"barValue": 6
},
{
"xAxisTickValue": "10-3",
"barValue": 7
}
],
[
{
"xAxisTickValue": "10-1",
"barValue": 6
},
{
"xAxisTickValue": "10-2",
"barValue": 8
},
{
"xAxisTickValue": "10-3",
"barValue": 10
}
]
];
app.allLinesDatasets =
{
"points": [
{
"x": 1,
"y": 10
},
{
"x": 2,
"y": 8
},
{
"x": 3,
"y": 14
}
],
"color": "blue"
};
app.busStopsWaitTimes = {
"1": {
"days": {
"we": {
"10-1": [
17,
14,
14,
4,
8,
13,
11,
3,
2,
14,
14,
8,
9,
1,
9,
9,
9,
17,
1,
20
],
"10-2": [
13,
12,
3,
5,
18,
14,
17,
5,
9,
12,
19,
3,
8,
9,
20,
3,
14,
5,
7,
13
],
"10-3": [
18,
8,
8,
7,
10,
20,
16,
17,
6,
13,
5,
11,
11,
14,
18,
17,
11,
17,
4,
3
]
}
},
"name": "Adderley"
}
};
app.populateBusStopsWaitSelectionForm = function () {
let stopOptions = `<option value="">Select a stop</option>`;
$.each(app.busStopsWaitTimes, function (idx, stop) {
stopOptions += `<option value={"stopId":${idx}}>${stop.name}</option>`;
});
$("#busStopAnalysis_Stops").html(stopOptions);
}
app.populateBusStopsWaitSelectionForm();
$("#busStopAnalysis_Stops").change(function() {
let values = $("#busStopAnalysis_Stops").val();
if (values !== "") {
values = JSON.parse(values);
let daysOptions = `<option value="">Select a day</option>`;
if ("we" in app.busStopsWaitTimes[values.stopId].days) {
daysOptions += `<option value={"dayKey":"we"}>Wednesday</option>`
}
$("#busStopAnalysis_Days").html(daysOptions);
} else {
$("#busStopAnalysis_Days").html("<option>Please select a route</option>");
}
});
$("#drawBusStopAnalysisChart").on("click", function (evt) {
evt.preventDefault();
const stopInfo = JSON.parse($("#busStopAnalysis_Stops").val());
const dayInfo = JSON.parse($("#busStopAnalysis_Days").val());
if (stopInfo !== "" || dayInfo !== "") {
const allBarsDatasets = [];
const allBarsDatasets2 = [[],[]]
const allLinesdatasets = [];
const linePoints = [];
let i = 1;
$.each(app.busStopsWaitTimes[stopInfo.stopId]["days"][dayInfo.dayKey], function (idx, timeslot) {
timeslot.sort(function (a,b) {
return a - b;
});
let Percentile25th = timeslot[parseInt(timeslot.length / 4)];
let Percentile50th = timeslot[parseInt(timeslot.length / 2)];
let Percentile75th = timeslot[parseInt((timeslot.length / 4) * 3)];
allBarsDatasets.push({
xAxisTickValue: idx,
barValue: Percentile75th
});
allBarsDatasets2[0].push({
xAxisTickValue: idx,
barValue: Percentile25th
});
allBarsDatasets2[1].push({
xAxisTickValue: idx,
barValue: Percentile75th - Percentile25th
});
linePoints.push({x : i, y : Percentile50th});
i++;
});
allLinesdatasets.push({points:linePoints,color:"blue"});
app.drawBusStopAnalysisOneDayChart(allBarsDatasets, allBarsDatasets2, allLinesdatasets);
}
});
app.drawBusStopAnalysisOneDayChart = function (allBarsDatasets, allBarsDatasets2, allLinesdatasets) {
app.allLinesdatasets = allLinesdatasets;
$("#busStopAnalysis_OneDayChart").html("");
var barColor = '#384a60';
// calculate total frequency by state for all segment.
// var fD = app.allBarsDatasets.map(function(d){return [d.xAxisTickValue,d.barValue];});
var fD = allBarsDatasets.map(function(d){return [d.xAxisTickValue,d.barValue];});
var margin = {top: 20, right: 100, bottom: 30, left: 100},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var padding = 100;
//create svg for histogram.
var svg = d3.select("#busStopAnalysis_OneDayChart").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 + ")");
// create function for x-axis mapping.
var x = d3.scale.ordinal().rangeRoundBands([0, width], 0.1, 0)
.domain(fD.map(function(d) { return d[0]; }));
// Add x-axis to the histogram svg.
svg.append("g").attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.svg.axis()
.scale(x)
.orient("bottom")
.innerTickSize(-height)
.outerTickSize(0)
.tickPadding(10));
// create function for y-axis mapping.
var yMin = 0;
var yMax = d3.max(fD.map(function(d) { return d[1]; }));
var y = d3.scale.linear().range([height, 0])
.domain([0, d3.max(fD, function(d) { return d[1]; })]);
var yScaleGridLines = d3.scale.linear()
.domain([0, yMax])
.range([height, 0]);
var yAxisGridLines = d3.svg.axis()
.scale(yScaleGridLines)
.orient("left")
.innerTickSize(-width)
.outerTickSize(0)
.tickPadding(10);
svg.append("g")
.attr("class", "y axis")
.call(yAxisGridLines);
// You would think d3 draws bar by bar but it draws level by level
// therefore you need to create stacks which are sub-arrays whose contents
// are arrays of elements at the same level
// to achieve that
// call stack,
// call map and iterate over each array
// call map iterate over all elements within an array while creating points based on values to visualize
var layers = d3.layout.stack() (
allBarsDatasets2.map(
function(barDataset) {
return barDataset.map(
function(d) {
return {x: d.xAxisTickValue, y:d.barValue};
}
)
}
)
);
var z = d3.scale.category10();
var layer = svg.selectAll(".layer")
.data(layers)
.enter().append("g")
.attr("class", "layer")
.style("fill", function(d, i) {
var x;
if (i === 0) {
x = "transparent";
} else {
// x = z(i);
x = "#686868";
}
return x;
});
var mouseG = d3.select("#busStopAnalysis_Charts").append("g")
.attr("class", "mouse-over-effects");
// this is the vertical line
svg.append("path")
.attr("class", "mouse-line")
.style("stroke", "black")
.style("stroke-width", "1px")
.style("opacity", "1");
var tooltip = d3.select("#busStopAnalysis_Charts")
.append('div')
.attr('id', 'tooltip');
$("#tooltip").css('display', 'none');
layer.selectAll("rect")
.data(function (d) { return d; })
.enter().append("rect")
.attr("x", function (d) { return x(d.x); })
.attr("y", function (d) { return y(d.y + d.y0); })
.attr("height", function (d) { return y(d.y0) - y(d.y + d.y0); })
.attr("width", x.rangeBand() - 1)
.on("mousemove", function (d) {
var mouse = d3.mouse(this);
// move the vertical line
d3.select(".mouse-line")
.attr("d", function() {
var d = "M" + mouse[0] + "," + height;
d += " " + mouse[0] + "," + 0;
return d;
});
})
.on("mouseover", function (d) {
var mouse = d3.mouse(this);
console.log("first chart");
console.log(mouse);
d3.select("#tooltip")
.style("left", mouse[0] + "px")
.style("top", mouse[1] + "px")
.style("width", "auto")
.style("height", "auto")
.html("Day: " + $("#busStopAnalysis_Days option:selected").text() + "<br>Time Range: " + d.x + "<br>Avg Wait: " + d.y);
$("#tooltip").css("display", "");
app.drawAllDaysStopAnalysisChart(d);
})
.on("mouseout", function() {
$("#tooltip").css("display", "none");
})
.on("click", function (d) {
app.drawAllDaysStopAnalysisChart(d);
});
// Beginning of line things drawing
// Add min and max x and y borrowed from weird lines
var xMin = app.allLinesdatasets.reduce(function(pv,cv){
var currentXMin = cv.points.reduce(function(pv,cv){
return Math.min(pv,cv.x);
},100)
return Math.min(pv,currentXMin);
},100);
var xMax = app.allLinesdatasets.reduce(function(pv,cv){
var currentXMax = cv.points.reduce(function(pv,cv){
return Math.max(pv,cv.x);
},0)
return Math.max(pv,currentXMax);
},0);
var yMin = app.allLinesdatasets.reduce(function(pv,cv){
var currentYMin = cv.points.reduce(function(pv,cv){
return Math.min(pv,cv.y);
},100)
return Math.min(pv,currentYMin);
},100);
var yMax = app.allLinesdatasets.reduce(function(pv,cv){
var currentYMax = cv.points.reduce(function(pv,cv){
return Math.max(pv,cv.y);
},0)
return Math.max(pv,currentYMax);
},0);
var yScaleGridLines = d3.scale.linear()
.domain([0, yMax])
.range([height, 0]);
var yAxisGridLines = d3.svg.axis()
.scale(yScaleGridLines)
.orient("left")
.innerTickSize(-width)
.outerTickSize(0)
.tickPadding(10);
var xScaleGridLines = {};
xScaleGridLines = d3.scale.linear()
.domain([xMin, xMax])
.range([0, width]);
var xAxisGridLines = d3.svg.axis()
.scale(xScaleGridLines)
.orient("bottom")
.innerTickSize(-height)
.outerTickSize(0)
.tickPadding(10);
var lineGridLines = d3.svg.line()
.interpolate('step-after')
.x(function(d) { return xScaleGridLines(d.x); })
.y(function(d) { return yScaleGridLines(d.y); });
$.each(app.allLinesdatasets, function (idx, dataset) {
svg.append("path")
.data([dataset.points])
.attr("class", "line")
.attr("d", lineGridLines)
.style("stroke", function(){
// return dataset.color;
return "#FF9900";
});
});
}
/********************************************************************
* Append and Draw Second Chart
********************************************************************/
app.drawAllDaysStopAnalysisChart = function drawAllDateStopAnalysis (timeRange) {
const stopInfo = JSON.parse($("#busStopAnalysis_Stops").val());
const allBarsDatasets = [];
const allBarsDatasets2 = [[],[]]
const allLinesdatasets = [];
const linePoints = [];
if (stopInfo !== "" || dayInfo !== "") {
let i = 1;
$.each(app.busStopsWaitTimes[stopInfo.stopId]["days"], function (idx, day) {
const stopRangeWaitTimeInfo = day[timeRange.x];
stopRangeWaitTimeInfo.sort(function (a,b) {
return a - b;
});
let Percentile25th = stopRangeWaitTimeInfo[parseInt(stopRangeWaitTimeInfo.length / 4)];
let Percentile50th = stopRangeWaitTimeInfo[parseInt(stopRangeWaitTimeInfo.length / 2)];
let Percentile75th = stopRangeWaitTimeInfo[parseInt((stopRangeWaitTimeInfo.length / 4) * 3)];
allBarsDatasets.push({
xAxisTickValue: idx,
barValue: Percentile75th
});
allBarsDatasets2[0].push({
xAxisTickValue: idx,
barValue: Percentile25th
});
allBarsDatasets2[1].push({
xAxisTickValue: idx,
barValue: Percentile75th - Percentile25th
});
linePoints.push({x : i, y : Percentile50th});
i++;
});
allLinesdatasets.push({points:linePoints,color:"orange"});
}
$("#busStopAnalysis_AllDaysChart").html("");
var barColor = '#384a60';
var fD = allBarsDatasets.map(function(d){return [d.xAxisTickValue,d.barValue];});
var margin = {top: 20, right: 100, bottom: 30, left: 100},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var padding = 100;
//create svg for histogram.
var svg = d3.select("#busStopAnalysis_AllDaysChart").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 + ")");
// create function for x-axis mapping.
var x = d3.scale.ordinal().rangeRoundBands([0, width], 0.1, 0)
.domain(fD.map(function(d) { return d[0]; }));
// Add x-axis to the histogram svg.
svg.append("g").attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.svg.axis()
.scale(x)
.orient("bottom")
.innerTickSize(-height)
.outerTickSize(0)
.tickPadding(10));
// create function for y-axis mapping.
var yMin = 0;
var yMax = d3.max(fD.map(function(d) { return d[1]; }));
var y = d3.scale.linear().range([height, 0])
.domain([0, d3.max(fD, function(d) { return d[1]; })]);
var yScaleGridLines = d3.scale.linear()
.domain([yMin, yMax])
.range([height, 0]);
var yAxisGridLines = d3.svg.axis()
.scale(yScaleGridLines)
.orient("left")
.innerTickSize(-width)
.outerTickSize(0)
.tickPadding(10);
svg.append("g")
.attr("class", "y axis")
.call(yAxisGridLines);
var layers = d3.layout.stack() (
allBarsDatasets2.map(
function(barDataset) {
return barDataset.map(
function(d) {
return {x: d.xAxisTickValue, y:d.barValue};
}
)
}
)
);
var z = d3.scale.category10();
var layer = svg.selectAll(".layer")
.data(layers)
.enter().append("g")
.attr("class", "layer")
.style("fill", function(d, i) {
var x;
if (i === 0) {
x = "transparent";
} else {
x = "#FF9900";
}
return x;
});
// append a g for all the mouse over nonsense
var mouseG = svg.append("g")
.attr("class", "mouse-over-effects");
// this is the vertical line
svg.append("path")
.attr("class", "mouse-line")
.style("stroke", "grey")
.style("stroke-width", "1px")
.style("opacity", "1");
var tooltip = d3.select("#tooltip");
$("#tooltip").css('display', 'none');
layer.selectAll("rect")
.data(function (d) { return d; })
.enter().append("rect")
.attr("x", function (d) { return x(d.x); })
.attr("y", function (d) { return y(d.y + d.y0); })
.attr("height", function (d) { return y(d.y0) - y(d.y + d.y0); })
.attr("width", x.rangeBand() - 1)
.on("mousemove", function (d) {
var mouse = d3.mouse(this);
// move the vertical line
d3.select(".mouse-line")
.attr("d", function() {
var d = "M" + mouse[0] + "," + height;
d += " " + mouse[0] + "," + 0;
return d;
});
})
.on("mouseover", function (d) {
var mouse = d3.mouse(this);
console.log("second chart");
console.log(mouse);
tooltip.style("left", mouse[0] + "px")
.style("top", mouse[1] + "px")
.style("width", "auto")
.style("height", "auto")
.html("Day: " + $("#busStopAnalysis_Days option:selected").text() + "<br>Time Range: " + d.x + "<br>Avg Wait: " + d.y);
$("#tooltip").css("display", "");
})
.on("mouseout", function() {
$("#tooltip").css("display", "none");
});
// Beginning of line things drawing
// Add min and max x and y borrowed from weird lines
var xMin = app.allLinesdatasets.reduce(function(pv,cv){
var currentXMin = cv.points.reduce(function(pv,cv){
return Math.min(pv,cv.x);
},100)
return Math.min(pv,currentXMin);
},100);
var xMax = allLinesdatasets.reduce(function(pv,cv){
var currentXMax = cv.points.reduce(function(pv,cv){
return Math.max(pv,cv.x);
},0)
return Math.max(pv,currentXMax);
},0);
var yMin = allLinesdatasets.reduce(function(pv,cv){
var currentYMin = cv.points.reduce(function(pv,cv){
return Math.min(pv,cv.y);
},100)
return Math.min(pv,currentYMin);
},100);
var yMax = allLinesdatasets.reduce(function(pv,cv){
var currentYMax = cv.points.reduce(function(pv,cv){
return Math.max(pv,cv.y);
},0)
return Math.max(pv,currentYMax);
},0);
var yScaleGridLines = d3.scale.linear()
.domain([0, yMax])
.range([height, 0]);
var yAxisGridLines = d3.svg.axis()
.scale(yScaleGridLines)
.orient("left")
.innerTickSize(-width)
.outerTickSize(0)
.tickPadding(10);
var xScaleGridLines = {};
xScaleGridLines = d3.scale.linear()
.domain([xMin, xMax])
.range([0, width]);
var xAxisGridLines = d3.svg.axis()
.scale(xScaleGridLines)
.orient("bottom")
.innerTickSize(-height)
.outerTickSize(0)
.tickPadding(10);
var lineGridLines = d3.svg.line()
.interpolate('step-after')
.x(function(d) { return xScaleGridLines(d.x); })
.y(function(d) { return yScaleGridLines(d.y); });
$.each(app.allLinesdatasets, function (idx, dataset) {
svg.append("path")
.data([dataset.points])
.attr("class", "line")
.attr("d", lineGridLines)
.style("stroke", function(){
// return dataset.color;
return "#FF3300";
});
});
}
问题是我将线条光标作为一个同名类附加到两个图表上。我猜d3很难识别我要制作的动画,所以它总是选择第一个。
修复方法是用ID替换类,如下面的代码片段所示
var-app={};
app.allBarsDatasets=[
{
“xAxisTickValue”:“10-1”,
“barValue”:17
},
{
“xAxisTickValue”:“10-2”,
“barValue”:17
},
{
“xAxisTickValue”:“10-3”,
“barValue”:17
}
];
app.allBarsDatasets2=[
[
{
“xAxisTickValue”:“10-1”,
“barValue”:10
},
{
“xAxisTickValue”:“10-2”,
“barValue”:6
},
{
“xAxisTickValue”:“10-3”,
“barValue”:7
}
],
[
{
“xAxisTickValue”:“10-1”,
“barValue”:6
},
{
“xAxisTickValue”:“10-2”,
“barValue”:8
},
{
“xAxisTickValue”:“10-3”,
“barValue”:10
}
]
];
app.allLinesDatasets=
{
“要点”:[
{
“x”:1,
“y”:10
},
{
“x”:2,
“y”:8
},
{
“x”:3,
“y”:14
}
],
“颜色”:“蓝色”
};
app.busStopsWaitTimes={
"1": {
“天”:{
“我们”:{
"10-1": [
17,
14,
14,
4.
8.
13,
11,
3.
2.
14,
14,
8.
9,
1.
9,
9,
9,
17,
1.
20
],
"10-2": [
13,
12,
3.
5.
18,
14,
17,
5.
9,
12,
19,
3.
8.
9,
20,
3.
14,
5.
7.
13
],
"10-3": [
18,
8.
8.
7.
10,
20,
16,
17,
6.
13,
5.
11,
11,
14,
18,
17,
11,
17,
4.
3.
]
}
},
“名称”:“Adderley”
}
};
app.populateBusStopsWaitSelectionForm=函数(){
let stopOptions=`选择一个停止点';
$.each(app.busStopsWaitTimes,function(idx,stop){
stopOptions+=`${stop.name}`;
});
$(“#busStopAnalysis_Stops”).html(stopOptions);
}
app.populateBusStopsWaitSelectionForm();
$(“#busStopAnalysis_Stops”)。更改(函数(){
让值=$(“#busStopAnalysis_Stops”).val();
如果(值!==“”){
values=JSON.parse(值);
let daysOptions=`选择一天';
如果(app.busStopsWaitTimes[values.stopId].days中的“我们”){
daysOptions+=`星期三`
}
$(“#BustopAnalysis_Days”).html(daysOptions);
}否则{
$(“#busStopAnalysis_Days”).html(“请选择路线”);
}
});
$(#drawBusStopAnalysisChart”)。在(“单击”上,函数(evt){
evt.preventDefault();
const stopInfo=JSON.parse($(“#BustopAnalysis_Stops”).val();
const dayInfo=JSON.parse($(“#BustopAnalysis_Days”).val();
如果(stopInfo!==“”| | dayInfo!==“”){
常量allBarsDatasets=[];
常量allBarsDatasets2=[],[]
常量allLinesdatasets=[];
常量线点=[];
设i=1;
$.each(app.busStopsWaitTimes[stopInfo.stopId][“days”][dayInfo.dayKey],函数(idx,时隙){
时隙.排序(函数(a,b){
返回a-b;
});
设Percentile25=时隙[parseInt(timeslot.length/4)];
设百分位数50=时隙[parseInt(timeslot.length/2)];
设Percentile75th=时隙[parseInt((timeslot.length/4)*3];
allBarsDatasets.push({