0

I'm working in a graph with 3 points in it. (0,0), (5,5), (10,10). The user is able to move the points across the y axis. Currently I'm able to move the points across the Y axis but not to "update" the path between the points when one of it is moving.

I'll apreciate any kind of help, thanks!

Here is the code, I'm using d3js v4:

<script>
    // Set the dimension of the canvas / graph
    var margin = { top: 30, right: 20, bottom: 30, left: 50 };
    var width = 600 - margin.left - margin.right;
    var height = 500 - margin.top - margin.bottom;

    // Add the svg canvas
    var svg = d3.select("body")
        .append("svg")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
        .append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    // Set the linear scale and the range for the axes
    var xRange = d3.scaleLinear().range([0, width]).domain([0, 10]);
    var yRange = d3.scaleLinear().range([height, 0]).domain([0, 10]);

    var line = d3.line()
        .x(d => xRange(d.x))
        .y(d => yRange(d.y))
        .curve(d3.curveLinear);

    var drag = d3.drag()
        .on("drag", function (d) {
            var dragPoint = d3.select(this);
            dragPoint.attr("cy", d.y = d3.event.y);
        });

    // Points dataset
    var radius = 6;
    var points = [{ x: 0, y: 0 }, { x: 5, y: 5 }, { x: 10, y: 10 }];

    // Add the X axis
    svg.append("g")
        .attr("transform", "translate(0," + height + ")")
        .call(d3.axisBottom(xRange));

    // Add the Y axis
    svg.append("g")
        .call(d3.axisLeft(yRange));

    // Add points to the svg
    var circles = svg.attr("class", "circle")
        .selectAll("circle")
        .data(points)
        .enter()
        .append("circle")
        .attr("cx", d => xRange(d.x))
        .attr("cy", d => yRange(d.y))
        .attr("r", radius)
        .attr("fill", "teal")
        .style("cursor", "pointer");

    // Add the path
    var path = svg.append("path")
        .attr("d", line(points));

    drag(circles);
</script>

1 Answers1

0

Your drag needs little adjustment.

Using https://stackoverflow.com/a/38650810/9938317 I changed the starting object so we start dragging at the circle position.

Then we need to invert the y-value for this point and fill it in the datum object. To limit the drag beyond the axis domain we first clamp it to the domain.

Then we update the path based on the updated points array.

var drag = d3.drag()
    .subject(function() { 
        var t = d3.select(this);
        return {x: t.attr("cx"), y: t.attr("cy")};
    })
    .on("drag", function (d) {
        var domain = yRange.domain();
        d.y = Math.max(domain[0], Math.min(yRange.invert(d3.event.y), domain[1]))
        d3.select(this).attr("cy", yRange(d.y));
        path.attr("d", line(points));
    });

A complete example

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://d3js.org/d3.v5.min.js"></script>
<style>
.line {stroke:steelblue; fill:none;}
</style>
</head>
<body>
<script>
    // Set the dimension of the canvas / graph
    var margin = { top: 30, right: 20, bottom: 30, left: 50 };
    var width = 600 - margin.left - margin.right;
    var height = 500 - margin.top - margin.bottom;

    // Add the svg canvas
    var svg = d3.select("body")
        .append("svg")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
        .append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    // Set the linear scale and the range for the axes
    var xRange = d3.scaleLinear().range([0, width]).domain([0, 10]);
    var yRange = d3.scaleLinear().range([height, 0]).domain([0, 10]);

    var line = d3.line()
        .x(d => xRange(d.x))
        .y(d => yRange(d.y))
        .curve(d3.curveLinear);

    var drag = d3.drag()
        .subject(function() { 
            var t = d3.select(this);
            return {x: t.attr("cx"), y: t.attr("cy")};
        })
        .on("drag", function (d) {
            var domain = yRange.domain();
            d.y = Math.max(domain[0], Math.min(yRange.invert(d3.event.y), domain[1]))
            d3.select(this).attr("cy", yRange(d.y));
            path.attr("d", line(points));
        });

    // Points dataset
    var radius = 6;
    var points = [{ x: 0, y: 0 }, { x: 5, y: 7 }, { x: 10, y: 10 }];

    // Add the X axis
    svg.append("g")
        .attr("transform", "translate(0," + height + ")")
        .call(d3.axisBottom(xRange));

    // Add the Y axis
    svg.append("g")
        .call(d3.axisLeft(yRange));

    // Add points to the svg
    var circles = svg.attr("class", "circle")
        .selectAll("circle")
        .data(points)
        .enter()
        .append("circle")
        .attr("cx", d => xRange(d.x))
        .attr("cy", d => yRange(d.y))
        .attr("r", radius)
        .attr("fill", "teal")
        .style("cursor", "pointer");

    // Add the path
    var path = svg.append("path")
        .attr("class", "line")
        .attr("d", line(points));

    drag(circles);
</script>
</body>
</html>
rioV8
  • 24,506
  • 3
  • 32
  • 49