0

It is an old topic and read many questions regarding this issue but I still can't figure it out what the error can be. My only excuse is that I am pretty new with d3.

So I'd like a custom image or svg on nodes but all I can achieve is the built-in symbols (circle).

enter image description here

The code is

            const simulation = d3.forceSimulation(nodes)
                        .force('link', d3.forceLink(links).id((l: any) => l.id))
                        .force('charge', d3.forceManyBody())
                        .force('center', d3.forceCenter(width / 2, height / 2))
                        .on('tick', ticked)

            const svg = d3
                        .select(graphRef.current)
                        .attr("preserveAspectRatio", "xMinYMin meet")
                        .attr("viewBox", "0 0 960 500")

            const link = svg
                        .append('g')
                        .attr('stroke', '#999')
                        .attr('stroke-opacity', 0.6)                        
                        .selectAll()
                        .data(links)
                        .join('line')
                        .attr('stroke-width', (d: any) => Math.sqrt(d.value))    

                        
            const node = svg
                        .append('g')
                        .attr('stroke', '#fff')
                        .attr('stroke-width', 1.5)
                        .selectAll()
                        .data(nodes)
                        .join('circle')
                        .attr('r', 5)
                        .attr('fill', 'green')

            node.call(d3.drag()
                        .on('start', dragStarted)
                        .on('drag', dragged)
                        .on('end', dragEnded))

I tried to mimic the code from https://gist.github.com/mbostock/950642 but it doesn't show anything, the nodes disappear, only the links can be seen (well I think the icon is displayed on the top left corner though)

enter image description here

            const node = svg
                        .append('g')
                        .attr('stroke', '#fff')
                        .attr('stroke-width', 1.5)
                        .selectAll()
                        .data(nodes)

                        .enter()
                        .append("g")
                        .append("image")
                        .attr("xlink:href", "https://github.com/favicon.ico")
                        .attr("width", 16)
                        .attr("height", 16);

                        // .join('circle')
                        // .attr('r', 5)
                        // .attr('fill', 'green')

I'd appreciate any hint please.
Thank you!

//////////////// UPDATE //////////////////

            const ticked = (): void => {
                link.attr("x1", (d: any) => d.source.x)
                    .attr("y1", (d: any) => d.source.y)
                    .attr("x2", (d: any) => d.target.x)
                    .attr("y2", (d: any) => d.target.y);
    
                node.attr("cx", (d: any) => d.x)
                    .attr("cy", (d: any) => d.y);
            }

1 Answer 1

0

You didn't post your ticked function which is most relevant bit to your question. So, without seeing it, I'm guessing that you probably failed to update it to position the g holding your image and instead it is still trying to position a circle with cx and cy properties that don't exist on a g/image. The update would look something like this:

function ticked() {
   link
    .attr('x1', (d) => d.source.x)
    .attr('y1', (d) => d.source.y)
    .attr('x2', (d) => d.target.x)
    .attr('y2', (d) => d.target.y);

  node.attr('transform', (d) => `translate(${d.x - 8},${d.y - 8})`);
}

Notice how it is now being positioned with a transform.

Running code:

<!DOCTYPE html>

<html>
  <head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.9.0/d3.js"></script>
  </head>

  <body>
    <svg id="networkGraph" width="960" height="500"></svg>
    <script>
      const width = 300,
        height = 150;

      const nodes = [{ id: 'Alice' }, { id: 'Bob' }, { id: 'Carol' }];

      const links = [
        { source: 'Alice', target: 'Bob' },
        { source: 'Bob', target: 'Carol' },
      ];

      const simulation = d3
        .forceSimulation(nodes)
        .force(
          'link',
          d3.forceLink(links).id((l) => l.id)
        )
        .force('charge', d3.forceManyBody())
        .force('center', d3.forceCenter(width / 2, height / 2))
        .on('tick', ticked);

      const svg = d3
        //.select(graphRef.current)
        .select('#networkGraph')
        .attr('preserveAspectRatio', 'xMinYMin meet')
        .attr('viewBox', '0 0 960 500');

      const link = svg
        .append('g')
        .attr('stroke', '#999')
        .attr('stroke-opacity', 0.6)
        .selectAll()
        .data(links)
        .join('line')
        .attr('stroke-width', (d) => Math.sqrt(d.value));

      const node = svg
        .append('g')
        .selectAll()
        .data(nodes)
        .enter()
        .append('g');
        
      node
        .append('image')
        .attr('xlink:href', 'https://github.com/favicon.ico')
        .attr('width', 16)
        .attr('height', 16);

      /*
      node.call(
        d3
          .drag()
          .on('start', dragStarted)
          .on('drag', dragged)
          .on('end', dragEnded)
      );
      */

      function ticked() {
        link
          .attr('x1', (d) => d.source.x)
          .attr('y1', (d) => d.source.y)
          .attr('x2', (d) => d.target.x)
          .attr('y2', (d) => d.target.y);

        node.attr('transform', (d) => `translate(${d.x - 8},${d.y - 8})`);
      }
    </script>
  </body>
</html>

Sign up to request clarification or add additional context in comments.

3 Comments

Hi Mark, thank you for your reply! I added the ticked function.
Ok, so my answer is correct then.
Yes! Thanks for the help.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.