Javascript d3js多重堆叠条形图线条光标出现在错误的图表中 我正在创建两个相关的叠置条形图。两个图表都必须包含一个线条光标。顶部图表的光标工作正常,但底部图表中不会显示任何线条光标。更糟糕的是,当我将鼠标移到底部图表上时,顶部图表中的线条光标会移动。要使线条光标出现在底部图表中,并且不影响顶部图表或与顶部图表中的线条光标同步,需要固定哪些内容

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"

注意:在说我不尊重我的代码中的“不要重复你自己的原则”之前,请注意Kent Beck说:“首先,让它工作,然后让它正确,最后让它快速工作”。另外,可以跳过数据创建部分。 这是我的密码:

            
   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({