Adrian Wajs
Adrian Wajs

Reputation: 137

d3 path gradient stroke

Hello I am working with d3 diagonal diagram and would like to add a gradient to path which links my circles...

I am generating my tree with:

            var width = 800,
                height = 700;

            element.html('');
            var color = d3.interpolateLab("#008000", "#c83a22");
            var scale = d3.scale.linear().domain([0, 100]).range(["red", "green"]);
            var cluster = d3.layout.cluster()
                .size([height, width - 160]);

            var diagonal = d3.svg.diagonal()
                .projection(function(d) { return [d.y, d.x]; });

            var svg = d3.select('#tab-manageAccess').append('svg')
                .attr('width', width)
                .attr('height', height)
                .append('g')
                .attr('transform', 'translate(40,0)');


            /*svg.append("linearGradient")
                .attr("id", "line-gradient")
                .attr("gradientUnits", "userSpaceOnUse")
                .attr("x1", 0).attr("y1", y(0))
                .attr("x2", 0).attr("y2", y(1000))
                .selectAll("stop")
                .data([
                    {offset: "0%", color: "red"},
                    {offset: "40%", color: "red"},
                    {offset: "40%", color: "black"},
                    {offset: "62%", color: "black"},
                    {offset: "62%", color: "lawngreen"},
                    {offset: "100%", color: "lawngreen"}
                ])
                .enter().append("stop")
                .attr("offset", function(d) { return d.offset; })
                .attr("stop-color", function(d) { return d.color; });*/

            var nodes = cluster.nodes(scope.accessTree),
                links = cluster.links(nodes);

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

            var node = svg.selectAll('.node')
                .data(nodes)
                .enter().append('g')
                .attr('class', 'node')
                .attr('transform', function(d) { return 'translate(' + d.y + ',' + d.x + ')'; });

            node.append('circle')
                .attr('r', 4.5);

            node.append('text')
                .attr('dx', function(d) { return d.children ? -8 : 8; })
                .attr('dy', 3)
                .style('text-anchor', function(d) { return d.children ? 'end' : 'start'; })
                .style('font-weight', 'bold')
                .attr('fill', function (d) {
                    var color = '#4D7B88';
                    if (d.depth === 0) {
                        color = '#7F3762';
                    } else if(d.depth === 1) {
                        color = '#83913D';
                    }

                    return color;
                })
                .text(function(d) { return d.name; });

            d3.select(self.frameElement).style('height', height + 'px');

I found this example: https://gist.github.com/mbostock/4163057 co I created variable color with d3.interpolateLab("#008000", "#c83a22"); and then added .style("fill", function(d) { return color(d.t); }) .style("stroke", function(d) { return color(d.t); }) to path element but it doesn't work :( can anyone help me?

Upvotes: 3

Views: 6388

Answers (1)

AmeliaBR
AmeliaBR

Reputation: 27544

The aspect of Mike Bostock's code that you're missing is where he divides the path up into hundreds of different sub-paths and sets the color on each one separately. Go to the live version at http://bl.ocks.org/mbostock/4163057 and check the DOM to see what's really going on.

Why does he do that? Because, while you can set the stroke of an SVG line or path to a gradient, you can't tell it to make the gradient follow the slope or curve of that line. The angle of the gradient is defined when the gradient is created, based on either:

  • the rectangular bounding box for the element that uses it
    (if gradientUnits is set to ObjectBoundingBox), or

  • the user coordinate system where the object is drawn
    (if gradientUnits is set to userSpaceOnUse).

The way you have it set up (in your commented out code) basically creates a hidden gradient background over the entire image, and then lets it show through wherever you draw your lines. Clearly not what you wanted.

Hence, Mike's complex function and the hundreds of sub-paths it creates. Probably not what you want, either, especially if you want the graph to be interactive.

For simple lines, there is another way to get gradients to line up correctly from start to finish of your line.
I've got a very simple example with plain SVG (no D3) up here: http://codepen.io/AmeliaBR/pen/rFtGs

In short, you have to define your line to go in the direction that matches up with the gradient, and then use transforms (scale/rotate/translate) to actually position the line where you want it.

How tricky that would be to implement in D3 depends on how complex your layout is. If you were just using simple lines, I think this would work:

  • calculate the length of the line and its slope using simple geometry from the (x1,y1) and (x2,y2) values,

  • draw the line from (0,0) to (0,length) (assuming a vertical gradient),

  • add a transform attribute of translate(x1,y1) rotate(slope)

With paths, you'd need to know what type of path you're dealing with and use regular expressions to parse and edit the path's d attribute. Very messy.

Maybe just try line markers for start and end?

Upvotes: 7

Related Questions