Skip to content

Commit dceb459

Browse files
committed
Multitouch dragging!
1 parent 38a6c30 commit dceb459

File tree

5 files changed

+71
-68
lines changed

5 files changed

+71
-68
lines changed

d3.v2.js

+22-23
Original file line numberDiff line numberDiff line change
@@ -3615,21 +3615,23 @@
36153615
this.on("mousedown.drag", mousedown).on("touchstart.drag", mousedown);
36163616
}
36173617
function mousedown() {
3618-
var target = this, event_ = event.of(target, arguments), eventTarget = d3.event.target, offset, origin_ = point(), moved = 0;
3619-
var w = d3.select(window).on("mousemove.drag", dragmove).on("touchmove.drag", dragmove).on("mouseup.drag", dragend, true).on("touchend.drag", dragend, true);
3618+
var target = this, event_ = event.of(target, arguments), eventTarget = d3.event.target, touchId = d3.event.touches && d3.event.changedTouches[0].identifier, offset, origin_ = point(), moved = 0;
3619+
var w = d3.select(window).on(touchId ? "touchmove.drag-" + touchId : "mousemove.drag", dragmove).on(touchId ? "touchend.drag-" + touchId : "mouseup.drag", dragend, true);
36203620
if (origin) {
36213621
offset = origin.apply(target, arguments);
36223622
offset = [ offset.x - origin_[0], offset.y - origin_[1] ];
36233623
} else {
36243624
offset = [ 0, 0 ];
36253625
}
3626-
d3_eventCancel();
3626+
if (!touchId) d3_eventCancel();
36273627
event_({
36283628
type: "dragstart"
36293629
});
36303630
function point() {
3631-
var p = target.parentNode, t = d3.event.changedTouches;
3632-
return t ? d3.touches(p, t)[0] : d3.mouse(p);
3631+
var p = target.parentNode;
3632+
return touchId ? d3.touches(p).filter(function(p) {
3633+
return p.identifier === touchId;
3634+
})[0] : d3.mouse(p);
36333635
}
36343636
function dragmove() {
36353637
if (!target.parentNode) return dragend();
@@ -3653,7 +3655,7 @@
36533655
d3_eventCancel();
36543656
if (d3.event.target === eventTarget) w.on("click.drag", click, true);
36553657
}
3656-
w.on("mousemove.drag", null).on("touchmove.drag", null).on("mouseup.drag", null).on("touchend.drag", null);
3658+
w.on(touchId ? "touchmove.drag-" + touchId : "mousemove.drag", null).on(touchId ? "touchend.drag-" + touchId : "mouseup.drag", null);
36573659
}
36583660
function click() {
36593661
d3_eventCancel();
@@ -4164,30 +4166,27 @@
41644166
return force.alpha(0);
41654167
};
41664168
force.drag = function() {
4167-
if (!drag) drag = d3.behavior.drag().origin(d3_identity).on("dragstart", dragstart).on("drag", d3_layout_forceDrag).on("dragend", d3_layout_forceDragEnd);
4168-
this.on("mouseover.force", d3_layout_forceDragOver).on("mouseout.force", d3_layout_forceDragOut).call(drag);
4169+
if (!drag) drag = d3.behavior.drag().origin(d3_identity).on("dragstart", d3_layout_forceDragstart).on("drag", dragmove).on("dragend", d3_layout_forceDragend);
4170+
this.on("mouseover.force", d3_layout_forceMouseover).on("mouseout.force", d3_layout_forceMouseout).call(drag);
41694171
};
4170-
function dragstart(d) {
4171-
d3_layout_forceDragOver(d3_layout_forceDragNode = d);
4172-
d3_layout_forceDragForce = force;
4172+
function dragmove(d) {
4173+
d.px = d3.event.x;
4174+
d.py = d3.event.y;
4175+
force.resume();
41734176
}
41744177
return d3.rebind(force, event, "on");
41754178
};
4176-
var d3_layout_forceDragForce, d3_layout_forceDragNode;
4177-
function d3_layout_forceDragOver(d) {
4178-
d.fixed |= 2;
4179+
function d3_layout_forceDragstart(d) {
4180+
d.fixed |= 1;
41794181
}
4180-
function d3_layout_forceDragOut(d) {
4181-
if (d !== d3_layout_forceDragNode) d.fixed &= 1;
4182+
function d3_layout_forceDragend(d) {
4183+
d.fixed &= 2;
41824184
}
4183-
function d3_layout_forceDragEnd() {
4184-
d3_layout_forceDragNode.fixed &= 1;
4185-
d3_layout_forceDragForce = d3_layout_forceDragNode = null;
4185+
function d3_layout_forceMouseover(d) {
4186+
d.fixed |= 2;
41864187
}
4187-
function d3_layout_forceDrag() {
4188-
d3_layout_forceDragNode.px = d3.event.x;
4189-
d3_layout_forceDragNode.py = d3.event.y;
4190-
d3_layout_forceDragForce.resume();
4188+
function d3_layout_forceMouseout(d) {
4189+
d.fixed &= 1;
41914190
}
41924191
function d3_layout_forceAccumulate(quad, alpha, charges) {
41934192
var cx = 0, cy = 0;

d3.v2.min.js

+4-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/drag/drag.html

+17-5
Original file line numberDiff line numberDiff line change
@@ -19,26 +19,38 @@
1919
radius = 120;
2020

2121
var drag = d3.behavior.drag()
22-
.origin(Object)
23-
.on("drag", dragmove);
22+
.origin(function(d) { return d; })
23+
.on("dragstart", dragstart)
24+
.on("drag", dragmove)
25+
.on("dragend", dragend);
2426

2527
var svg = d3.select("body").append("svg")
2628
.attr("width", width)
2729
.attr("height", height)
2830

29-
var circle = svg.append("circle")
30-
.data([{x: width / 2, y: height / 2}])
31+
var circle = svg.selectAll("circle")
32+
.data([1 / 3, 2 / 3])
33+
.enter().append("circle")
34+
.datum(function(d) { return {x: width * d, y: height / 2}; })
3135
.attr("r", radius)
3236
.attr("cx", function(d) { return d.x; })
3337
.attr("cy", function(d) { return d.y; })
3438
.call(drag);
3539

40+
function dragstart() {
41+
d3.select(this).style("fill", "red");
42+
}
43+
3644
function dragmove(d) {
37-
circle
45+
d3.select(this)
3846
.attr("cx", d.x = Math.max(radius, Math.min(width - radius, d3.event.x)))
3947
.attr("cy", d.y = Math.max(radius, Math.min(height - radius, d3.event.y)));
4048
}
4149

50+
function dragend() {
51+
d3.select(this).style("fill", null);
52+
}
53+
4254
</script>
4355
</body>
4456
</html>

src/behavior/drag.js

+11-14
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
// TODO Track touch points by identifier.
2-
31
d3.behavior.drag = function() {
42
var event = d3_eventDispatch(drag, "drag", "dragstart", "dragend"),
53
origin = null;
@@ -13,15 +11,14 @@ d3.behavior.drag = function() {
1311
var target = this,
1412
event_ = event.of(target, arguments),
1513
eventTarget = d3.event.target,
14+
touchId = d3.event.touches && d3.event.changedTouches[0].identifier,
1615
offset,
1716
origin_ = point(),
1817
moved = 0;
1918

2019
var w = d3.select(window)
21-
.on("mousemove.drag", dragmove)
22-
.on("touchmove.drag", dragmove)
23-
.on("mouseup.drag", dragend, true)
24-
.on("touchend.drag", dragend, true);
20+
.on(touchId ? "touchmove.drag-" + touchId : "mousemove.drag", dragmove)
21+
.on(touchId ? "touchend.drag-" + touchId : "mouseup.drag", dragend, true);
2522

2623
if (origin) {
2724
offset = origin.apply(target, arguments);
@@ -30,13 +27,15 @@ d3.behavior.drag = function() {
3027
offset = [0, 0];
3128
}
3229

33-
d3_eventCancel();
30+
// Only cancel mousedown; touchstart is needed for draggable links.
31+
if (!touchId) d3_eventCancel();
3432
event_({type: "dragstart"});
3533

3634
function point() {
37-
var p = target.parentNode,
38-
t = d3.event.changedTouches;
39-
return t ? d3.touches(p, t)[0] : d3.mouse(p);
35+
var p = target.parentNode;
36+
return touchId
37+
? d3.touches(p).filter(function(p) { return p.identifier === touchId; })[0]
38+
: d3.mouse(p);
4039
}
4140

4241
function dragmove() {
@@ -62,10 +61,8 @@ d3.behavior.drag = function() {
6261
if (d3.event.target === eventTarget) w.on("click.drag", click, true);
6362
}
6463

65-
w .on("mousemove.drag", null)
66-
.on("touchmove.drag", null)
67-
.on("mouseup.drag", null)
68-
.on("touchend.drag", null);
64+
w .on(touchId ? "touchmove.drag-" + touchId : "mousemove.drag", null)
65+
.on(touchId ? "touchend.drag-" + touchId : "mouseup.drag", null);
6966
}
7067

7168
// prevent the subsequent click from propagating (e.g., for anchors)

src/layout/force.js

+17-22
Original file line numberDiff line numberDiff line change
@@ -274,43 +274,38 @@ d3.layout.force = function() {
274274
force.drag = function() {
275275
if (!drag) drag = d3.behavior.drag()
276276
.origin(d3_identity)
277-
.on("dragstart", dragstart)
278-
.on("drag", d3_layout_forceDrag)
279-
.on("dragend", d3_layout_forceDragEnd);
277+
.on("dragstart", d3_layout_forceDragstart)
278+
.on("drag", dragmove)
279+
.on("dragend", d3_layout_forceDragend);
280280

281-
this.on("mouseover.force", d3_layout_forceDragOver)
282-
.on("mouseout.force", d3_layout_forceDragOut)
281+
this.on("mouseover.force", d3_layout_forceMouseover)
282+
.on("mouseout.force", d3_layout_forceMouseout)
283283
.call(drag);
284284
};
285285

286-
function dragstart(d) {
287-
d3_layout_forceDragOver(d3_layout_forceDragNode = d);
288-
d3_layout_forceDragForce = force;
286+
function dragmove(d) {
287+
d.px = d3.event.x;
288+
d.py = d3.event.y;
289+
force.resume(); // restart annealing
289290
}
290291

291292
return d3.rebind(force, event, "on");
292293
};
293294

294-
var d3_layout_forceDragForce,
295-
d3_layout_forceDragNode;
296-
297-
function d3_layout_forceDragOver(d) {
298-
d.fixed |= 2;
295+
function d3_layout_forceDragstart(d) {
296+
d.fixed |= 1;
299297
}
300298

301-
function d3_layout_forceDragOut(d) {
302-
if (d !== d3_layout_forceDragNode) d.fixed &= 1;
299+
function d3_layout_forceDragend(d) {
300+
d.fixed &= 2;
303301
}
304302

305-
function d3_layout_forceDragEnd() {
306-
d3_layout_forceDragNode.fixed &= 1;
307-
d3_layout_forceDragForce = d3_layout_forceDragNode = null;
303+
function d3_layout_forceMouseover(d) {
304+
d.fixed |= 2;
308305
}
309306

310-
function d3_layout_forceDrag() {
311-
d3_layout_forceDragNode.px = d3.event.x;
312-
d3_layout_forceDragNode.py = d3.event.y;
313-
d3_layout_forceDragForce.resume(); // restart annealing
307+
function d3_layout_forceMouseout(d) {
308+
d.fixed &= 1;
314309
}
315310

316311
function d3_layout_forceAccumulate(quad, alpha, charges) {

0 commit comments

Comments
 (0)