How to update an svg path with d3.js

2020-02-23 08:15发布

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>

2条回答
冷血范
2楼-- · 2020-02-23 08:19

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.

查看更多
▲ chillily
3楼-- · 2020-02-23 08:44

Please try the following code, I think it's what you want. The problem is caused because you should distinguish enter, update and exit when you want to update elements, otherwise it will add data again and again.

$(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');

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

function render() {

    var svg = d3.select('#container').select("svg").selectAll('path').data(shapeCoords);
    svg.enter().append('svg:path')
            .attr('d', line(shapeCoords) + 'Z')
            .style('stroke-width', 1)
            .style('stroke', 'steelblue');
    svg.attr('d', line(shapeCoords) + 'Z')
            .style('stroke-width', 1)
            .style('stroke', 'steelblue');    
    svg.exit().remove();    

}
render();
查看更多
登录 后发表回答