Chromium Code Reviews| 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 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 71 * @private | 71 * @private |
| 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 CARD_ADDED: 'cardSlider:card_added', | |
| 82 CARD_REMOVED: 'cardSlider:card_removed', | |
| 81 // Fired when the user slides to another card. | 83 // Fired when the user slides to another card. |
| 82 CARD_CHANGED: 'cardSlider:card_changed' | 84 CARD_CHANGED: 'cardSlider:card_changed', |
| 85 CARD_CHANGE_ENDED: 'cardSlider:card_change_ended', | |
|
Evan Stade
2012/01/17 19:45:29
as long as you're changing this, I'm unsure of its
Dan Beam
2012/01/17 21:33:09
Nope.
Evan Stade
2012/01/17 22:33:05
I believe we've had this conversation before, and
Dan Beam
2012/01/17 23:33:17
Done. (I know you'd rather change the name, *howev
| |
| 83 }; | 86 }; |
| 84 | 87 |
| 85 | 88 |
| 86 /** | 89 /** |
| 87 * The time to transition between cards when animating. Measured in ms. | 90 * The time to transition between cards when animating. Measured in ms. |
| 88 * @type {number} | 91 * @type {number} |
| 89 * @private | 92 * @private |
| 90 * @const | 93 * @const |
| 91 */ | 94 */ |
| 92 CardSlider.TRANSITION_TIME_ = 200; | 95 CardSlider.TRANSITION_TIME_ = 200; |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 125 | 128 |
| 126 this.updateCardWidths_(); | 129 this.updateCardWidths_(); |
| 127 | 130 |
| 128 this.mouseWheelScrollAmount_ = 0; | 131 this.mouseWheelScrollAmount_ = 0; |
| 129 this.mouseWheelCardSelected_ = false; | 132 this.mouseWheelCardSelected_ = false; |
| 130 this.mouseWheelIsContinuous_ = false; | 133 this.mouseWheelIsContinuous_ = false; |
| 131 this.scrollClearTimeout_ = null; | 134 this.scrollClearTimeout_ = null; |
| 132 this.frame_.addEventListener('mousewheel', | 135 this.frame_.addEventListener('mousewheel', |
| 133 this.onMouseWheel_.bind(this)); | 136 this.onMouseWheel_.bind(this)); |
| 134 this.container_.addEventListener( | 137 this.container_.addEventListener( |
| 135 'webkitTransitionEnd', this.onAnimationTransitioned_.bind(this)); | 138 'webkitTransitionEnd', this.onWebkitTransitionEnd_.bind(this)); |
| 136 | 139 |
| 137 // Also support touch events in case a touch screen happens to be | 140 // Also support touch events in case a touch screen happens to be |
| 138 // available. Ideally we would support touch events whenever they | 141 // available. Ideally we would support touch events whenever they |
| 139 // are fired, but for now restrict this extra code to when we know | 142 // are fired, but for now restrict this extra code to when we know |
| 140 // we want to support touch input. | 143 // we want to support touch input. |
| 141 if (cr.isTouchOptimized) { | 144 if (cr.isTouchOptimized) { |
| 142 var TouchHandler = cr.ui.TouchHandler; | 145 var TouchHandler = cr.ui.TouchHandler; |
| 143 this.container_.addEventListener(TouchHandler.EventType.TOUCH_START, | 146 this.container_.addEventListener(TouchHandler.EventType.TOUCH_START, |
| 144 this.onTouchStart_.bind(this)); | 147 this.onTouchStart_.bind(this)); |
| 145 this.container_.addEventListener(TouchHandler.EventType.DRAG_START, | 148 this.container_.addEventListener(TouchHandler.EventType.DRAG_START, |
| (...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 292 * Resets the amount of horizontal scroll we've seen to 0. See | 295 * Resets the amount of horizontal scroll we've seen to 0. See |
| 293 * onMouseWheel_. | 296 * onMouseWheel_. |
| 294 * @private | 297 * @private |
| 295 */ | 298 */ |
| 296 clearMouseWheelScroll_: function() { | 299 clearMouseWheelScroll_: function() { |
| 297 this.mouseWheelScrollAmount_ = 0; | 300 this.mouseWheelScrollAmount_ = 0; |
| 298 this.mouseWheelCardSelected_ = false; | 301 this.mouseWheelCardSelected_ = false; |
| 299 }, | 302 }, |
| 300 | 303 |
| 301 /** | 304 /** |
| 302 * A handler for the animations ending their transition. | 305 * Handles the ends of -webkit-transitions on -webkit-transform (animated |
| 306 * card switches). | |
| 307 * @param {Event} e The webkitTransitionEnd event. | |
| 303 * @private | 308 * @private |
| 304 */ | 309 */ |
| 305 onAnimationTransitioned_: function(event) { | 310 onWebkitTransitionEnd_: function(e) { |
| 306 if (event.target.id == 'page-list') { | 311 // Ignore irrelevant transitions that might bubble up. |
| 307 cr.dispatchSimpleEvent(this.currentCardValue, 'cardSelectionCompleted', | 312 if (e.target !== this.container_ || |
| 308 true, true); | 313 e.propertyName != '-webkit-transform') { |
|
Evan Stade
2012/01/17 19:45:29
why !== then !=
Dan Beam
2012/01/17 21:33:09
I tend to use strict equal/does-not-equal operator
| |
| 314 return; | |
| 309 } | 315 } |
| 316 this.fireChangeEndedEvent_(true); | |
| 310 }, | 317 }, |
| 311 | 318 |
| 312 /** | 319 /** |
| 320 * Dispatches a simple event to tell subscribers we're done moving to the | |
| 321 * newly selected card. | |
| 322 * @param {boolean} wasAnimated whether or not the change was animated. | |
| 323 * @private | |
| 324 */ | |
| 325 fireChangeEndedEvent_: function(wasAnimated) { | |
| 326 var e = document.createEvent('Event'); | |
| 327 e.initEvent(CardSlider.EventType.CARD_CHANGE_ENDED, true, true); | |
| 328 e.cardSlider = this; | |
| 329 e.changedTo = this.currentCard_; | |
| 330 e.wasAnimated = wasAnimated; | |
| 331 this.container_.dispatchEvent(e); | |
| 332 }, | |
| 333 | |
| 334 /** | |
| 335 * Add a card to the card slider at a particular index. If the card being | |
| 336 * added is inserted in front of the current card, cardSlider.currentCard | |
| 337 * will be adjusted accordingly (to current card + 1). | |
| 338 * @param {!Node} card A card that will be added to the card slider. | |
| 339 * @param {number} index An index at which the given |card| should be | |
| 340 * inserted. Must be positive and less than the number of cards. | |
| 341 */ | |
| 342 addCardAtIndex: function(card, index) { | |
| 343 assert(card instanceof Node, '|card| isn\'t a Node'); | |
| 344 this.assertValidIndex_(index); | |
| 345 this.cards_ = Array.prototype.concat.call( | |
| 346 this.cards_.slice(0, index), card, this.cards_.slice(index)); | |
| 347 if (index <= this.currentCard_) | |
| 348 this.selectCard(this.currentCard_ + 1, false, true); | |
| 349 this.fireAddedEvent_(card, index); | |
| 350 }, | |
| 351 | |
| 352 /** | |
| 353 * Append a card to the end of the list. | |
| 354 * @param {!Node} card A card to add at the end of the card slider. | |
| 355 */ | |
| 356 appendCard: function(card) { | |
| 357 assert(card instanceof Node, '|card| isn\'t a Node'); | |
| 358 this.cards_.push(card); | |
| 359 this.fireAddedEvent_(card, this.cards_.length - 1); | |
| 360 }, | |
| 361 | |
| 362 /** | |
| 363 * Dispatches a simple event to tell interested subscribers that a card was | |
| 364 * added to this card slider. | |
| 365 * @param {Node} card The recently added card. | |
| 366 * @param {number} index The position of the newly added card. | |
| 367 * @private | |
| 368 */ | |
| 369 fireAddedEvent_: function(card, index) { | |
| 370 this.assertValidIndex_(index); | |
| 371 var e = document.createEvent('Event'); | |
| 372 e.initEvent(CardSlider.EventType.CARD_ADDED, true, true); | |
| 373 e.addedIndex = index; | |
| 374 e.addedCard = card; | |
| 375 this.container_.dispatchEvent(e); | |
| 376 }, | |
| 377 | |
| 378 /** | |
| 379 * Removes a card by index from the card slider. If the card to be removed | |
| 380 * is the current card or in front of the current card, the current card | |
| 381 * will be updated (to current card - 1). | |
| 382 * @param {!Node} card A card to be removed. | |
| 383 */ | |
| 384 removeCard: function(card) { | |
| 385 assert(card instanceof Node, '|card| isn\'t a Node'); | |
| 386 this.removeCardAtIndex(this.cards_.indexOf(card)); | |
| 387 }, | |
| 388 | |
| 389 /** | |
| 390 * Removes a card by index from the card slider. If the card to be removed | |
| 391 * is the current card or in front of the current card, the current card | |
| 392 * will be updated (to current card - 1). | |
| 393 * @param {number} index The index of the tile that should be removed. | |
| 394 */ | |
| 395 removeCardAtIndex: function(index) { | |
| 396 this.assertValidIndex_(index); | |
| 397 var removed = this.cards_.splice(index, 1).pop(); | |
| 398 if (index < this.currentCard_) | |
| 399 this.selectCard(this.currentCard_ - 1, false, true); | |
| 400 this.fireRemovedEvent_(removed, index); | |
| 401 }, | |
| 402 | |
| 403 /** | |
| 404 * Dispatches a CARD_REMOVED event so interested subscribers know when a | |
| 405 * card was removed from this card slider. | |
| 406 * @param {Node} card The recently removed card. | |
| 407 * @param {number} index The index of the card before it was removed. | |
| 408 * @private | |
| 409 */ | |
| 410 fireRemovedEvent_: function(card, index) { | |
| 411 var e = document.createEvent('Event'); | |
| 412 e.initEvent(CardSlider.EventType.CARD_REMOVED, true, true); | |
| 413 e.removedCard = card; | |
| 414 e.removedIndex = index; | |
| 415 this.container_.dispatchEvent(e); | |
| 416 }, | |
| 417 | |
| 418 /** | |
| 419 * Checks the the given |index| exists in this.cards_. | |
| 420 * @param {number} index An index to check. | |
| 421 * @private | |
| 422 */ | |
| 423 assertValidIndex_: function(index) { | |
| 424 assert(index >= 0 && index < this.cards_.length); | |
| 425 }, | |
| 426 | |
| 427 /** | |
| 313 * Selects a new card, ensuring that it is a valid index, transforming the | 428 * Selects a new card, ensuring that it is a valid index, transforming the |
| 314 * view and possibly calling the change card callback. | 429 * view and possibly calling the change card callback. |
| 315 * @param {number} newCardIndex Index of card to show. | 430 * @param {number} newCardIndex Index of card to show. |
| 316 * @param {boolean=} opt_animate If true will animate transition from | 431 * @param {boolean=} opt_animate If true will animate transition from |
| 317 * current position to new position. | 432 * current position to new position. |
| 433 * @param {boolean=} opt_dontNotify If true, don't tell subscribers that | |
| 434 * we've changed cards. | |
| 318 */ | 435 */ |
| 319 selectCard: function(newCardIndex, opt_animate) { | 436 selectCard: function(newCardIndex, opt_animate, opt_dontNotify) { |
| 437 this.assertValidIndex_(newCardIndex); | |
| 438 | |
| 320 var previousCard = this.currentCardValue; | 439 var previousCard = this.currentCardValue; |
| 321 | |
| 322 var isChangingCard = | 440 var isChangingCard = |
| 323 !this.cards_[newCardIndex].classList.contains('selected-card'); | 441 !this.cards_[newCardIndex].classList.contains('selected-card'); |
| 324 | 442 |
| 325 if (isChangingCard) { | 443 if (isChangingCard) { |
| 326 previousCard.classList.remove('selected-card'); | 444 if (previousCard) |
| 327 // If we have a new card index and it is valid then update the left | 445 previousCard.classList.remove('selected-card'); |
| 328 // position and current card index. | |
| 329 this.currentCard_ = newCardIndex; | 446 this.currentCard_ = newCardIndex; |
| 330 this.currentCardValue.classList.add('selected-card'); | 447 this.currentCardValue.classList.add('selected-card'); |
| 331 } | 448 } |
| 332 | 449 |
| 333 this.transformToCurrentCard_(opt_animate); | 450 var willTransitionHappen = this.transformToCurrentCard_(opt_animate); |
| 334 | 451 |
| 335 if (isChangingCard) { | 452 if (isChangingCard && !opt_dontNotify) { |
| 336 var event = document.createEvent('Event'); | 453 var event = document.createEvent('Event'); |
| 337 event.initEvent(CardSlider.EventType.CARD_CHANGED, true, true); | 454 event.initEvent(CardSlider.EventType.CARD_CHANGED, true, true); |
| 338 event.cardSlider = this; | 455 event.cardSlider = this; |
| 456 event.wasAnimated = !!opt_animate; | |
| 339 this.container_.dispatchEvent(event); | 457 this.container_.dispatchEvent(event); |
| 340 | 458 |
| 341 // We also dispatch an event on the cards themselves. | 459 // We also dispatch an event on the cards themselves. |
| 342 if (previousCard) { | 460 if (previousCard) { |
| 343 cr.dispatchSimpleEvent(previousCard, 'carddeselected', | 461 cr.dispatchSimpleEvent(previousCard, 'carddeselected', |
| 344 true, true); | 462 true, true); |
| 345 } | 463 } |
| 346 cr.dispatchSimpleEvent(this.currentCardValue, 'cardselected', | 464 cr.dispatchSimpleEvent(this.currentCardValue, 'cardselected', |
| 347 true, true); | 465 true, true); |
| 348 } | 466 } |
| 467 | |
| 468 // If we're not changing, animated, or transitioning, fire a | |
| 469 // CARD_CHANGE_ENDED event right away. | |
| 470 if ((!isChangingCard || !opt_animate || !willTransitionHappen) && | |
| 471 !opt_dontNotify) { | |
| 472 this.fireChangeEndedEvent_(false); | |
| 473 } | |
| 349 }, | 474 }, |
| 350 | 475 |
| 351 /** | 476 /** |
| 352 * Selects a card from the stack. Passes through to selectCard. | 477 * Selects a card from the stack. Passes through to selectCard. |
| 353 * @param {Node} newCard The card that should be selected. | 478 * @param {Node} newCard The card that should be selected. |
| 354 * @param {boolean=} opt_animate Whether to animate. | 479 * @param {boolean=} opt_animate Whether to animate. |
| 355 */ | 480 */ |
| 356 selectCardByValue: function(newCard, opt_animate) { | 481 selectCardByValue: function(newCard, opt_animate) { |
| 357 var i = this.cards_.indexOf(newCard); | 482 var i = this.cards_.indexOf(newCard); |
| 358 assert(i != -1); | 483 assert(i != -1); |
| 359 this.selectCard(i, opt_animate); | 484 this.selectCard(i, opt_animate); |
| 360 }, | 485 }, |
| 361 | 486 |
| 362 /** | 487 /** |
| 363 * Centers the view on the card denoted by this.currentCard. Can either | 488 * Centers the view on the card denoted by this.currentCard. Can either |
| 364 * animate to that card or snap to it. | 489 * animate to that card or snap to it. |
| 365 * @param {boolean=} opt_animate If true will animate transition from | 490 * @param {boolean=} opt_animate If true will animate transition from |
| 366 * current position to new position. | 491 * current position to new position. |
| 492 * @return {boolean} Whether or not a transformation was necessary. | |
| 367 * @private | 493 * @private |
| 368 */ | 494 */ |
| 369 transformToCurrentCard_: function(opt_animate) { | 495 transformToCurrentCard_: function(opt_animate) { |
| 496 var prevLeft = this.currentLeft_; | |
| 370 this.currentLeft_ = -this.cardWidth_ * | 497 this.currentLeft_ = -this.cardWidth_ * |
| 371 (isRTL() ? this.cards_.length - this.currentCard - 1 : | 498 (isRTL() ? this.cards_.length - this.currentCard - 1 : |
| 372 this.currentCard); | 499 this.currentCard); |
| 373 | 500 |
| 501 // If there's no change, return something to let the caller know there | |
| 502 // won't be a transition occuring. | |
| 503 if (prevLeft == this.currentLeft_) | |
| 504 return false; | |
| 505 | |
| 374 // Animate to the current card, which will either transition if the | 506 // Animate to the current card, which will either transition if the |
| 375 // current card is new, or reset the existing card if we didn't drag | 507 // current card is new, or reset the existing card if we didn't drag |
| 376 // enough to change cards. | 508 // enough to change cards. |
| 377 var transition = ''; | 509 var transition = ''; |
| 378 if (opt_animate) { | 510 if (opt_animate) { |
| 379 transition = '-webkit-transform ' + CardSlider.TRANSITION_TIME_ + | 511 transition = '-webkit-transform ' + CardSlider.TRANSITION_TIME_ + |
| 380 'ms ease-in-out'; | 512 'ms ease-in-out'; |
| 381 } | 513 } |
| 382 this.container_.style.WebkitTransition = transition; | 514 this.container_.style.WebkitTransition = transition; |
| 383 this.translateTo_(this.currentLeft_); | 515 this.translateTo_(this.currentLeft_); |
| 516 | |
| 517 return true; | |
| 384 }, | 518 }, |
| 385 | 519 |
| 386 /** | 520 /** |
| 387 * Moves the view to the specified position. | 521 * Moves the view to the specified position. |
| 388 * @param {number} x Horizontal position to move to. | 522 * @param {number} x Horizontal position to move to. |
| 389 * @private | 523 * @private |
| 390 */ | 524 */ |
| 391 translateTo_: function(x) { | 525 translateTo_: function(x) { |
| 392 // We use a webkitTransform to slide because this is GPU accelerated on | 526 // We use a webkitTransform to slide because this is GPU accelerated on |
| 393 // Chrome and iOS. Once Chrome does GPU acceleration on the position | 527 // Chrome and iOS. Once Chrome does GPU acceleration on the position |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 468 | 602 |
| 469 // Ensure we're at a card bounary | 603 // Ensure we're at a card bounary |
| 470 this.transformToCurrentCard_(true); | 604 this.transformToCurrentCard_(true); |
| 471 }, | 605 }, |
| 472 }; | 606 }; |
| 473 | 607 |
| 474 return { | 608 return { |
| 475 CardSlider: CardSlider | 609 CardSlider: CardSlider |
| 476 }; | 610 }; |
| 477 }); | 611 }); |
| OLD | NEW |