Я использую силу d3, чтобы создать диаграмму теплого пчеловодства, которая разделяется на групповую категорию с помощью переключателя. У меня все работает, кроме того, когда я разгруппирую, сгруппирую, а затем разгруппирую, позиция y, похоже, становится все меньше и меньше по дальности.
пример первой разгруппировки: ![enter image description here](https://i.stack.imgur.com/Fjdy7.png)
при группировке: ![enter image description here](https://i.stack.imgur.com/FlLUk.png)
Вторая разгруппировка: ![enter image description here](https://i.stack.imgur.com/0HxTX.png)
Любая помощь в правильном направлении очень ценится.
Вот мой код для диаграммы:
Dimensions for chart
*/
this.width = 900;
this.height = 750;
this.margin = {x: 50, y:100};
this.data = data;
svg.attr('height', (this.height + this.margin.y)).attr('width', this.width + (this.margin.x * 2));
svg.append('g').attr('id', 'circle-wrap').attr('transform', `translate(${this.margin.x},${this.margin.y})`);
let xAxis = svg.append('g').attr('id', 'x-axis');
this.updateBubbleChart(d3.select('#separate').node().checked, svg, data);
}
/**
* Updates the bubble chart
* @param {Boolean} separate
* @param {*} svg
* @param {*} data
*/
updateBubbleChart(separate, svg, data){
//GETTING OVERALL MIN AND MAX FOR SCALES//
let difMax = d3.max(data.map(d=> d.position));
let difMin = d3.min(data.map(d=> d.position));
//SO WE CAN SEE THE DIFFERENCES IN POLARITY ON THE SAME SCALE,
//TAKE THE MIN AND MAX AND GET THE MAX VALUE FOR THAT.
//USE POS AND NEG OF THAT VALUE FOR DOMAIN.
let overallMax = d3.max([Math.abs(difMin), difMax]);
//USING D3.GROUPS TO GROUP DATA BY CATEGORY
let groupData = d3.groups(data, d=> d.category);
//SORT BY LENGTH OF GROUP?
let groupKeys = groupData.map((g, i)=> {
return {key:g[0], pos: i};
});
//SCALE FOR X POSITION//
let xScale = d3.scaleLinear().domain([(0-overallMax), overallMax]);
xScale.range([0, this.width]);
//SCALE FOR Y POSITION//
let yScale = d3.scaleLinear().domain([0, groupKeys.length - 1]).range([0, this.height - this.margin.y])
//SCALES FOR COLOR//
let colorScale = d3.scaleOrdinal().domain(groupKeys.map(k=> k.key)).range(d3.schemeSet2);
//SCALE FOR CIRCLE SIZE//
let circleScale = d3.scaleLinear().domain([d3.min(data.map(d=> +d.total)), d3.max(data.map(d=> +d.total))])
.range([3, 12]);
//SIMULATION PART
let simulation = d3.forceSimulation().nodes(data)
.force('x', d3.forceX(d => xScale(d.position)).strength(1))
.force('y', d3.forceY().strength(1))
.force('collision', d3.forceCollide().radius( d => circleScale(d.total)))
.on('tick',ticked);
let xAxis = d3.axisTop().scale(xScale);
let xAxisG = d3.select('#x-axis').call(xAxis).attr('transform', `translate(${this.margin.x}, ${this.margin.y/2})`)
let circleGroup = d3.select('#circle-wrap');
//BINDING CIRCLES TO THE DATA
let circles = circleGroup.selectAll('circle').data(data).join('circle');
circles.attr('r', (d)=> circleScale(+d.total))//.attr('cx', (d) => posScale(d.position)).attr('cy', 50);
.attr("cx", d=> xScale(+d.position))
circles.attr('fill', (d)=> colorScale(d.category))
.attr('stroke', 'black').attr('stroke-width', 0.3);
circles.style('opacity', '0.9');
//TOOLTIP FOR CIRCLES ON HOVER///
//Add the tooltip labels on mouseover
circles.on('mouseover', (d, i, n)=> {
//SHOW DATA IN TOOLTIP
d3.select('#tooltip').transition()
.duration(200)
.style("opacity", .9);
d3.select('#tooltip').html(tooltipRender(d) + "<br/>")
.style("left", (d3.event.pageX + 5) + "px")
.style("top", (d3.event.pageY - 28) + "px");
//HIGHLIGHT CIRCLE
d3.select(n[i]).attr('stroke', 'black').attr('stroke-width', 2);
});
circles.on("mouseout", function(d, i, n) {
d3.select('#tooltip').transition()
.duration(500)
.style("opacity", 0);
//REMOVE CIRCLE HIGHLIGHT
d3.select(n[i]).attr('stroke', 'black').attr('stroke-width', 0.3);
});
// Apply these forces to the nodes and update their positions.
// Once the force algorithm is happy with positions ('alpha' value is low enough), simulations will stop.
function ticked(){
circles.attr("cy", d=> d.y).attr('cx', d=> d.x);
if(separate === true){
simulation.force('y', d3.forceY().y( d => {
let move = groupKeys.filter(g=> g.key === d.category)[0].pos;
return yScale(move);
}));
}
}