Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to update an svg path with d3.js

I'd like to apply the "general update pattern" from official documentation to update an svg path on the mouse event (but could be a button or whatever).

But the the path is only added and not updated. I think it's why I'm not properly using the enter and exit property but after some various trial, I cannot manage to let it work.

Here is a jsfiddle.

My js code is here:

var shapeCoords = [
                  [10, 10], [100, 10], [100, 100], [10, 100]                  
                  ];

$(function() {
    var container = $('#container');

    // D3
    console.log("D3: ", d3);

    var svg = d3.select('#container')
                .append('svg:svg')
                .attr('height', 600)
                .attr('width', 800);

    var line = d3.svg.line()
                    .x(function(d) { return d[0]; })
                    .y(function(d) { return d[1]; })
                    .interpolate('linear');

    function render() {

            svg.data(shapeCoords)
                .append('svg:path')
                .attr('d', line(shapeCoords) + 'Z')
                .style('stroke-width', 1)
                .style('stroke', 'steelblue');
    }
    render();

    var mouseIsDown = false;  
    container.on('mousedown mouseup mousemove', function(e) {
        if (e.type == 'mousedown') {
            mouseIsDown = true; 
            shapeCoords[3] = [e.offsetX, e.offsetY];
        } else if (e.type == 'mouseup' ){
            mouseIsDown = false;
            shapeCoords[3] = [e.offsetX, e.offsetY];
        } else if (e.type == 'mousemove') {
            if (mouseIsDown) {
                shapeCoords[3] = [e.offsetX, e.offsetY];
                render();
            }
        }
    });


});

And the html:

<!DOCTYPE html>
<html>
<head>
    <title>D3 mousemove</title>
    <script type="text/javascript" 
            src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js">
    </script>
    <script type="text/javascript" 
            src="http://mbostock.github.com/d3/d3.js">
    </script>
    <script type="text/javascript" src="script.js"></script>
    <style>
        #container { 
            width: 800px; 
            height: 600px; 
            border: 1px solid silver; }
        path, line {
            stroke: steelblue;
            stroke-width: 1;
            fill: none;
        }
    </style>
</head> 
<body>   
    <div id="container"></div>
</body>
</html>
like image 674
nkint Avatar asked Mar 24 '14 22:03

nkint


1 Answers

Your code isn't selecting an existing element, so instead of updating the "d" attribute of an existing path via the update selection, it's appending a new path each time. This version of render() produced the expected behavior. More on selections here.

function render() {
    path = svg.selectAll('path').data([shapeCoords])
    path.attr('d', function(d){return line(d) + 'Z'})
        .style('stroke-width', 1)
        .style('stroke', 'steelblue');
    path.enter().append('svg:path').attr('d', function(d){return line(d) + 'Z'})
        .style('stroke-width', 1)
        .style('stroke', 'steelblue');
    path.exit().remove()

Once you run the data join on path via .data(), operations performed on path will only apply to the update selection. Meaning, only those existing elements that still have corresponding data elements under the new join. When you call enter().append() it will append a new element for every data element without a pre-existing element, and then apply the following operations only to those elements.

In the above, the first path.attr() above operates only on existing elements; those applied after path.enter() apply only to new elements. It's not featured in the snippet above, but enter() adds the enter selection to the update selection: any operations on path after calling enter() would apply to both existing and new elements.

like image 86
Sean Easter Avatar answered Sep 22 '22 17:09

Sean Easter