简体   繁体   中英

How to center a List inside a D3 circle

I have a List of items that are inside a circle. I am using hardcoded values for the alignment. I need it to be based off the central point of the circle and by the length of the array.

Need to get rid of these "yAxis: -40, yAxis: -40, yAxis: 0, yAxis: 20";

And also have some space between line items.

PIC

 const w = 500, h = 400, r = 160; const STREAMS = [{ label: 'Emissions', isSelected: true, yAxis: -40 }, { label: 'Energy Produced', isSelected: false, yAxis: -20 }, { label: 'Energy Consumed', isSelected: false, yAxis: 0 }, { label: 'Intensity', isSelected: false, yAxis: 20 }] const SUB_STREAMS = [{ value: 0.15, label: 'Total', isSelected: true }, { value: 0.2, label: 'CO2', isSelected: false }, { value: 0.25, label: 'Methane', isSelected: false }, { value: 0.30, label: 'N2O', isSelected: false }, { value: 0.35, label: 'Other', isSelected: false }]; const svg = d3.select("#foo").append("svg").attr("width", w).attr("height", h); const g = svg.append("g").attr("transform", "translate(" + [w / 2, h / 2] + ")"); g.append("circle").attr("r", r).style("fill", "none").style("stroke", "black"); const points = g.selectAll(null).data(SUB_STREAMS).enter().append("circle").attr('stroke', 'dodgerblue').attr('stroke-width', 1).style("fill", function(d) { return d.isSelected? 'dodgerblue': 'white' }).attr("r", 12).attr("cx", function(d) { return r * Math.cos(d.value * Math.PI * 2 - Math.PI / 2) }).attr("cy", function(d) { return r * Math.sin(d.value * Math.PI * 2 - Math.PI / 2) }) points.on("click", function(d) { console.log(d) }) g.selectAll(null).data(SUB_STREAMS).enter().append('text').style('cursor', 'pointer').style('fill', 'black').attr('text-anchor', 'right').attr('font-size', '1.3em').attr('dx', (d) => 14 + r * Math.cos(d.value * Math.PI * 2 - Math.PI / 2)).attr('dy', (d) => r * Math.sin(d.value * Math.PI * 2 - Math.PI / 2)).text((d) => d.label) const text = g.selectAll('path').data(STREAMS).enter().append("text").attr("text-anchor", "left").attr('font-size', '1em').attr("y", function(d, a) { return d.yAxis - 5 }).text((d) => d.label); text.on("click", function(d) { console.log(d) }) var arc = d3.symbol().type(d3.symbolTriangle) var line = g.selectAll('path').data(STREAMS).enter().append('path').attr('d', arc).attr('fill', 'red').attr('stroke', '#000').attr('stroke-width', 1).attr('transform', function(d) { return `translate(-10,${d.yAxis - 5}) rotate(210)`; });
 text { dominant-baseline: central; }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script> <div id="foo" />

One solution out of many is setting a padding...

const padding = 20

...and translating the container groups by their indices times that padding:

const groups = g
  .selectAll('path')
  .data(STREAMS)
  .enter()
  .append("g")
  .attr("transform", (_, i) => "translate(0," + 
    (-padding * (STREAMS.length - 1) / 2 + i * padding) + ")");

Then, you append both texts and paths to those groups.

