Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(396)

Side by Side Diff: chrome/browser/resources/touch_ntp/grabber.js

Issue 6661024: Use a specialized new tab page in TOUCH_UI builds (Closed) Base URL: http://git.chromium.org/git/chromium.git@trunk
Patch Set: Tweaks based on CR feedback from arv Created 9 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 /**
6 * @fileoverview Grabber implementation.
7 * Allows you to pick up objects (with a long-press) and drag them around the
8 * screen.
9 *
10 * Note: This should perhaps really use standard drag-and-drop events, but there
11 * is no standard for them on touch devices. We could define a model for
12 * activating touch-based dragging of elements (programatically and/or with
13 * CSS attributes) and use it here (even have a JS library to generate such
14 * events when the browser doesn't support them).
15 */
16
17 /**
18 * Create a Grabber object to enable grabbing and dragging a given element.
19 * @constructor
20 * @param {!Element} element The element that can be grabbed and moved.
21 */
22 function Grabber(element) {
23 /**
24 * The element the grabber is attached to.
25 * @type {!Element}
26 * @private
27 */
28 this.element_ = element;
29
30 /**
31 * The TouchHandler responsible for firing lower-level touch events when the
32 * element is manipulated.
33 * @type {!TouchHandler}
34 * @private
35 */
36 this.touchHandler_ = new TouchHandler(this.element_);
37
38 /**
39 * Tracks all event listeners we have created.
40 * @type {EventTracker}
41 * @private
42 */
43 this.events_ = new EventTracker();
44
45 // TODO: Switch to getter syntax for properties once there is an official
arv (Not doing code reviews) 2011/03/11 20:08:47 Sad
Rick Byers 2011/03/15 21:47:54 Fixed
46 // JSCompiler build that supports the syntax (via --language_in=ECMASCRIPT5)
47 Object.defineProperty(this, 'element', {
48 value: element,
49 writable: false,
50 enumerable: true
51 });
52
arv (Not doing code reviews) 2011/03/11 20:08:47 We can skip this.element_ now and replace all usag
Rick Byers 2011/03/15 21:47:54 Done.
53 //
54 // Enable the generation of events when the element is touched (but no need to
55 // use the early capture phase of event processing).
56 this.touchHandler_.enable(/* opt_capture */ false);
57
58 // Prevent any built-in drag-and-drop support from activating for the element.
59 // Note that we don't want details of how we're implementing dragging here to
60 // leak out of this file (eg. we may switch to using webkit drag-and-drop).
61 this.events_.add(this.element_, 'dragstart', function(e) {
62 e.preventDefault();
63 }, true);
64
65 // Add our TouchHandler event listeners
66 this.events_.add(this.element_, TouchHandler.EventType.TOUCH_START,
67 this.onTouchStart_.bind(this), false);
68 this.events_.add(this.element_, TouchHandler.EventType.LONG_PRESS,
69 this.onLongPress_.bind(this), false);
70 this.events_.add(this.element_, TouchHandler.EventType.DRAG_START,
71 this.onDragStart_.bind(this), false);
72 this.events_.add(this.element_, TouchHandler.EventType.DRAG_MOVE,
73 this.onDragMove_.bind(this), false);
74 this.events_.add(this.element_, TouchHandler.EventType.DRAG_END,
75 this.onDragEnd_.bind(this), false);
76 this.events_.add(this.element_, TouchHandler.EventType.TOUCH_END,
77 this.onTouchEnd_.bind(this), false);
78 }
79
80 /**
81 * Events fired by the grabber.
82 * Events are fired at the element affected (not the element being dragged).
83 * @enum {string}
84 */
85 Grabber.EventType = {
86 // Fired at the grabber element when it is first grabbed
87 GRAB: 'grabber:grab',
88 // Fired at the grabber element when dragging begins (after GRAB)
89 DRAG_START: 'grabber:dragstart',
90 // Fired at an element when something is dragged over top of it.
91 DRAG_ENTER: 'grabber:dragenter',
92 // Fired at an element when something is no longer over top of it.
93 // Not fired at all in the case of a DROP
94 DRAG_LEAVE: 'grabber:drag',
95 // Fired at an element when something is dropped on top of it.
96 DROP: 'grabber:drop',
97 // Fired at the grabber element when dragging ends (successfully or not) -
98 // after any DROP or DRAG_LEAVE
99 DRAG_END: 'grabber:dragend',
100 // Fired at the grabber element when it is released (even if no drag
101 // occured) - after any DRAG_END event.
102 RELEASE: 'grabber:release'
103 };
104
105 /**
106 * The CSS class to apply when an element is touched but not yet
107 * grabbed.
108 * @type {string}
109 */
110 Grabber.PRESSED_CLASS = 'grabber-pressed';
111
112 /**
113 * The class to apply when an element has been held (including when it is
114 * being dragged.
115 * @type {string}
116 */
117 Grabber.GRAB_CLASS = 'grabber-grabbed';
118
119 /**
120 * The class to apply when a grabbed element is being dragged.
121 * @type {string}
122 */
123 Grabber.DRAGGING_CLASS = 'grabber-dragging';
124
125 Grabber.prototype = {
126
Rick Byers 2011/03/11 02:44:33 Darn - reitveld did a poor job of matching this di
127 /**
128 * Clean up all event handlers (eg. if the underlying element will be removed)
129 * @this {Grabber}
130 */
131 dispose: function() {
132 this.touchHandler_.disable();
133 this.events_.removeAll();
134
135 // Clean-up any active touch/drag
136 if (this.dragging_)
137 this.stopDragging_();
138 this.onTouchEnd_();
139 },
140
141 /**
142 * Invoked whenever this element is first touched
143 * @this {Grabber}
144 * @param {!CustomEvent} e The TouchHandler event.
145 * @private
146 */
147 onTouchStart_: function(e) {
148 this.element_.classList.add(Grabber.PRESSED_CLASS);
149
150 // Always permit the touch to perhaps trigger a drag
151 e.detail.enableDrag = true;
152 },
153
154 /**
155 * Invoked whenever the element stops being touched.
156 * Can be called explicitly to cleanup any active touch.
157 * @this {Grabber}
158 * @param {!CustomEvent=} opt_e The TouchHandler event.
159 * @private
160 */
161 onTouchEnd_: function(opt_e) {
162 if (this.grabbed_) {
163 // Mark this element as no longer being grabbed
164 this.element_.classList.remove(Grabber.GRAB_CLASS);
165 this.grabbed_ = false;
166
167 this.dispatchEvent_(Grabber.EventType.RELEASE);
168 }
169 else
170 {
arv (Not doing code reviews) 2011/03/11 20:08:47 } else {
Rick Byers 2011/03/15 21:47:54 Done.
171 this.element_.classList.remove(Grabber.PRESSED_CLASS);
172 }
173 },
174
175 /**
176 * Handler for TouchHandler's LONG_PRESS event
177 * Invoked when the element is held (without being dragged)
178 * @this {Grabber}
179 * @param {!CustomEvent} e The TouchHandler event.
180 * @private
181 */
182 onLongPress_: function(e) {
183 assert(!this.grabbed_, 'Got longPress while still being held');
184
185 this.element_.classList.remove(Grabber.PRESSED_CLASS);
186 this.element_.classList.add(Grabber.GRAB_CLASS);
187 this.grabbed_ = true;
188
189 this.dispatchEvent_(Grabber.EventType.GRAB);
190 },
191
192 /**
193 * Invoked when the element is dragged.
194 * @this {Grabber}
195 * @param {!CustomEvent} e The TouchHandler event.
196 * @private
197 */
198 onDragStart_: function(e) {
199 assert(!this.lastEnter_, 'only expect one drag to occur at a time');
200 assert(!this.dragging_);
201
202 // We only want to drag the element if its been grabbed
203 if (this.grabbed_) {
204 // Mark the item as being dragged
205 // Ensures our translate transform won't be animated and cancels any
206 // outstanding animations.
207 this.element_.classList.add(Grabber.DRAGGING_CLASS);
208
209 // Determine the webkitTransform currently applied to the element.
210 // Note that it's important that we do this AFTER cancelling animation,
211 // otherwise we could see an intermediate value.
212 // We'll assume this value will be constant for the duration of the drag
213 // so that we can combine it with our translate3d transform.
214 this.baseTransform_ = this.element_.ownerDocument.defaultView.
215 getComputedStyle(this.element_).webkitTransform;
216
217 this.dispatchEvent_(Grabber.EventType.DRAG_START);
218 e.detail.enableDrag = true;
219 this.dragging_ = true;
220 } else {
221 // Hasn't been grabbed - don't drag, just unpress
222 this.element_.classList.remove(Grabber.PRESSED_CLASS);
223 e.detail.enableDrag = false;
224 }
225 },
226
227 /**
228 * Invoked when a grabbed element is being dragged
229 * @this {Grabber}
230 * @param {!CustomEvent} e The TouchHandler event.
231 * @private
232 */
233 onDragMove_: function(e) {
234 assert(this.grabbed_ && this.dragging_);
235
236 this.translateTo_(e.detail.dragDeltaX, e.detail.dragDeltaY);
237
238 var target = this.getCoveredElement_(e.detail);
239 if (target && target != this.lastEnter_) {
240 // Send the events
241 this.sendDragLeave_(e);
242 this.dispatchEventTo_(Grabber.EventType.DRAG_ENTER, target);
243 }
244 this.lastEnter_ = target;
245 },
246
247 /**
248 * Send DRAG_LEAVE to the element last sent a DRAG_ENTER if any.
249 * @this {Grabber}
250 * @param {!CustomEvent} e The event triggering this DRAG_LEAVE.
251 * @private
252 */
253 sendDragLeave_: function(e) {
254 if (this.lastEnter_) {
255 this.dispatchEventTo_(Grabber.EventType.DRAG_LEAVE, this.lastEnter_);
256 this.lastEnter_ = undefined;
257 }
258 },
259
260 /**
261 * Moves the element to the specified position.
262 * @this {Grabber}
263 * @param {number} x Horizontal position to move to.
264 * @param {number} y Vertical position to move to.
265 * @private
266 */
267 translateTo_: function(x, y) {
268 // Order is important here - we want to translate before doing the zoom
269 this.element_.style.webkitTransform = 'translate3d(' + x + 'px, ' +
arv (Not doing code reviews) 2011/03/11 20:08:47 WebkitTransform for consistency with the name conv
Rick Byers 2011/03/15 21:47:54 Done.
270 y + 'px, 0) ' + this.baseTransform_;
271 },
272
273 /**
274 * Get the element being covered by a given touch.
275 * @this {Grabber}
276 * @param {TouchHandler.EventDetail} touch The details of the touch event
277 * indicating the position to check.
278 * @return {Element} The element under the touch or null.
279 * @private
280 */
281 getCoveredElement_: function(touch) {
282 // Ensure the element being dragged doesn't get in the way
283 // It's unfortunate that there's not a better way to do this (such as a
284 // 'elementsFromPoint' API that returns the elements in stack order).
285 // Relying on pointerEvents (instead of display or zIndex for example)
286 // should ensure that we don't trigger a relayout.
arv (Not doing code reviews) 2011/03/11 20:08:47 Actually, why don't you set "pointer-events: none"
Rick Byers 2011/03/15 21:47:54 Nice, I like it. It complicates my TouchHandler a
287 var origEvents = this.element_.style.pointerEvents || '';
288 this.element_.style.pointerEvents = 'none';
289 var target = document.elementFromPoint(touch.clientX, touch.clientY);
290 this.element_.style.pointerEvents = origEvents;
291
292 return target;
293 },
294
295 /**
296 * Invoked when the element is no longer being dragged.
297 * @this {Grabber}
298 * @param {CustomEvent} e The TouchHandler event.
299 * @private
300 */
301 onDragEnd_: function(e) {
302 // We should get this before the onTouchEnd. Don't change
303 // this.grabbed_ - it's onTouchEnd's responsibility to clear it.
304 assert(this.grabbed_ && this.dragging_);
305 var event;
306
307 // Try to determine what element is underneath us
308 var target = this.getCoveredElement_(e.detail);
309 if (target)
310 this.dispatchEventTo_(Grabber.EventType.DROP, target);
311
312 // Cleanup and send DRAG_END
313 // Note that like HTML5 DND, we don't send DRAG_LEAVE on drop
314 this.stopDragging_();
315 },
316
317 /**
318 * Clean-up the active drag and send DRAG_LEAVE
319 * @this {Grabber}
320 * @private
321 */
322 stopDragging_: function() {
323 assert(this.dragging_);
324 this.lastEnter_ = undefined;
325
326 // Mark the element as no longer being dragged
327 this.element_.classList.remove(Grabber.DRAGGING_CLASS);
328 this.element_.style.webkitTransform = '';
329
330 this.dragging_ = false;
331 this.dispatchEvent_(Grabber.EventType.DRAG_END);
332 },
333
334 /**
335 * Send a Grabber event to a specific element
336 * @this {Grabber}
337 * @param {string} eventType The type of event to send.
338 * @param {!Element} target The element to send the event to.
339 * @private
340 */
341 dispatchEventTo_: function(eventType, target) {
342 var event = document.createEvent('Event');
343 event.initEvent(eventType, true, true);
344 event.sender = this;
345 target.dispatchEvent(event);
346 },
347
348 /**
349 * Send a Grabber event to the grabbed element
350 * @this {Grabber}
351 * @param {string} eventType The type of event to send.
352 * @private
353 */
354 dispatchEvent_: function(eventType) {
355 this.dispatchEventTo_(eventType, this.element_);
356 },
357
358 /**
359 * Whether or not the element is currently grabbed.
360 * @type {boolean}
361 * @private
362 */
363 grabbed_: false,
364
365 /**
366 * Whether or not the element is currently being dragged.
367 * @type {boolean}
368 * @private
369 */
370 dragging_: false,
371
372 /**
373 * The webkitTransform applied to the element when it first started being
374 * dragged.
375 * @type {string|undefined}
376 * @private
377 */
378 baseTransform_: undefined,
379
380 /**
381 * The element for which a DRAG_ENTER event was last fired
382 * @type {Element|undefined}
383 * @private
384 */
385 lastEnter_: undefined
386 };
387
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698