Javascript d3.js仅突出显示数据点的多折线图

Javascript d3.js仅突出显示数据点的多折线图,javascript,d3.js,charts,highlighting,bisection,Javascript,D3.js,Charts,Highlighting,Bisection,我不太明白d3.js中的对分函数是如何通过竖线突出显示值的 我已经让它在一行/路径上运行,但性能很差,至少在谷歌浏览器上是这样。可能是因为我的函数计算路径上的每个点,而不是仅计算数据点,这正是我实际需要的 代码如下: /*create svg element*/ var svg = d3.select('.linechart') .append('svg') .attr('width', w) .attr('height', h) .attr('id', 'chart'); /*x scale

我不太明白d3.js中的对分函数是如何通过竖线突出显示值的

我已经让它在一行/路径上运行,但性能很差,至少在谷歌浏览器上是这样。可能是因为我的函数计算路径上的每个点,而不是仅计算数据点,这正是我实际需要的

代码如下:

/*create svg element*/
var svg = d3.select('.linechart')
.append('svg')
.attr('width', w)
.attr('height', h)
.attr('id', 'chart');

/*x scale*/
var xScale = d3.scale.linear()
.domain([0, d3.max(dataset, function(d) {
  return d[0];
})])
.range([padding, w - padding]);

/*y scale*/
var yScale = d3.scale.linear()
.domain([0, d3.max(dataset, function(d) {
  return d[1];
})])
.range([h - padding, padding]);

/*x axis*/
var xAxis = d3.svg.axis()
.scale(xScale)
.orient('bottom')
.ticks(20)
.tickSize(0, 0)
//.tickPadding(padding);

/*append x axis*/
svg.append('g')
.attr({
  'class': 'xaxis',
  //'transform': 'translate(0,' + (h - padding) + ')'
  'transform': 'translate(0,' + 0 + ')'
})
.call(xAxis);

/*y axis*/
var yAxis = d3.svg.axis()
.scale(yScale)
.orient('left')
.tickSize(0, 0)
.tickValues([0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100]);

/*append y axis*/
svg.append('g')
.attr({
  'class': 'yaxis',
  'transform': 'translate(' + padding + ',0)'
})
.call(yAxis);


/*define line*/
var lines = d3.svg.line()
.x(function(d) {
  return xScale(d[0])
})
.y(function(d) {
  return yScale(d[1])
})
.interpolate('monotone');


/*append line*/
var path = svg.append('path')
.attr({
  'd': lines(dataset),
  'fill': 'none',
  'class': 'lineChart'
});

/*get length*/
var length = svg.select('.lineChart').node().getTotalLength();

/*animate line chart*/
svg.select('.lineChart')
.attr("stroke-dasharray", length + " " + length)
.attr("stroke-dashoffset", length)
.transition()
.ease('linear')
.delay(function(d) {
  return dataset.length * 100;
})
.duration(3000)
.attr("stroke-dashoffset", 0);

/*add points*/
var points = svg.selectAll('circle')
.data(dataset)
.enter()
.append('circle');

/*point attributes*/
points.attr('cy', function(d) {
  return yScale(d[1])
})
.style('opacity', 0)
.transition()
.duration(1000)
.ease('elastic')
.delay(function(d, i) {
  return i * 100;
})
.attr({
  'cx': function(d) {
    return xScale(d[0]);
  },
  'cy': function(d) {
    return yScale(d[1]);
  },
  'r': 5,
  'class': 'datapoint',
  'id': function(d, i) {
    return i;
  }
})


.style('opacity', 1);

