Rohit Chakraborty
Rohit Chakraborty

Reputation: 83

How to include gradient color in the edges of a force-directed graph using d3.js?

As a beginner I am having problem in including the gradient color in edges of a force directed graph.

Here I have added the color to all the nodes and links through style tag


<style>
    .node{
        fill: blue;
        stroke: black;
        stroke-width: 2px;
    }

    .link{
        stroke: #777;
        stroke-width: 2px;
    }
</style>

This results in coloring of all edges in black... And I want to add the gradient color to the edges.. Similar to the image shared below

enter image description here


<!DOCTYPE html>
<meta charset="utf-8">
<style>
    .node{
        fill: blue;
        stroke: black;
        stroke-width: 2px;
    }

    .link{
        stroke: #777;
        stroke-width: 2px;
    }
</style>
<body>
    <script src="https://d3js.org/d3.v3.min.js"></script>
    <script>
        var width = 640,
            height = 480;

        var links = [
            {source: 'Rohit', target: 'Deep'},
            {source: 'Deep', target: 'Deepa'},
            {source: 'Deepa', target: 'Rohit'},
        ];
        var nodes = {};

        //adding to nodes
        links.forEach(function(link){
            link.source = nodes[link.source] ||
                (nodes[link.source] = {name: link.source});

            link.target = nodes[link.target] ||
                (nodes[link.target] = {name: link.target});
        });

        //adding svg to body

        var svg = d3.select('body').append('svg')
            .attr('width', width)
            .attr('height', height);

        var force = d3.layout.force()
            .size([width, height])
            .nodes(d3.values(nodes))
            .links(links)
            .on("tick", tick)
            .linkDistance(300)
            .start();

        var link = svg.selectAll('.link')
            .data(links)
            .enter().append('line')
            .attr('class', 'link');

        var node = svg.selectAll('.node')
            .data(force.nodes())
            .enter().append('circle')
            .attr('class', 'node')
            .attr('r', width*0.003);

        //define the tick func.
        function tick(e){
            node
                .attr('cx', function(d){
                return d.x;
                })
                .attr('cy', function(d){
                    return d.y;
                })
                .call(force.drag);
            
            link
                .attr('x1', function(d){
                return d.source.x;
                })
                .attr('y1', function(d){
                return d.source.y;
                })
                .attr('x2', function(d){
                return d.target.x;
                })
                .attr('y2', function(d){
                return d.target.y;
                })

        }


    </script>
</body>

Here is the full script

Upvotes: 1

Views: 370

Answers (1)

Syed Jafri
Syed Jafri

Reputation: 476

You can use the linearGradient. For the future, and any styling needs you might have, just check the MDN docs with the SVG youre working with. In most cases it is less of a 'can D3 style a certain way' and more of a 'can SVGs be styled this way'.

The var defs = svg.append('defs'); is appending a defs block to the DOM. This is used to "store graphical objects that will be used at a later time", in our case, it will be a <linearGradient/> that will be used later (on our line).

The var gradient = defs .append('linearGradient') is where we define our gradient, where it starts, and its beginning and end colors. As with a line we can add the x1,x2,y1, and y2 to detail the starting endpoint of the line both on the x axis and y axis. When using percentages, you can define the start and end points of the gradient. If you play around and use

          .attr('x1', '0%')
          .attr('x2', '0%')
          .attr('y1', '90%')
          .attr('y2', '100%');

you can observe most of the line being solid red with very little ease into the blue. Although the same effect could be achieved using the stop offsets.

The .append('stop') is where we actually define the start and end colors, and how much of the line they take up.

<!DOCTYPE html>
<meta charset="utf-8">
<style>
    .node{
        fill: blue;
        stroke: black;
        stroke-width: 2px;
    }

    .link{
        stroke-width: 2px;
    }
</style>
<body>
    <script src="https://d3js.org/d3.v3.min.js"></script>
    <script>
        var width = 640,
            height = 480;

        var links = [
            {source: 'Rohit', target: 'Deep'},
            {source: 'Deep', target: 'Deepa'},
            {source: 'Deepa', target: 'Rohit'},
        ];
        var nodes = {};

        //adding to nodes
        links.forEach(function(link){
            link.source = nodes[link.source] ||
                (nodes[link.source] = {name: link.source});

            link.target = nodes[link.target] ||
                (nodes[link.target] = {name: link.target});
        });

        //adding svg to body

        var svg = d3.select('body').append('svg')
            .attr('width', width)
            .attr('height', height);

        var defs = svg.append('defs');

        var gradient = defs
          .append('linearGradient')
          .attr('id', 'svgGradient')

        gradient
          .append('stop')
          .attr('class', 'start')
          .attr('offset', '0%')
          .attr('stop-color', 'red')
          .attr('stop-opacity', 1);

        gradient
          .append('stop')
          .attr('class', 'end')
          .attr('offset', '100%')
          .attr('stop-color', 'blue')
          .attr('stop-opacity', 1);

        var force = d3.layout.force()
            .size([width, height])
            .nodes(d3.values(nodes))
            .links(links)
            .on("tick", tick)
            .linkDistance(300)
            .start();

        var link = svg.selectAll('.link')
            .data(links)
            .enter().append('line')
            .attr('class', 'link')
            .attr('stroke', 'url(#svgGradient)');

        var node = svg.selectAll('.node')
            .data(force.nodes())
            .enter().append('circle')
            .attr('class', 'node')
            .attr('r', width*0.003);

        //define the tick func.
        function tick(e){
            node
                .attr('cx', function(d){
                return d.x;
                })
                .attr('cy', function(d){
                    return d.y;
                })
                .call(force.drag);
            
            link
                .attr('x1', function(d){
                return d.source.x;
                })
                .attr('y1', function(d){
                return d.source.y;
                })
                .attr('x2', function(d){
                return d.target.x;
                })
                .attr('y2', function(d){
                return d.target.y;
                })

        }


    </script>
</body>

Upvotes: 1

Related Questions