Here is your code with those changes:

 const w = 500, h = 400, r = 160, padding = 20; const STREAMS = [{ label: 'Emissions', isSelected: true }, { label: 'Energy Produced', isSelected: false }, { label: 'Energy Consumed', isSelected: false }, { label: 'Intensity', isSelected: false }] const SUB_STREAMS = [{ value: 0.15, label: 'Total', isSelected: true }, { value: 0.2, label: 'CO2', isSelected: false }, { value: 0.25, label: 'Methane', isSelected: false }, { value: 0.30, label: 'N2O', isSelected: false }, { value: 0.35, label: 'Other', isSelected: false }]; const svg = d3.select("#foo").append("svg").attr("width", w).attr("height", h); const g = svg.append("g").attr("transform", "translate(" + [w / 2, h / 2] + ")"); g.append("circle").attr("r", r).style("fill", "none").style("stroke", "black"); const points = g.selectAll(null).data(SUB_STREAMS).enter().append("circle").attr('stroke', 'dodgerblue').attr('stroke-width', 1).style("fill", function(d) { return d.isSelected? 'dodgerblue': 'white' }).attr("r", 12).attr("cx", function(d) { return r * Math.cos(d.value * Math.PI * 2 - Math.PI / 2) }).attr("cy", function(d) { return r * Math.sin(d.value * Math.PI * 2 - Math.PI / 2) }) points.on("click", function(d) { console.log(d) }) g.selectAll(null).data(SUB_STREAMS).enter().append('text').style('cursor', 'pointer').style('fill', 'black').attr('text-anchor', 'right').attr('font-size', '1.3em').attr('dx', (d) => 14 + r * Math.cos(d.value * Math.PI * 2 - Math.PI / 2)).attr('dy', (d) => r * Math.sin(d.value * Math.PI * 2 - Math.PI / 2)).text((d) => d.label) const groups = g.selectAll('path').data(STREAMS).enter().append("g").attr("transform", (_, i) => "translate(0," + (-padding * (STREAMS.length - 1) / 2 + i * padding) + ")"); groups.append("text").attr('font-size', '1em').text((d) => d.label).on("click", function(d) { console.log(d) }) var arc = d3.symbol().type(d3.symbolTriangle) groups.append('path').attr('d', arc).attr('fill', 'red').attr('stroke', '#000').attr('stroke-width', 1).attr('transform', function(d) { return "translate(-10,0) rotate(210)"; });
 text { dominant-baseline: central; }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script> <div id="foo" />

And here the same code, with a bigger data array, so you can see that it dynamically sets the positions according to the number of elements:

 const w = 500, h = 400, r = 160, padding = 20; const STREAMS = [{ label: 'Emissions', isSelected: true }, { label: 'Energy Produced', isSelected: false }, { label: 'Energy Consumed', isSelected: false }, { label: 'Intensity', isSelected: false }, { label: 'Foo', isSelected: false }, { label: 'Bar', isSelected: false }, { label: 'Baz', isSelected: false }] const SUB_STREAMS = [{ value: 0.15, label: 'Total', isSelected: true }, { value: 0.2, label: 'CO2', isSelected: false }, { value: 0.25, label: 'Methane', isSelected: false }, { value: 0.30, label: 'N2O', isSelected: false }, { value: 0.35, label: 'Other', isSelected: false }]; const svg = d3.select("#foo").append("svg").attr("width", w).attr("height", h); const g = svg.append("g").attr("transform", "translate(" + [w / 2, h / 2] + ")"); g.append("circle").attr("r", r).style("fill", "none").style("stroke", "black"); const points = g.selectAll(null).data(SUB_STREAMS).enter().append("circle").attr('stroke', 'dodgerblue').attr('stroke-width', 1).style("fill", function(d) { return d.isSelected? 'dodgerblue': 'white' }).attr("r", 12).attr("cx", function(d) { return r * Math.cos(d.value * Math.PI * 2 - Math.PI / 2) }).attr("cy", function(d) { return r * Math.sin(d.value * Math.PI * 2 - Math.PI / 2) }) points.on("click", function(d) { console.log(d) }) g.selectAll(null).data(SUB_STREAMS).enter().append('text').style('cursor', 'pointer').style('fill', 'black').attr('text-anchor', 'right').attr('font-size', '1.3em').attr('dx', (d) => 14 + r * Math.cos(d.value * Math.PI * 2 - Math.PI / 2)).attr('dy', (d) => r * Math.sin(d.value * Math.PI * 2 - Math.PI / 2)).text((d) => d.label) const groups = g.selectAll('path').data(STREAMS).enter().append("g").attr("transform", (_, i) => "translate(0," + (-padding * (STREAMS.length - 1) / 2 + i * padding) + ")"); groups.append("text").attr('font-size', '1em').text((d) => d.label).on("click", function(d) { console.log(d) }) var arc = d3.symbol().type(d3.symbolTriangle) groups.append('path').attr('d', arc).attr('fill', 'red').attr('stroke', '#000').attr('stroke-width', 1).attr('transform', function(d) { return "translate(-10,0) rotate(210)"; });
 text { dominant-baseline: central; }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script> <div id="foo" />

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM