Javascript d3.js仅突出显示数据点的多折线图
我不太明白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
/*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));
}