我没有完全摆脱d3.js中的bisect函数,以便通过垂直线突出显示值。
我已经让它适用于一条线路/路径,但性能很差,至少在google chrome中。可能是因为我的函数计算路径上的每个点而不是数据点,这是我实际需要的。
以下是代码:
/*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 + ")";
});
});
这是一个小提琴: https://jsfiddle.net/mindcraft/vk2w7k2f/2/
所以我的问题是:
如何设置仅突出显示数据点? (通过我不理解的平分功能,我想......)
如何将相同的功能应用于第二行(以更有效的方式点击“显示个人”按钮后可见?
提前谢谢!
答案 0 :(得分:0)
让我看看能否为您解释d3.bisector()
。与搜索两个数据点之间的每个值相比,它将为您带来巨大的性能提升。平分线是一种用于排序数据的阵列搜索形式。但是,您不是要搜索特定值,而是搜索插入任意值x
的正确索引,以便维护排序顺序(平分线将阵列分成两半) ,一个值小于或等于x
,一个值大于x
)。此索引还为您提供数据集中与新值最接近的值(即index
和index-1
。这就是bisector()
在您的示例中非常有用的方式。它基本上会返回数据集中两个与鼠标位置最接近的值。您只需要算法查找两者中哪一个最接近的。
以下是https://bl.ocks.org/micahstubbs/e4f5c830c264d26621b80b754219ae1b的过程示例,我的评论映射到上面的解释:
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));
}