OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 /** | 5 /** |
6 * @fileoverview Card slider implementation. Allows you to create interactions | 6 * @fileoverview Card slider implementation. Allows you to create interactions |
7 * that have items that can slide left to right to reveal additional items. | 7 * that have items that can slide left to right to reveal additional items. |
8 * Works by adding the necessary event handlers to a specific DOM structure | 8 * Works by adding the necessary event handlers to a specific DOM structure |
9 * including a frame, container and cards. | 9 * including a frame, container and cards. |
10 * - The frame defines the boundary of one item. Each card will be expanded to | 10 * - The frame defines the boundary of one item. Each card will be expanded to |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
72 */ | 72 */ |
73 this.touchHandler_ = new cr.ui.TouchHandler(this.container_); | 73 this.touchHandler_ = new cr.ui.TouchHandler(this.container_); |
74 } | 74 } |
75 | 75 |
76 /** | 76 /** |
77 * Events fired by the slider. | 77 * Events fired by the slider. |
78 * Events are fired at the container. | 78 * Events are fired at the container. |
79 */ | 79 */ |
80 CardSlider.EventType = { | 80 CardSlider.EventType = { |
81 // Fired when the user slides to another card. | 81 // Fired when the user slides to another card. |
82 CARD_CHANGED: 'cardSlider:card_changed' | 82 CARD_CHANGED: 'cardSlider:card_changed', |
| 83 CARD_CHANGE_ENDED: 'cardSlider:card_change_ended' |
83 }; | 84 }; |
84 | 85 |
85 | 86 |
86 /** | 87 /** |
87 * The time to transition between cards when animating. Measured in ms. | 88 * The time to transition between cards when animating. Measured in ms. |
88 * @type {number} | 89 * @type {number} |
89 * @private | 90 * @private |
90 * @const | 91 * @const |
91 */ | 92 */ |
92 CardSlider.TRANSITION_TIME_ = 200; | 93 CardSlider.TRANSITION_TIME_ = 200; |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
125 | 126 |
126 this.updateCardWidths_(); | 127 this.updateCardWidths_(); |
127 | 128 |
128 this.mouseWheelScrollAmount_ = 0; | 129 this.mouseWheelScrollAmount_ = 0; |
129 this.mouseWheelCardSelected_ = false; | 130 this.mouseWheelCardSelected_ = false; |
130 this.mouseWheelIsContinuous_ = false; | 131 this.mouseWheelIsContinuous_ = false; |
131 this.scrollClearTimeout_ = null; | 132 this.scrollClearTimeout_ = null; |
132 this.frame_.addEventListener('mousewheel', | 133 this.frame_.addEventListener('mousewheel', |
133 this.onMouseWheel_.bind(this)); | 134 this.onMouseWheel_.bind(this)); |
134 | 135 |
| 136 // Listen for the end of card movements (which is currently implemented |
| 137 // with -webkit-transitions of -webkit-transforms. |
| 138 this.container_.addEventListener('webkitTransitionEnd', |
| 139 this.onWebkitTransitionEnd_.bind(this)); |
| 140 |
135 // Also support touch events in case a touch screen happens to be | 141 // Also support touch events in case a touch screen happens to be |
136 // available. Ideally we would support touch events whenever they | 142 // available. Ideally we would support touch events whenever they |
137 // are fired, but for now restrict this extra code to when we know | 143 // are fired, but for now restrict this extra code to when we know |
138 // we want to support touch input. | 144 // we want to support touch input. |
139 if (cr.isTouchOptimized) { | 145 if (cr.isTouchOptimized) { |
140 var TouchHandler = cr.ui.TouchHandler; | 146 var TouchHandler = cr.ui.TouchHandler; |
141 this.container_.addEventListener(TouchHandler.EventType.TOUCH_START, | 147 this.container_.addEventListener(TouchHandler.EventType.TOUCH_START, |
142 this.onTouchStart_.bind(this)); | 148 this.onTouchStart_.bind(this)); |
143 this.container_.addEventListener(TouchHandler.EventType.DRAG_START, | 149 this.container_.addEventListener(TouchHandler.EventType.DRAG_START, |
144 this.onDragStart_.bind(this)); | 150 this.onDragStart_.bind(this)); |
(...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
290 * Resets the amount of horizontal scroll we've seen to 0. See | 296 * Resets the amount of horizontal scroll we've seen to 0. See |
291 * onMouseWheel_. | 297 * onMouseWheel_. |
292 * @private | 298 * @private |
293 */ | 299 */ |
294 clearMouseWheelScroll_: function() { | 300 clearMouseWheelScroll_: function() { |
295 this.mouseWheelScrollAmount_ = 0; | 301 this.mouseWheelScrollAmount_ = 0; |
296 this.mouseWheelCardSelected_ = false; | 302 this.mouseWheelCardSelected_ = false; |
297 }, | 303 }, |
298 | 304 |
299 /** | 305 /** |
| 306 * Handles the ends of -webkit-transitions on -webkit-transform (animated |
| 307 * card switches). |
| 308 * @param {Event} e The webkitTransitionEnd event. |
| 309 * @private |
| 310 */ |
| 311 onWebkitTransitionEnd_: function(e) { |
| 312 // Ignore irrelevant transitions that might bubble up. |
| 313 if (e.target !== this.container_ || |
| 314 e.propertyName != '-webkit-transform') { |
| 315 return; |
| 316 } |
| 317 this.fireChangeEndEvent_(); |
| 318 }, |
| 319 |
| 320 /** |
| 321 * Dispatches a simple event to tell subscribers we're done moving to the |
| 322 * newly selected card. |
| 323 * @private |
| 324 */ |
| 325 fireChangeEndEvent_: function() { |
| 326 var e = document.createEvent('Event'); |
| 327 e.initEvent(CardSlider.EventType.CARD_CHANGE_ENDED, true, true); |
| 328 e.cardSlider = this; |
| 329 this.container_.dispatchEvent(e); |
| 330 }, |
| 331 |
| 332 /** |
300 * Selects a new card, ensuring that it is a valid index, transforming the | 333 * Selects a new card, ensuring that it is a valid index, transforming the |
301 * view and possibly calling the change card callback. | 334 * view and possibly calling the change card callback. |
302 * @param {number} newCardIndex Index of card to show. | 335 * @param {number} newCardIndex Index of card to show. |
303 * @param {boolean=} opt_animate If true will animate transition from | 336 * @param {boolean=} opt_animate If true will animate transition from |
304 * current position to new position. | 337 * current position to new position. |
305 */ | 338 */ |
306 selectCard: function(newCardIndex, opt_animate) { | 339 selectCard: function(newCardIndex, opt_animate) { |
307 var previousCard = this.currentCardValue; | 340 var previousCard = this.currentCardValue; |
308 | 341 |
309 var isChangingCard = | 342 var isChangingCard = |
310 !this.cards_[newCardIndex].classList.contains('selected-card'); | 343 !this.cards_[newCardIndex].classList.contains('selected-card'); |
311 | 344 |
312 if (isChangingCard) { | 345 if (isChangingCard) { |
313 previousCard.classList.remove('selected-card'); | 346 if (previousCard) |
| 347 previousCard.classList.remove('selected-card'); |
314 // If we have a new card index and it is valid then update the left | 348 // If we have a new card index and it is valid then update the left |
315 // position and current card index. | 349 // position and current card index. |
316 this.currentCard_ = newCardIndex; | 350 this.currentCard_ = newCardIndex; |
317 this.currentCardValue.classList.add('selected-card'); | 351 this.currentCardValue.classList.add('selected-card'); |
318 } | 352 } |
319 | 353 |
320 this.transformToCurrentCard_(opt_animate); | 354 var willTransitionHappen = this.transformToCurrentCard_(opt_animate); |
321 | 355 |
322 if (isChangingCard) { | 356 if (isChangingCard) { |
323 var event = document.createEvent('Event'); | 357 var event = document.createEvent('Event'); |
324 event.initEvent(CardSlider.EventType.CARD_CHANGED, true, true); | 358 event.initEvent(CardSlider.EventType.CARD_CHANGED, true, true); |
325 event.cardSlider = this; | 359 event.cardSlider = this; |
326 this.container_.dispatchEvent(event); | 360 this.container_.dispatchEvent(event); |
327 | 361 |
328 // We also dispatch an event on the cards themselves. | 362 // We also dispatch an event on the cards themselves. |
329 if (previousCard) { | 363 if (previousCard) { |
330 cr.dispatchSimpleEvent(previousCard, 'carddeselected', | 364 cr.dispatchSimpleEvent(previousCard, 'carddeselected', |
331 true, true); | 365 true, true); |
332 } | 366 } |
333 cr.dispatchSimpleEvent(this.currentCardValue, 'cardselected', | 367 cr.dispatchSimpleEvent(this.currentCardValue, 'cardselected', |
334 true, true); | 368 true, true); |
335 } | 369 } |
| 370 |
| 371 // If we're not changing, animated, or transitioning, fire a |
| 372 // CARD_CHANGE_ENDED event right away. |
| 373 if (!isChangingCard || !opt_animate || !willTransitionHappen) |
| 374 this.fireChangeEndEvent_(); |
336 }, | 375 }, |
337 | 376 |
338 /** | 377 /** |
339 * Selects a card from the stack. Passes through to selectCard. | 378 * Selects a card from the stack. Passes through to selectCard. |
340 * @param {Node} newCard The card that should be selected. | 379 * @param {Node} newCard The card that should be selected. |
341 * @param {boolean=} opt_animate Whether to animate. | 380 * @param {boolean=} opt_animate Whether to animate. |
342 */ | 381 */ |
343 selectCardByValue: function(newCard, opt_animate) { | 382 selectCardByValue: function(newCard, opt_animate) { |
344 var i = this.cards_.indexOf(newCard); | 383 var i = this.cards_.indexOf(newCard); |
345 assert(i != -1); | 384 assert(i != -1); |
346 this.selectCard(i, opt_animate); | 385 this.selectCard(i, opt_animate); |
347 }, | 386 }, |
348 | 387 |
349 /** | 388 /** |
350 * Centers the view on the card denoted by this.currentCard. Can either | 389 * Centers the view on the card denoted by this.currentCard. Can either |
351 * animate to that card or snap to it. | 390 * animate to that card or snap to it. |
352 * @param {boolean=} opt_animate If true will animate transition from | 391 * @param {boolean=} opt_animate If true will animate transition from |
353 * current position to new position. | 392 * current position to new position. |
| 393 * @return {boolean} Whether or not a transformation was necessary. |
354 * @private | 394 * @private |
355 */ | 395 */ |
356 transformToCurrentCard_: function(opt_animate) { | 396 transformToCurrentCard_: function(opt_animate) { |
| 397 var prevLeft = this.currentLeft_; |
357 this.currentLeft_ = -this.cardWidth_ * | 398 this.currentLeft_ = -this.cardWidth_ * |
358 (isRTL() ? this.cards_.length - this.currentCard - 1 : | 399 (isRTL() ? this.cards_.length - this.currentCard - 1 : |
359 this.currentCard); | 400 this.currentCard); |
360 | 401 |
| 402 // If there's no change, return something to let the caller no there won't |
| 403 // be a transition occuring. |
| 404 if (prevLeft == this.currentLeft_) |
| 405 return false; |
| 406 |
361 // Animate to the current card, which will either transition if the | 407 // Animate to the current card, which will either transition if the |
362 // current card is new, or reset the existing card if we didn't drag | 408 // current card is new, or reset the existing card if we didn't drag |
363 // enough to change cards. | 409 // enough to change cards. |
364 var transition = ''; | 410 var transition = ''; |
365 if (opt_animate) { | 411 if (opt_animate) { |
366 transition = '-webkit-transform ' + CardSlider.TRANSITION_TIME_ + | 412 transition = '-webkit-transform ' + CardSlider.TRANSITION_TIME_ + |
367 'ms ease-in-out'; | 413 'ms ease-in-out'; |
368 } | 414 } |
369 this.container_.style.WebkitTransition = transition; | 415 this.container_.style.WebkitTransition = transition; |
370 this.translateTo_(this.currentLeft_); | 416 this.translateTo_(this.currentLeft_); |
| 417 |
| 418 return true; |
371 }, | 419 }, |
372 | 420 |
373 /** | 421 /** |
374 * Moves the view to the specified position. | 422 * Moves the view to the specified position. |
375 * @param {number} x Horizontal position to move to. | 423 * @param {number} x Horizontal position to move to. |
376 * @private | 424 * @private |
377 */ | 425 */ |
378 translateTo_: function(x) { | 426 translateTo_: function(x) { |
379 // We use a webkitTransform to slide because this is GPU accelerated on | 427 // We use a webkitTransform to slide because this is GPU accelerated on |
380 // Chrome and iOS. Once Chrome does GPU acceleration on the position | 428 // Chrome and iOS. Once Chrome does GPU acceleration on the position |
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
455 | 503 |
456 // Ensure we're at a card bounary | 504 // Ensure we're at a card bounary |
457 this.transformToCurrentCard_(true); | 505 this.transformToCurrentCard_(true); |
458 }, | 506 }, |
459 }; | 507 }; |
460 | 508 |
461 return { | 509 return { |
462 CardSlider: CardSlider | 510 CardSlider: CardSlider |
463 }; | 511 }; |
464 }); | 512 }); |
OLD | NEW |