//  LINES INDIVIDUAL
function drawIndividualLines (){

  /*define line*/
  var linesIndividual = d3.svg.line()
  .x(function(d) {
    return xScale(d[0])
  })
  .y(function(d) {
    return yScale(d[1])
  })
  .interpolate('monotone');

  /*append line*/
  var pathIndividual = svg.append('path')
  .attr({
    //'d': linesIndividual(datasetIndividual),
    'd': linesIndividual(datasetIndividual),
    'fill': 'none',
    'class': 'lineChartIndividual'
  });

  /*get length*/
  var lengthIndividual = svg.select('.lineChartIndividual').node().getTotalLength();

  /*animate line chart*/
  svg.select('.lineChartIndividual')
  .attr("stroke-dasharray", lengthIndividual + " " + lengthIndividual)
  .attr("stroke-dashoffset", lengthIndividual)
  .transition()
  .ease('linear')
  .delay(function(d) {
    return datasetIndividual.length * 100;
  })
  .duration(3000)
  .attr("stroke-dashoffset", 0);

  /*add points*/
  var pointsIndividual = svg.selectAll('circleIndividual')
  .data(datasetIndividual)
  .enter()
  .append('circle');

  /*point attributes*/
  pointsIndividual.attr('cy', function(d) {
    return yScale(d[1])
  })
  .style('opacity', 0)
  .transition()
  .duration(1000)
  .ease('elastic')
  .delay(function(d, i) {
    return i * 100;
  })
  .attr({
    'cx': function(d) {
      return xScale(d[0]);
    },
    'cy': function(d) {
      return yScale(d[1]);
    },
    'r': 5,
    'class': 'datapointIndividual',
    'id': function(d, i) {
      return i;
    }
  })


  .style('opacity', 1);
};


$(".individual").click(function() {
  drawIndividualLines();
  drawIndividualLegend();
  swapShifts();
});

var mouseG = svg.append("g")
.attr("class", "mouse-over-effects");

mouseG.append("path") // this is the white vertical line to follow mouse
.attr("class", "mouse-line")
.style("stroke", "white")
.style("stroke-width", "1px")
.style("opacity", "0");

var linesForMouse = document.getElementsByClassName('lineChart');
var linesIndividualForMouse = document.getElementsByClassName('lineChartIndividual');

var mousePerLine = mouseG.selectAll('.mouse-per-line')
.data(dataset)
.enter()
.append("g")
.attr("class", "mouse-per-line");

mousePerLine.append("circle")
.attr("r", 7)
.style("stroke", "#A0B1AB")
.style("fill", "none")
.style("stroke-width", "1px")
.style("opacity", "0");

mousePerLine.append("text")
.attr("transform", "translate(10,3)");

