我已经使用D3创建了一个多折线图。绘制线条后,我在鼠标事件上添加了缩放和平移功能。我面临的问题是X轴正在重新缩放,但数据丢失并且无法与X轴一起正确缩放。我认为,我正在犯一些愚蠢的错误。任何帮助将不胜感激。
if (!window.lineChart) window.lineChart = {}
var xAxisForAllChart = {};
var xAxisData = {}
var yAxisTest = {}
//Hello test
lineChart = function (graphContainer) {
// set the dimensions and margins of the graph
var margin = graphContainer.margin,
padding = graphContainer.padding,
width = graphContainer.size.width - margin.left - margin.right,
height = graphContainer.size.height - 40;
// CONVERT LIST INTO ARRAY
variableslist = graphContainer.graphVariableList.split(",");
var $this ='';
// ADD DATE VARIBLE TO ARRAY
variableslist.push("Date");
var lineChartType = graphContainer.containerDisplayType;
if( typeof(lineChartType) == 'undefined'){
lineChartType = 'quadratic';
}
var typeOfLineChart = {"quadratic":d3.curveStep, "split":d3.curveLinear, "scatter":d3.curveLinear, "default":d3.curveStep};
var y2AxisVariablesList = graphContainer.y2LineVariableData;
for(var key in graphContainer.y2LineVariableData) {
this['y2'+key] = d3.scaleLinear()
.domain([y2AxisVariablesList[key].min, y2AxisVariablesList[key].max])
.range([height, 11]);
this['valueline'+key] ='';
width = width;
padding.right = padding.right + 40;
}
$this = this;
// set the ranges
var x = d3.scaleUtc().range([0, width]);
// stored for further opertaion of synchline
xAxisForAllChart[graphContainer.id] = x;
//for zooming and panning
var data = graphContainer.data
var xExtent = d3.extent(data, function(d, i) { return d.Date; });
var x2 = d3.scaleUtc().range([width, 0]);
var xOrigScale = d3.scaleUtc()
.domain([ new Date(xExtent[0]), new Date(xExtent[1]) ])
.range([0, width]);
var xScale = xOrigScale.copy();
var y = d3.scaleLinear()
.domain([graphContainer.yAxisData.y1Data.min, graphContainer.yAxisData.y1Data.max])
.range([height, 11]);
var line2 = '';
var line1 = '';
var y2Lines = '';
var paths = {};
var lines = {};
Object.keys(typeOfLineChart).forEach(function(daCurve) {
if(daCurve == lineChartType) {
line1 = d3.line().curve(typeOfLineChart[lineChartType])
.x(function(d) {
return x( Date.parse(d.Date));
})
.y(function(d) {
var line1GraphVariableIdValue = parseFloat(d[this["graphVariableId"]]);
if(isNaN(line1GraphVariableIdValue)){
return y(graphContainer.yAxisData.y1Data.min);
} else if(line1GraphVariableIdValue < graphContainer.yAxisData.y1Data.min) {
return y(graphContainer.yAxisData.y1Data.min);
}else {
return y(line1GraphVariableIdValue);
}
});
for(var key in y2AxisVariablesList) {
$this['valueline'+key] = d3.line().curve(typeOfLineChart[lineChartType])
.x(function(d) { return x( Date.parse(d.Date)); })
.y(function(d) {
var line2GraphVariableIdValue = parseFloat(d[this["graphVariableId"]]);
if(this['y2lines'] == this["graphVariableId"]) {
y2Lines = $this['y2'+this["graphVariableId"]];
} else {
return y(line1GraphVariableIdValue);
}
});
for(var key in y2AxisVariablesList) {
$this['valueline'+key] = d3.line().curve(typeOfLineChart[lineChartType])
.x(function(d) { return x( Date.parse(d.Date)); })
.y(function(d) {
var line2GraphVariableIdValue = parseFloat(d[this["graphVariableId"]]);
if(this['y2lines'] == this["graphVariableId"]) {
y2Lines = $this['y2'+this["graphVariableId"]];
} else {
y2Lines = $this['y2'+this['y2lines']];
}
if(isNaN(line2GraphVariableIdValue))
{
return y2Lines(y2AxisVariablesList[this["graphVariableId"]].min);
} else {
return y2Lines(line2GraphVariableIdValue);
}
});
}
}
}
});
// append the svg obgect to the body of the page
// appends a 'group' element to 'svg'
// moves the 'group' element to the top left margin
var svg = d3.select("#"+graphContainer.id).append("svg")
.attr('class', "graph")
.attr('width', width + padding.left + padding.right)
.attr('height', height + padding.bottom + padding.top)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.attr('mirrorXAxis' , graphContainer.mirrorXAxis)
.style("padding-left", (padding.left - 30));
var linesG = svg.append("g")
.attr("class", "movingTag")
var circleG = svg.append("g")
.attr("class", "movingCircleTag")
// data pass to draw function to plot the lines
draw(graphContainer.data);
var getAllGElements = $('#'+graphContainer.id+' svg .movingTag');
var getAllCircleElements = $('#'+graphContainer.id+' svg .movingCircleTag');
$.each( getAllGElements, function( i, l ){
if(lineChartType == 'scatter'){
moveDown(getAllCircleElements[i],(getAllCircleElements.length -1)- i)
}else{
moveDown(getAllGElements[i],(getAllGElements.length -1)- i)
}
});
//MOVE HTML ELEMENT DOWN
function moveDown(element,index) {
for (var i = 0; i < index; i++) {
if(element.nextElementSibling)
element.parentNode.insertBefore(element.nextElementSibling, element);
}
}
//MOVE HTML ELEMENT UP
function moveUp(element,index) {
for (var i = 0; i < index; i++) {
if(element.previousElementSibling)
element.parentNode.insertBefore(element, element.previousElementSibling);
}
}
function draw(data) {
// DRAW X AND X2 DOMAIN IN ASCENDING ORDER
x.domain(d3.extent(data, function(d) { return Date.parse(d.Date); }));
x2.domain(d3.extent(data, function(d) { return Date.parse(d.Date); }));
// Scale the range of the data IN DESCENDING ORDER
if(graphContainer.mirrorXAxis == true) {
x.domain(d3.extent(data, function(d) { return Date.parse(d.Date); }).reverse());
x2.domain(d3.extent(data, function(d) { return Date.parse(d.Date); }).reverse());
}
variableslist.forEach(function(variablevalue,key) {
$this['DataLine'+variablevalue] = [];
data.forEach(function(datavalue,key) {
for (var i in datavalue){
if(variablevalue == i && i != 'Date'){
t = {'Date':datavalue['Date']};
t[i] = datavalue[i];
$this['DataLine'+variablevalue].push(t)
}
}
});
});
variableslist.forEach(function(value,key) {
if(value != 'Date')
{
this['graphVariableId'] = value;
this['y2lines'] = value;
if(graphContainer.y2AxisVariables.indexOf(value) != -1) {
plottingLine = $this['valueline'+value];
yaxisLine = $this['y2'+value];
if(typeof(graphContainer.y2LineVariableData[value]) == 'undefined') {
yAxisSameLines = Object.keys(graphContainer.y2LineVariableData);
plottingLine = $this['valueline'+yAxisSameLines[0]];
yaxisLine = $this['y2'+yAxisSameLines[0]];
this['y2lines'] = yAxisSameLines[0];
}
} else {
plottingLine = line1;
yaxisLine = y;
}
// lines[value]= line1;
// Add the scatterplot on y axis
if(lineChartType == 'scatter') {
var Line_chart = svg.append('g').attr('class','movingCircleTag').attr('id','containerId'+graphContainer.containerid+'Circle'+value).selectAll("dot")
.data($this['DataLine'+value])
.enter().append("circle")
.attr("transform", "translate(45,0)")
.style("fill", graphContainer.colourList[value])
.attr("r", 2)
.attr("cx", function(d) {
return x(Date.parse(d['Date']));})
.attr("cy", function(d) {
if(typeof(d[value]) == 'undefined')
{ // checking whether the variable in of first or secound line
if((value).includes(y2AxisVariablesList)) {
return yaxisLine(y2AxisVariablesList[value].min);
} else {
return yaxisLine(graphContainer.yAxisData.y1Data.min)
}
} else{
return yaxisLine(d[value]);
}
})
// paths['containerId'+graphContainer.containerid+'Circle'+value] = Line_chart;
} else {
var Line_chart = linesG.append("path")
.data([$this['DataLine'+value]]) // Adding separate data for each line
.attr("id",'containerId'+graphContainer.containerid+'line'+value)
.attr("class", "line group")
.attr("transform", "translate(45,0)")
.style("stroke", graphContainer.colourList[value])
.attr("d", plottingLine)
lines['containerId'+graphContainer.containerid+'line'+value]= plottingLine;
paths['containerId'+graphContainer.containerid+'line'+value] = Line_chart;
}
}
});
var legend = d3.select('#'+graphContainer.legend)
.attr("class", "legends sortableLegendList")
var legenG = legend.selectAll("div")
.data(variableslist.map(function(d) { return d;}))
.enter()
.append("div")
.attr("containerId",graphContainer.containerid)
.attr("id", function(d, i) { if(d != "Date") { return graphContainer['id']+d; } })
.attr("variableSource", function(d, i) { if(d != "Date") {return d; } })
.attr("containerId",function(d, i) { if(d != "Date") {return graphContainer.containerid; } })
.append("span");
legenG.append("span")
.attr('class', 'legendColor')
.attr("x", 0)
.attr("width", 20)
.attr("height", 20)
.style("background-color", function(d, i) { if(d != "Date") {return graphContainer.colourList[d]; } });
legenG.append("text")
.attr("x", 25)
.attr("y", 9.5)
.attr("dy", "0.32em")
.attr("id", function(d, i) { if(d != "Date") {return graphContainer.id+d; } })
.text(function(d, i) { if(d != "Date") { return d; } });
var clipPath = svg.append('clipPath')
.attr('id', 'clip'+graphContainer.containerid)
.append('rect')
.attr('x', 40)
.attr('y', 0)
.attr('height', 240)
.attr('width', 500);
svg.append("rect")
.attr("class", "overlay")
.attr("width", 500)
.attr("height", 240)
.style('cursor', 'move')
.attr('clip-path', 'url(#clip'+graphContainer.containerid+')');
// Add the Y2 Axis
if(graphContainer.y2Axis == true) {
var i = 0;
for(var key in graphContainer.y2LineVariableData) {
svg.append("g")
.attr("class", "y2-axis "+key+" axis line")
.attr("transform", "translate(" + (width + i + 35) + ",0)")
.call(d3.axisRight($this['y2'+key]).tickFormat(d3.format("d")).ticks(11).tickSize(0).tickPadding(15));
i = i+40;
}
}
// Add the X Axis
var xAxis = svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(45,"+ (height) +")")
.call(d3.axisBottom(x));
xAxisData[graphContainer.id] = xAxis
// Add the Y1 Axis
var yAxis = svg.append("g")
.attr("class", "y-axis axis line")
.attr("transform", "translate(45,0)")
.call(d3.axisLeft(y).tickFormat(d3.format("d")).tickSize(-width + 5).tickPadding(10));
yAxisTest[graphContainer.id] = yAxis
}
var activeLine = ''
variableslist.forEach(function(value,key) {
$("#"+graphContainer.id+value).on("click", function() {
// Determine if current line is visible
attributeValue = $(this).attr("id");
// Check the opacity of line
var active = activeLine ? false : true
newOpacity = active ? 0 : 1;
// Hide or show the elements
d3.select('#containerId'+graphContainer.containerid+'line'+value).style("opacity", newOpacity);
d3.select('#containerId'+graphContainer.containerid+'Circle'+value).style("opacity", newOpacity);
// Update whether or not the elements are active
// Set value for hide and show line
activeLine = active;
})
});
var zoom = d3.zoom()
.scaleExtent([1, 10])
.on('zoom', zoomed);
svg.call(zoom);
function zoomed() {
xScale = d3.event.transform.rescaleX(xOrigScale);
var xAxis = d3.axisBottom(x)
xAxisData[graphContainer.id].call(xAxis.scale(xScale));
var ContainerID = (this.parentNode.id).slice(9, -1)
variableslist.forEach(function(value,key) {
if(value != 'Date')
{
var line = lines['containerId'+ContainerID+'line'+value];
var path = paths['containerId'+ContainerID+'line'+value];
path.attr("d", line(data));
path.attr('clip-path', 'url(#clip'+ContainerID+')');
}
});
}
}
var jsonData = [{Date: "February, 14 2019 03:35:53 +0530", BCCH: "972"},
{Date: "February, 14 2019 03:35:51 +0530", BCCH: "972"},
{Date: "February, 14 2019 03:35:50 +0530", BCCH: "972"},
{Date: "February, 14 2019 03:35:49 +0530", BCCH: "972", RxLev: "-33"},
{Date: "February, 14 2019 03:35:48 +0530", BCCH: "972", RxLev: "-33"},
{Date: "February, 14 2019 03:35:47 +0530", BCCH: "972", RxLev: "-33"},
{Date: "February, 14 2019 03:35:46 +0530", BCCH: "972", RxLev: "-33"}]
var graphVariableList = 'BCCH,RxLev';
var objectname = "linechart";
var objectId = "linechart4211";
var containerid = "421";
var margin = {top: 10, right: 0, bottom: 100, left: 10};
var padding = {top:10, right:50, bottom: 50, left:50};
var size = {height: 300,width: 400};
var displayY2Variable = false;
var interval = 10;
var mirrorXAxis = 1;
var y1Data = {min: 950, max: 974};
var colourList = {BCCH: "#1ef25d", RxLev: "#f2911e"};
var containerDisplayType = "quadratic";
var y2LineVariableData = {RxLev: {min: -110, max: -20}};
var y2Line = '';
var y2Data = {min: -110, max: -20};
var y2AxisVariableList = "RxLev";
var yAxisData = {y1Data, y2Data};
var thresholdLine = {};
var graphContainer= {id:objectId,legend:objectId+"Legends", size, margin, padding, yAxisData,data:jsonData,graphVariableList:graphVariableList,colourList, y2Axis:displayY2Variable,y2AxisVariables:y2AxisVariableList,thresholdLine:thresholdLine,containerid:containerid,mirrorXAxis:mirrorXAxis,objectname:objectname,interval:interval,containerDisplayType:containerDisplayType,y2Line:y2Line,y2LineVariableData:y2LineVariableData};
new lineChart(graphContainer);
.graph .group {
fill: none;
stroke: black;
stroke-width: 1.5;
}
.overlay {
fill: none;
pointer-events: all;
}
.graph .axis .tick line {
stroke: #cbcbcc;
opacity: 0.3;
font-size: 1.2em;
}
.graph .axis .domain {
fill: none;
stroke: #6c6c75;
opacity: 0;
cursor: move;
pointer-events: all;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.1.0/d3.min.js"></script>
<div class="graph" graphid="linechart4211" containerid="421">
<div id="linechart4211"></div>
</div>