Reputation: 573
I am trying to implement some sort of visualization when a user is pressing and holding the mouse button. Just like in first person shooter games where you have an automatic weapon, you can hold down mouse button and it remains firing. Instead in this case, I would be using d3 to produce lets say circles on a canvas. So if the user were to click and hold on the canvas, a bunch of circles would be added to the DOM and would continue to be added until they let go. Currently, this is what I have tried:
var drag = d3.behavior.drag()
.on('dragstart', function () {
d3.event.sourceEvent.stopPropagation();
d3.event.sourceEvent.preventDefault(); })
.on('drag', function (d) {
console.log('dragging')
});
This works fine and I see a bunch of 'dragging' in the console, but since I need to invoke a function that is associated with a class, I am having trouble passing in the proper this context such that I can call said function. I always get the error 'cannot find property 'X' of undefined' which makes perfect sense. How/Is it possible to pass in the context to the d3 drag behavior?
It is sorta funny since most of the posts I have looked for are always about removing multiple clicks or multiple clicks are firing.
Upvotes: 1
Views: 107
Reputation: 8970
You could do that with setInterval
in mousedown event and in mouseup you can remove the interval.
Please have a look at this demo jsFiddle. It is this demo with the click behaviour added.
The code could be improved because I think it is re-creating the node map at every click and it would be better to add the new node to the data and only add the new node to the force layout.
var width = 500,
height = 300;
var newNode = function() {
return {
radius: Math.random() * 12 + 4
};
},
nodes = d3.range(20).map(newNode),
root = nodes[0],
color = d3.scale.category10();
root.radius = 0;
root.fixed = true;
var force = d3.layout.force()
.gravity(0.05)
.charge(function(d, i) {
return i ? 0 : -2000;
})
.nodes(nodes)
.size([width, height]);
force.start();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var update = function() {
svg.selectAll("circle")
.data(nodes) //.slice(1))
.enter().append("circle")
.attr("r", function(d) {
return d.radius;
})
.style("fill", function(d, i) {
return color(i % 3);
});
};
update();
force.on("tick", function(e) {
var q = d3.geom.quadtree(nodes),
i = 0,
n = nodes.length;
while (++i < n) q.visit(collide(nodes[i]));
svg.selectAll("circle")
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
});
});
var intervalId = null,
pos = null;
var addNewNode = function() {
var nextNode = newNode();
nextNode.x = pos[0];
nextNode.y = pos[1];
nodes.push(nextNode);
d3.select('#debug').text('pos x=' + pos[0] + ',y=' + pos[1] + '-nodes=' + nodes.length);
update();
force.start();
};
svg.on('mousedown', function() {
//console.log('down');
//addNewNode(this);
pos = d3.mouse(this);
intervalId = setInterval(addNewNode, 100);
})
.on('mouseup', function() {
//console.log('up');
clearInterval(intervalId);
})
.on("mousemove", function() {
pos = d3.mouse(this);
root.px = pos[0];
root.py = pos[1];
force.resume();
})
.on("mouseleave", function() {
if (intervalId) clearInterval(intervalId);
});
function collide(node) {
var r = node.radius + 16,
nx1 = node.x - r,
nx2 = node.x + r,
ny1 = node.y - r,
ny2 = node.y + r;
return function(quad, x1, y1, x2, y2) {
if (quad.point && (quad.point !== node)) {
var x = node.x - quad.point.x,
y = node.y - quad.point.y,
l = Math.sqrt(x * x + y * y),
r = node.radius + quad.point.radius;
if (l < r) {
l = (l - r) / l * .5;
node.x -= x *= l;
node.y -= y *= l;
quad.point.x += x;
quad.point.y += y;
}
}
return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
};
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="debug"></div>
Upvotes: 1