I would like to create an application like scratch or node-red, with D3.js, by this I mean create some svg elements by clicking on a 'button list' to create an element and then drag them over an area to arrange them.
This idea is working with my code below. I can click to create shapes (svg group). Once created, I can click on them (AGAIN) and drag it over svg area.
But, I want to mimic the behavior of same apps node-red and scratch, by dragging the new svg element with the same click used to create it. Sparing a click, in one word. But I don't know how to start drag behavior programmatically on the element created. Here is my working code.
var svg = d3.select("body").append("svg")
.attr("width", 1500)
.attr("height", 800);
addButton(svg, 'ADD');
function addShape(svg, x, y) {
var dotContainer = svg.append("g")
.attr("class", "dotContainer")
.datum({
x: x,
y: y
})
.attr("transform", function(d) {
return 'translate(' + d.x + ' ' + d.y + ')';
})
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
var text = dotContainer.append("text")
.datum({
x: 20,
y: 20
})
.attr("x", function(d) {
return d.x;
})
.attr("y", function(d) {
return d.y;
})
.text('Title');
var rectangle = dotContainer.append("rect")
.attr("width", 200)
.attr("height", 100)
.attr("x", 0)
.attr("y", 0)
.attr('style', "opacity:1;fill:#ffffff;fill-opacity:0;stroke:#000000;stroke-width:5;stroke-opacity:1")
.attr("ry", 8);
return dotContainer;
}
function dragstarted(d) {
let xCoord = d3.event.dx - d3.select(this).attr('x')
let yCoord = d3.event.dy - d3.select(this).attr('y')
}
function dragged(d) {
d3.select(this).select("text").text(d.x + ';' + d.y);
d.x += d3.event.dx;
d.y += d3.event.dy;
d3.select(this).attr("transform", function(d, i) {
return "translate(" + [d.x, d.y] + ")"
});
}
function dragended(d) {
d3.select(this).attr("transform", function(d, i) {
return "translate(" + [d.x, d.y] + ")"
});
}
function addButton(area, title) {
var group = area.append("g");
group.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", 100)
.attr("height", 50)
.attr('style', 'fill:rgb(255,0,0);stroke-width:1;stroke:rgb(200,200,200)');
group.append("text")
.attr('x', 20)
.attr('y', 20)
.text(title);
group.on('mousedown', function() {
var grp = addShape(area, 0, 0);
//START DRAG ON grp HERE ???
});
}
<script src="https://d3js.org/d3.v5.min.js"></script>
So, my issue is here that I can't figure out how to call dragstarted() from outside of svg group dotContainer, since dragstarted use this and d, which refers to the svg group. Or use a complete other way to achieve this? I am lost here.... Thanks,
When in doubt, you can always reach back to vanilla JavaScript. In this case, you can dispatch a custom MouseDown event using the d3.event
object as the attribute dictionary, essentially cloning the element.
Then, the MouseMove
events take over and are processed seamlessly:
var svg = d3.select("body").append("svg")
.attr("width", 1500)
.attr("height", 800);
addButton(svg, 'ADD');
function addShape(svg, x, y) {
var dotContainer = svg.append("g")
.attr("class", "dotContainer")
.datum({
x: x,
y: y
})
.attr("transform", function(d) {
return 'translate(' + d.x + ' ' + d.y + ')';
})
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
var text = dotContainer.append("text")
.datum({
x: 20,
y: 20
})
.attr("x", function(d) {
return d.x;
})
.attr("y", function(d) {
return d.y;
})
.text('Title');
var rectangle = dotContainer.append("rect")
.attr("width", 200)
.attr("height", 100)
.attr("x", 0)
.attr("y", 0)
.attr('style', "opacity:1;fill:#ffffff;fill-opacity:0;stroke:#000000;stroke-width:5;stroke-opacity:1")
.attr("ry", 8);
return dotContainer;
}
function dragstarted(d) {
let xCoord = d3.event.dx - d3.select(this).attr('x')
let yCoord = d3.event.dy - d3.select(this).attr('y')
}
function dragged(d) {
d3.select(this).select("text").text(d.x + ';' + d.y);
d.x += d3.event.dx;
d.y += d3.event.dy;
d3.select(this).attr("transform", function(d, i) {
return "translate(" + [d.x, d.y] + ")"
});
}
function dragended(d) {
d3.select(this).attr("transform", function(d, i) {
return "translate(" + [d.x, d.y] + ")"
});
}
function addButton(area, title) {
var group = area.append("g");
group.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", 100)
.attr("height", 50)
.attr('style', 'fill:rgb(255,0,0);stroke-width:1;stroke:rgb(200,200,200)');
group.append("text")
.attr('x', 20)
.attr('y', 20)
.text(title);
group.on('mousedown', function() {
var grp = addShape(area, 0, 0);
grp.node().dispatchEvent(new MouseEvent(
"mousedown",
d3.event
));
});
}
<script src="https://d3js.org/d3.v5.js"></script>