Home > Mobile >  D3 append text to inner circle
D3 append text to inner circle

Time:01-05

Original question:

I want to visual highlight attributes for each node. To do so I added another small-circle. Further I want to add a short string in each blue circle. I tried different approaches, the most common seems to be to .append("text") during the init straight away. Unfortunately I was not lucky yet.

How can I achieve that?

MODIFIED, as kikon mentioned:

I did as suggested, it works. But how can I assign the text to each node? Now it appears only on the first child circle.

enter image description here

 var width = window.innerWidth,
            height = window.innerHeight;

        var svg = d3.select("body").append("svg")
            .attr("width", width)
            .attr("height", height)
            .call(d3.zoom().on("zoom", function(event) {
                svg.attr("transform", event.transform)
            }))
            .append("g")

        ////////////////////////
        // outer force layout

        var data = {
            "nodes":[
                { "id": "A", "shoes": [{"brand": "Nike"}, {"brand": "Adidas"}, {"brand": "Adidas"}]}, 
                { "id": "B", "shoes": [{"brand": "Nike"}, {"brand": "Adidas"}, {"brand": "Adidas"}, {"brand": "Adidas"}]},
                { "id": "C", "shoes": []},
            ],
            "links": [
                { "source": "A", "target": "B"},
                { "source": "B", "target": "C"},
                { "source": "C", "target": "A"}
            ]
        };

        var simulation = d3.forceSimulation()
            .force("size", d3.forceCenter(width / 2, height / 2))
            .force("charge", d3.forceManyBody().strength(-1000))
            .force("link", d3.forceLink().id(function (d) { return d.id }).distance(250))
       
        linksContainer = svg.append("g").attr("class", "linkscontainer")
        nodesContainer = svg.append("g").attr("class", "nodesContainer")
       
        var links = linksContainer.selectAll("g")
            .data(data.links)
            .join("g")
            .attr("fill", "transparent")

        var linkLine = linksContainer.selectAll(".linkPath")
            .data(data.links)
            .join("path")
            .attr("stroke", "red")
            .attr("fill", "transparent")
            .attr("stroke-width", 3)
        
        nodes = nodesContainer.selectAll(".nodes")
            .data(data.nodes, function (d) { return d.id; })
            .join("g")
            .attr("class", "nodes")
            .attr("id", function (d) { return d.id; })
            .call(d3.drag()
                .on("start", dragStarted)
                .on("drag", dragged)
                .on("end", dragEnded)
            )

        nodes.selectAll("circle")
            .data(d => [d])
            .join("circle")
            .style("fill", "lightgrey")
            .style("stroke", "blue")
            .attr("r", 40)

        var eventCircle = nodes.selectAll("g")
            .data(data.nodes)
            .enter()
            .append("g")
            
        eventCircle.append("g")
            .selectAll("circle-small")
            .data(x => x.shoes)
            .enter()
            .append('circle')
            .attr('r', 15)
            .attr("fill", "blue")
            .attr('cx', function (d,i) {
                const factor = (i / 40) * (15 / 2) * 5;
                return 40 * Math.cos(factor - Math.PI * 0.5);
            })
            .attr('cy', function (d,i) { 
                const factor = (i / 40) * (15 / 2) * 5;
                return 40 * Math.sin(factor - Math.PI * 0.5);
            })
            .attr("class", "circle-small")

        eventCircle.append("text")
            .attr("x", function(d) {
                return -4
            })
            .attr("y", function(d) {
                return  -34
            })
            .attr("font-size", 15)
            .attr("fill", "white")
            .attr("pointer-events", "cursor")
            .text(function (d) {
                return "1"
            })

        simulation
            .nodes(data.nodes)
            .on("tick", tick)

        simulation
            .force("link")
            .links(data.links)


            
        function tick() {
            linkLine.attr("d", function(d) {
                var dx = (d.target.x - d.source.x),
                    dy = (d.target.y - d.source.y),
                    dr = Math.sqrt(dx * dx   dy * dy)

                return "M"   d.source.x   ","   d.source.y   "A"   dr   ","   dr   " 0 0,1 "   d.target.x   ","   d.target.y;
            })
                

            nodes
                .attr("transform", d => `translate(${d.x}, ${d.y})`);
        }

        function dragStarted(event, d) {
            if (!event.active) simulation.alphaTarget(0.3).restart();
            d.fx = d.x;
            d.fy = d.y;
        }

        function dragged(event, d) {
            d.fx = event.x;
            d.fy = event.y;
        }

        function dragEnded(event, d) {
            if (!event.active) simulation.alphaTarget(0);
            d.fx = null;
            d.fy = null;
        }
   body {
        background: whitesmoke,´;
        overflow: hidden;
        margin: 0px;
    }
<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>D3v7</title>
    <!-- d3.js framework -->
    <script src="https://d3js.org/d3.v7.js"></script>
