source: Dev/trunk/d3/d3.behavior.js @ 201

Last change on this file since 201 was 76, checked in by fpvanagthoven, 14 years ago

d3

File size: 5.9 KB
Line 
1(function(){d3.behavior = {};
2// TODO unbind zoom behavior?
3// TODO unbind listener?
4d3.behavior.zoom = function() {
5  var xyz = [0, 0, 0],
6      event = d3.dispatch("zoom");
7
8  function zoom() {
9    this
10        .on("mousedown.zoom", mousedown)
11        .on("mousewheel.zoom", mousewheel)
12        .on("DOMMouseScroll.zoom", dblclick)
13        .on("dblclick.zoom", dblclick)
14        .on("touchstart.zoom", touchstart);
15
16    d3.select(window)
17        .on("mousemove.zoom", d3_behavior_zoomMousemove)
18        .on("mouseup.zoom", d3_behavior_zoomMouseup)
19        .on("touchmove.zoom", d3_behavior_zoomTouchmove)
20        .on("touchend.zoom", d3_behavior_zoomTouchup);
21  }
22
23  // snapshot the local context for subsequent dispatch
24  function start() {
25    d3_behavior_zoomXyz = xyz;
26    d3_behavior_zoomDispatch = event.zoom.dispatch;
27    d3_behavior_zoomTarget = this;
28    d3_behavior_zoomArguments = arguments;
29  }
30
31  function mousedown() {
32    start.apply(this, arguments);
33    d3_behavior_zoomPanning = d3_behavior_zoomLocation(d3.svg.mouse(d3_behavior_zoomTarget));
34    d3.event.preventDefault();
35    window.focus();
36  }
37
38  // store starting mouse location
39  function mousewheel() {
40    start.apply(this, arguments);
41    if (!d3_behavior_zoomZooming) d3_behavior_zoomZooming = d3_behavior_zoomLocation(d3.svg.mouse(d3_behavior_zoomTarget));
42    d3_behavior_zoomTo(d3_behavior_zoomDelta() + xyz[2], d3.svg.mouse(d3_behavior_zoomTarget), d3_behavior_zoomZooming);
43  }
44
45  function dblclick() {
46    start.apply(this, arguments);
47    var mouse = d3.svg.mouse(d3_behavior_zoomTarget);
48    d3_behavior_zoomTo(d3.event.shiftKey ? Math.ceil(xyz[2] - 1) : Math.floor(xyz[2] + 1), mouse, d3_behavior_zoomLocation(mouse));
49  }
50
51  // doubletap detection
52  function touchstart() {
53    start.apply(this, arguments);
54    var touches = d3_behavior_zoomTouchup(),
55        touch,
56        now = Date.now();
57    if ((touches.length === 1) && (now - d3_behavior_zoomLast < 300)) {
58      d3_behavior_zoomTo(1 + Math.floor(xyz[2]), touch = touches[0], d3_behavior_zoomLocations[touch.identifier]);
59    }
60    d3_behavior_zoomLast = now;
61  }
62
63  zoom.on = function(type, listener) {
64    event[type].add(listener);
65    return zoom;
66  };
67
68  return zoom;
69};
70
71var d3_behavior_zoomDiv,
72    d3_behavior_zoomPanning,
73    d3_behavior_zoomZooming,
74    d3_behavior_zoomLocations = {}, // identifier -> location
75    d3_behavior_zoomLast = 0,
76    d3_behavior_zoomXyz,
77    d3_behavior_zoomDispatch,
78    d3_behavior_zoomTarget,
79    d3_behavior_zoomArguments;
80
81function d3_behavior_zoomLocation(point) {
82  return [
83    point[0] - d3_behavior_zoomXyz[0],
84    point[1] - d3_behavior_zoomXyz[1],
85    d3_behavior_zoomXyz[2]
86  ];
87}
88
89// detect the pixels that would be scrolled by this wheel event
90function d3_behavior_zoomDelta() {
91
92  // mousewheel events are totally broken!
93  // https://bugs.webkit.org/show_bug.cgi?id=40441
94  // not only that, but Chrome and Safari differ in re. to acceleration!
95  if (!d3_behavior_zoomDiv) {
96    d3_behavior_zoomDiv = d3.select("body").append("div")
97        .style("visibility", "hidden")
98        .style("top", 0)
99        .style("height", 0)
100        .style("width", 0)
101        .style("overflow-y", "scroll")
102      .append("div")
103        .style("height", "2000px")
104      .node().parentNode;
105  }
106
107  var e = d3.event, delta;
108  try {
109    d3_behavior_zoomDiv.scrollTop = 1000;
110    d3_behavior_zoomDiv.dispatchEvent(e);
111    delta = 1000 - d3_behavior_zoomDiv.scrollTop;
112  } catch (error) {
113    delta = e.wheelDelta || -e.detail;
114  }
115
116  return delta * .005;
117}
118
119// Note: Since we don't rotate, it's possible for the touches to become
120// slightly detached from their original positions. Thus, we recompute the
121// touch points on touchend as well as touchstart!
122function d3_behavior_zoomTouchup() {
123  var touches = d3.svg.touches(d3_behavior_zoomTarget),
124      i = -1,
125      n = touches.length,
126      touch;
127  while (++i < n) d3_behavior_zoomLocations[(touch = touches[i]).identifier] = d3_behavior_zoomLocation(touch);
128  return touches;
129}
130
131function d3_behavior_zoomTouchmove() {
132  var touches = d3.svg.touches(d3_behavior_zoomTarget);
133  switch (touches.length) {
134
135    // single-touch pan
136    case 1: {
137      var touch = touches[0];
138      d3_behavior_zoomTo(d3_behavior_zoomXyz[2], touch, d3_behavior_zoomLocations[touch.identifier]);
139      break;
140    }
141
142    // double-touch pan + zoom
143    case 2: {
144      var p0 = touches[0],
145          p1 = touches[1],
146          p2 = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2],
147          l0 = d3_behavior_zoomLocations[p0.identifier],
148          l1 = d3_behavior_zoomLocations[p1.identifier],
149          l2 = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2, l0[2]];
150      d3_behavior_zoomTo(Math.log(d3.event.scale) / Math.LN2 + l0[2], p2, l2);
151      break;
152    }
153  }
154}
155
156function d3_behavior_zoomMousemove() {
157  d3_behavior_zoomZooming = null;
158  if (d3_behavior_zoomPanning) d3_behavior_zoomTo(d3_behavior_zoomXyz[2], d3.svg.mouse(d3_behavior_zoomTarget), d3_behavior_zoomPanning);
159}
160
161function d3_behavior_zoomMouseup() {
162  if (d3_behavior_zoomPanning) {
163    d3_behavior_zoomMousemove();
164    d3_behavior_zoomPanning = null;
165  }
166}
167
168function d3_behavior_zoomTo(z, x0, x1) {
169  var K = Math.pow(2, (d3_behavior_zoomXyz[2] = z) - x1[2]),
170      x = d3_behavior_zoomXyz[0] = x0[0] - K * x1[0],
171      y = d3_behavior_zoomXyz[1] = x0[1] - K * x1[1],
172      o = d3.event, // Events can be reentrant (e.g., focus).
173      k = Math.pow(2, z);
174
175  d3.event = {
176    scale: k,
177    translate: [x, y],
178    transform: function(sx, sy) {
179      if (sx) transform(sx, x);
180      if (sy) transform(sy, y);
181    }
182  };
183
184  function transform(scale, o) {
185    var domain = scale.__domain || (scale.__domain = scale.domain()),
186        range = scale.range().map(function(v) { return (v - o) / k; });
187    scale.domain(domain).domain(range.map(scale.invert));
188  }
189
190  try {
191    d3_behavior_zoomDispatch.apply(d3_behavior_zoomTarget, d3_behavior_zoomArguments);
192  } finally {
193    d3.event = o;
194  }
195
196  o.preventDefault();
197}
198})();
Note: See TracBrowser for help on using the repository browser.