mouseG.append('svg:rect') // append a rect to catch mouse movements on canvas
.attr('width', w) // can't catch mouse events on a g element
.attr('height', h)
.attr('fill', 'none')
.attr('pointer-events', 'all')
.on('mouseout', function() { // on mouse out hide line, circles and text
  d3.select(".mouse-line")
  .style("opacity", "0");
  d3.selectAll(".mouse-per-line circle")
  .style("opacity", "0");
  d3.selectAll(".mouse-per-line text")
  .style("opacity", "0");
})
.on('mouseover', function() { // on mouse in show line, circles and text
  d3.select(".mouse-line")
  .style("opacity", "1");
  d3.selectAll(".mouse-per-line circle")
  .style("opacity", "1");
  d3.selectAll(".mouse-per-line text")
  .style("opacity", "1");
})
.on('mousemove', function() { // mouse moving over canvas
  var mouse = d3.mouse(this);
  d3.select(".mouse-line")
  .attr("d", function() {
    var d = "M" + mouse[0] + "," + height;
    d += " " + mouse[0] + "," + 0;
    return d;
  });

  d3.selectAll(".mouse-per-line")
  .attr("transform", function(d, i) {
    //console.log(w/mouse[0])
    //var xDate = xScale.invert(mouse[0]),
    //bisect = d3.bisector(function(d) { return d.date; }).right;
    //      idx = bisect(d.values, xDate);

    var beginning = 0,
    end = length,
    target = null
    console.log(end);

    while (true) {
      target = Math.floor((beginning + end) / 2);
      //pos = linesForMouse[i].getPointAtLength(target);
      pos = svg.select('.lineChart').node().getPointAtLength(target);
      //console.log(pos);
      if ((target === end || target === beginning) && pos.x !== mouse[0]) {
        break;
      }
      if (pos.x > mouse[0]) end = target;
      else if (pos.x < mouse[0]) beginning = target;
      else break; //position found
    }

    d3.select(this).select('text')
    .text(yScale.invert(pos.y).toFixed(2))
    .attr("fill", "#fff");

    return "translate(" + mouse[0] + "," + pos.y + ")";
  });
});
/*创建svg元素*/
var svg=d3。选择(“.linechart”)
.append('svg')
.attr('宽度',w)
.attr('高度',h)
.attr('id','chart');
/*x刻度*/
var xScale=d3.scale.linear()
.domain([0,d3.max(数据集,函数(d)){
返回d[0];
})])
.范围([填充,w-填充]);
/*y刻度*/
var yScale=d3.scale.linear()
.domain([0,d3.max(数据集,函数(d)){
返回d[1];
})])
.范围([h-填充,填充]);
/*x轴*/
var xAxis=d3.svg.axis()
.scale(xScale)
.orient('底部')
.滴答声(20)
.tickSize(0,0)
//.填充(填充);
/*附加x轴*/
append('g')
艾特先生({
“类”:“xaxis”,
//“transform”:“translate(0,+(h-padding)+”)”
“转换”:“转换(0,+0+)”
})
.呼叫(xAxis);
/*y轴*/
var yAxis=d3.svg.axis()
.刻度(yScale)
.orient('左')
.tickSize(0,0)
.tick值([0,10,20,30,40,50,60,70,80,90,100]);
/*附加y轴*/
append('g')
艾特先生({
'class':'yaxis',
“transform”:“translate(“+padding+”,0)”
})
.呼叫(yAxis);
/*定义行*/
var lines=d3.svg.line()
.x(功能(d){
返回xScale(d[0])
})
.y(功能(d){
返回Y刻度(d[1])
})
.插值(‘单调’);
/*附加行*/
var path=svg.append('path')
艾特先生({
“d”:行(数据集),
“填充”:“无”,
“类”:“线形图”
});
/*获取长度*/
var length=svg.select('.lineChart').node().getTotalLength();
/*为折线图制作动画*/
svg.select(“.lineChart”)
.attr(“笔划数组”,长度+“”+长度)
.attr(“笔划偏移”,长度)
.transition()
.ease(“线性”)
.延迟(功能(d){
返回dataset.length*100;
})
.持续时间(3000)
.attr(“行程偏移”,0);
/*加分*/
var points=svg.selectAll('circle')
.数据(数据集)
.输入()
.append('circle');
/*点属性*/
点属性('cy',函数(d){
返回Y刻度(d[1])
})
.style('opacity',0)
.transition()
.持续时间(1000)
.ease(“弹性”)
.延迟(功能(d,i){
返回i*100;
})
艾特先生({
“cx”:函数(d){
返回xScale(d[0]);
},
‘cy’:函数(d){
返回yScale(d[1]);
},
“r”:5,
“类”:“数据点”,
“id”:函数(d,i){
返回i;
}
})
.style('opacity',1);
//行个人
函数drawIndividualLines(){
/*定义行*/
var linesIndividual=d3.svg.line()
.x(功能(d){
返回xScale(d[0])
})
.y(功能(d){
返回Y刻度(d[1])
})
.插值(‘单调’);
/*附加行*/
var pathIndividual=svg.append('path')
艾特先生({
//“d”:linesIndividual(datasetIndividual),
“d”:linesIndividual(datasetIndividual),
“填充”:“无”,
“类”:“lineChartIndividual”
});
/*获取长度*/
var lengthIndividual=svg.select('.lineChartIndividual').node().getTotalLength();
/*为折线图制作动画*/
svg.select(“.lineChartIndividual”)
.attr(“笔划数组”,lengthIndividual+“”+lengthIndividual)
.attr(“行程偏移”,长度单个)
.transition()
.ease(“线性”)
.延迟(功能(d){
返回datasetividual.length*100;
})
.持续时间(3000)
.attr(“行程偏移”,0);
/*加分*/
var pointsIndividual=svg.selectAll('circleIndividual')
.数据(数据集个人)
.输入()
.append('circle');
/*点属性*/
pointsIndividual.attr('cy',函数(d){
返回Y刻度(d[1])
})
.style('opacity',0)
.transition()
.持续时间(1000)
.ease(“弹性”)
.延迟(功能(d,i){
返回i*100;
})
艾特先生({
“cx”:函数(d){
返回xScale(d[0]);
},
‘cy’:函数(d){
返回yScale(d[1]);
},
“r”:5,
“类”:“datapointIndividual”,
“id”:函数(d,i){
返回i;
}
})
.style('opacity',1);
};
$(“.individual”)。单击(函数(){
画线();
drawIndividualLegend();
交换档();
});
var mouseG=svg.append(“g”)
.attr(“类”、“鼠标悬停效果”);
mouseG.append(“path”)//这是跟随鼠标的白色垂直线
.attr(“类”、“鼠标线”)
.style(“笔划”、“白色”)
.style(“笔划宽度”、“1px”)
.样式(“不透明度”、“0”);
var linesForMouse=document.getElementsByClassName('lineChart');
var linesIndividualForMouse=document.getElementsByClassName('lineChartIndividual');
var mousePerLine=mouseG.selectAll(“.mouse per line”)
.数据(数据集)
.输入()
.附加(“g”)
.attr(“类”,“每行鼠标”);
鼠标线。附加(“圆”)
.attr(“r”,7)
.style(“笔划”,“#A0B1AB”)
.style(“填充”、“无”)
.style(“笔划宽度”、“1px”)
.样式(“不透明度”、“0”);
mousePerLine.append(“文本”)
.attr(“转换”、“翻译(10,3)”);
append('svg:rect')//追加一个rect以捕捉画布上的鼠标移动
.attr('width',w)//无法捕获g元素上的鼠标事件
.attr('高度',h)
.attr('填充','无')
.attr('pointer-events','all')
.on('mouseout',function(){//on mouseout隐藏线、圆和文本
d3.选择(“鼠标线”)
.样式(“不透明度”、“0”);
d3.全选(“.鼠标每行圆”)
.样式(“不透明度”、“0”);
d3.选择全部(“.鼠标每行文本”)
.样式(“不透明度”、“0”);
})
.on('mouseover',function(){//在鼠标上显示线、圆和文本
d3.选择(“鼠标线”)
.样式(“不透明度”、“1”);
d3.全选(“.鼠标每行圆”)
.样式(“不透明度”、“1”);
d3.选择全部(“.鼠标每行文本”)
.样式(“不透明度”、“1”);
})
.on('mousemove',function(){//鼠标在画布上移动
var mouse=d3.mouse(this);
d3.选择(“鼠标线”)
.attr(“d”,函数(){
var d=“M”+鼠标[0]+,“+高度;
d++=“+鼠标[0]+”,“+0;
返回d;
});
d3.全选(“每行鼠标数”)
.attr(“转换”,函数(d,i){
//console.log(带鼠标[0])
//var xDate=xScale.invert(鼠标[0])
function mousemove() {
  //convert absolute coordinates to the proper scale
  const x0 = x.invert(d3.mouse(this)[0]);
  //bisect the data
  const i = bisectDate(data, x0, 1);
  //get the two closest elements to the mouse
  const d0 = data[i - 1];
  const d1 = data[i];
  //check which one is actually the closest
  const d = x0 - d0.date > d1.date - x0 ? d1 : d0;
  //draw your lines
  focus.attr('transform', `translate(${x(d.date)}, ${y(d.close)})`);
  focus.select('line.x')
    .attr('x1', 0)
    .attr('x2', -x(d.date))
    .attr('y1', 0)
    .attr('y2', 0);

  focus.select('line.y')
    .attr('x1', 0)
    .attr('x2', 0)
    .attr('y1', 0)
    .attr('y2', height - y(d.close));

  focus.select('text').text(formatCurrency(d.close));
}