</head>



<body>
</body>
</html>

CodePudding user response:

If you inspect your SVG you'll see you are appending several circles on top of each other.

Just append one <g> for each brand, translating it accordingly...

var eventCircle = nodes.selectAll(null)
  .data(d => d.shoes)
  .enter()
  .append("g")
  .attr("transform", function(d, i) {
    const factor = (i / 40) * (15 / 2) * 5;
    return `translate(${40 * Math.cos(factor - Math.PI * 0.5)},${40 * Math.sin(factor - Math.PI * 0.5)})`;
  });

...then append your circles and texts to those groups.

Here is your modified demo:

var width = window.innerWidth,
  height = window.innerHeight;

var svg = d3.select("body").append("svg")
  .attr("width", width)
  .attr("height", height)
  .call(d3.zoom().on("zoom", function(event) {
    svg.attr("transform", event.transform)
  }))
  .append("g")

////////////////////////
// outer force layout

var data = {
  "nodes": [{
      "id": "A",
      "shoes": [{
        "brand": "Nike"
      }, {
        "brand": "Adidas"
      }, {
        "brand": "Adidas"
      }]
    },
    {
      "id": "B",
      "shoes": [{
        "brand": "Nike"
      }, {
        "brand": "Adidas"
      }, {
        "brand": "Adidas"
      }, {
        "brand": "Adidas"
      }]
    },
    {
      "id": "C",
      "shoes": []
    },
  ],
  "links": [{
      "source": "A",
      "target": "B"
    },
    {
      "source": "B",
      "target": "C"
    },
    {
      "source": "C",
      "target": "A"
    }
  ]
};

var simulation = d3.forceSimulation()
  .force("size", d3.forceCenter(width / 2, height / 2))
  .force("charge", d3.forceManyBody().strength(-1000))
  .force("link", d3.forceLink().id(function(d) {
    return d.id
  }).distance(250))

linksContainer = svg.append("g").attr("class", "linkscontainer")
nodesContainer = svg.append("g").attr("class", "nodesContainer")

var links = linksContainer.selectAll("g")
  .data(data.links)
  .join("g")
  .attr("fill", "transparent")

var linkLine = linksContainer.selectAll(".linkPath")
  .data(data.links)
  .join("path")
  .attr("stroke", "red")
  .attr("fill", "transparent")
  .attr("stroke-width", 3)

nodes = nodesContainer.selectAll(".nodes")
  .data(data.nodes, function(d) {
    return d.id;
  })
  .join("g")
  .attr("class", "nodes")
  .attr("id", function(d) {
    return d.id;
  })
  .call(d3.drag()
    .on("start", dragStarted)
    .on("drag", dragged)
    .on("end", dragEnded)
  )

nodes.selectAll("circle")
  .data(d => [d])
  .join("circle")
  .style("fill", "lightgrey")
  .style("stroke", "blue")
  .attr("r", 40)

var eventCircle = nodes.selectAll(null)
  .data(d => d.shoes)
  .enter()
  .append("g")
  .attr("transform", function(d, i) {
    const factor = (i / 40) * (15 / 2) * 5;
    return `translate(${40 * Math.cos(factor - Math.PI * 0.5)},${40 * Math.sin(factor - Math.PI * 0.5)})`;
  });

eventCircle.append('circle')
  .attr('r', 15)
  .attr("fill", "blue")
  .attr("class", "circle-small")

eventCircle.append("text")
  .attr("font-size", 10)
  .attr("fill", "white")
  .style("text-anchor", "middle")
  .attr("pointer-events", "cursor")
  .text(function(d) {
    return d.brand;
  })

simulation
  .nodes(data.nodes)
  .on("tick", tick)

simulation
  .force("link")
  .links(data.links)



function tick() {
  linkLine.attr("d", function(d) {
    var dx = (d.target.x - d.source.x),
      dy = (d.target.y - d.source.y),
      dr = Math.sqrt(dx * dx   dy * dy)

    return "M"   d.source.x   ","   d.source.y   "A"   dr   ","   dr   " 0 0,1 "   d.target.x   ","   d.target.y;
  })


  nodes
    .attr("transform", d => `translate(${d.x}, ${d.y})`);
}

function dragStarted(event, d) {
  if (!event.active) simulation.alphaTarget(0.3).restart();
  d.fx = d.x;
  d.fy = d.y;
}

function dragged(event, d) {
  d.fx = event.x;
  d.fy = event.y;
}

function dragEnded(event, d) {
  if (!event.active) simulation.alphaTarget(0);
  d.fx = null;
  d.fy = null;
}
body {
  background: whitesmoke, ´;
  overflow: hidden;
  margin: 0px;
}
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <title>D3v7</title>
  <!-- d3.js framework -->
  <script src="https://d3js.org/d3.v7.js"></script>
</head>



<body>
</body>

</html>

  • Related