Как я могу реализовать функцию инвертирования для шкалы баллов?
Я пытаюсь добавить всплывающую подсказку для моего графика с двумя линейными диаграммами.
Однако вместо использования timeScale или scaleLinear для построения графика я использовал scalePoint.
Я пытаюсь добиться следующего эффекта: https://bl.ocks.org/mbostock/3902569
this.x = d3.scalePoint().range([ this.margin.left, this.width - this.margin.right ]);
this.xAxis = d3.axisBottom(this.x);
this.x.domain(
this.dataArray.map(d => {
return this.format(d[ 'year' ]);
}));
Вот моя функция наведения мыши,
function mousemove() {
//d3.mouse(this)[ 0 ]
//x.invert
var x0 = d3.mouse(this)[ 0 ],
i = bisectDate(data, x0, 1),
d0 = data[ i - 1 ],
d1 = data[ i ],
d = x0 - d0.year > d1.year - x0 ? d1 : d0;
console.log(x0);
// focus.attr("transform", "translate(" + x(format(d.year)) + "," + y(d.housing_index_change) + ")");
// focus.select("text").text(d.housing_index_change);
}
Поскольку я использую scalePoint, очевидно, что нет функции инвертирования для сопоставления координат с моими данными. и я получаю только первый элемент в массиве, и это единственный отображаемый элемент независимо от положения мыши.
Итак, мой вопрос, как я могу реализовать функцию инвертирования здесь, все еще используя scalePoint?
Спасибо:)
1 ответ
Вы правы, для шкалы баллов нет инверсии. Но вы можете создать свою собственную функцию, чтобы получить соответствующий домен заданной позиции x:
function scalePointPosition() {
var xPos = d3.mouse(this)[0];
var domain = xScale.domain();
var range = xScale.range();
var rangePoints = d3.range(range[0], range[1], xScale.step())
var yPos = domain[d3.bisect(rangePoints, xPos) -1];
console.log(yPos);
}
Пошаговое объяснение
Во-первых, мы получаем x
положение мыши.
var xPos = d3.mouse(this)[0];
Затем, в зависимости от диапазона и домена вашей шкалы...
var domain = xScale.domain();
var range = xScale.range();
... мы создаем массив со всеми шагами в шкале точек, используя d3.range:
var rangePoints = d3.range(range[0], range[1], xScale.step())
Наконец, мы получаем соответствующий домен, используя bisect:
var yPos = domain[d3.bisect(rangePoints, xPos) -1];
Проверить console.log
в этой демонстрации:
var data = [{
A: "groupA",
B: 10
}, {
A: "groupB",
B: 20
}, {
A: "groupC",
B: 30
}, {
A: "groupD",
B: 10
}, {
A: "groupE",
B: 17
}]
var width = 500,
height = 200;
var svg = d3.selectAll("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var color = d3.scaleOrdinal(d3.schemeCategory10)
.domain(data.map(function(d) {
return d.A
}));
var xScale = d3.scalePoint()
.domain(data.map(function(d) {
return d.A
}))
.range([50, width - 50])
.padding(0.5);
var yScale = d3.scaleLinear()
.domain([0, d3.max(data, function(d) {
return d.B
}) * 1.1])
.range([height - 50, 10]);
var circles = svg.selectAll(".circles")
.data(data)
.enter()
.append("circle")
.attr("r", 8)
.attr("cx", function(d) {
return xScale(d.A)
})
.attr("cy", function(d) {
return yScale(d.B)
})
.attr("fill", function(d) {
return color(d.A)
});
var xAxis = d3.axisBottom(xScale);
var yAxis = d3.axisLeft(yScale);
svg.append("g").attr("transform", "translate(0,150)")
.attr("class", "xAxis")
.call(xAxis);
svg.append("g")
.attr("transform", "translate(50,0)")
.attr("class", "yAxis")
.call(yAxis);
svg.append("rect")
.attr("opacity", 0)
.attr("x", 50)
.attr("width", width - 50)
.attr("height", height)
.on("mousemove", scalePointPosition);
function scalePointPosition() {
var xPos = d3.mouse(this)[0];
var domain = xScale.domain();
var range = xScale.range();
var rangePoints = d3.range(range[0], range[1], xScale.step())
var yPos = domain[d3.bisect(rangePoints, xPos) - 1];
console.log(yPos);
}
.as-console-wrapper { max-height: 20% !important;}
<script src="https://d3js.org/d3.v4.min.js"></script>