| 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 |
| 11 * fill the width of the frame. This element is also overflow hidden so that | 11 * fill the width of the frame. This element is also overflow hidden so that |
| 12 * the additional items left / right do not trigger horizontal scrolling. | 12 * the additional items left / right do not trigger horizontal scrolling. |
| 13 * - The container is what all the touch events are attached to. This element | 13 * - The container is what all the touch events are attached to. This element |
| 14 * will be expanded to be the width of all cards. | 14 * will be expanded to be the width of all cards. |
| 15 * - The cards are the individual viewable items. There should be one card for | 15 * - The cards are the individual viewable items. There should be one card for |
| 16 * each item in the list. Only one card will be visible at a time. Two cards | 16 * each item in the list. Only one card will be visible at a time. Two cards |
| 17 * will be visible while you are transitioning between cards. | 17 * will be visible while you are transitioning between cards. |
| 18 * | 18 * |
| 19 * This class is designed to work well on any hardware-accelerated touch device. | 19 * This class is designed to work well on any hardware-accelerated touch device. |
| 20 * It should still work on pre-hardware accelerated devices it just won't feel | 20 * It should still work on pre-hardware accelerated devices it just won't feel |
| 21 * very good. It should also work well with a mouse. | 21 * very good. It should also work well with a mouse. |
| 22 */ | 22 */ |
| 23 | 23 |
| 24 | |
| 25 // Use an anonymous function to enable strict mode just for this file (which | 24 // Use an anonymous function to enable strict mode just for this file (which |
| 26 // will be concatenated with other files when embedded in Chrome | 25 // will be concatenated with other files when embedded in Chrome |
| 27 var CardSlider = (function() { | 26 cr.define('cr.ui', function() { |
| 28 'use strict'; | 27 'use strict'; |
| 29 | 28 |
| 30 /** | 29 /** |
| 31 * @constructor | 30 * @constructor |
| 32 * @param {!Element} frame The bounding rectangle that cards are visible in. | 31 * @param {!Element} frame The bounding rectangle that cards are visible in. |
| 33 * @param {!Element} container The surrounding element that will have event | 32 * @param {!Element} container The surrounding element that will have event |
| 34 * listeners attached to it. | 33 * listeners attached to it. |
| 35 * @param {number} cardWidth The width of each card should have. | 34 * @param {number} cardWidth The width of each card should have. |
| 36 */ | 35 */ |
| 37 function CardSlider(frame, container, cardWidth) { | 36 function CardSlider(frame, container, cardWidth) { |
| (...skipping 23 matching lines...) Expand all Loading... |
| 61 */ | 60 */ |
| 62 this.currentCard_ = 0; | 61 this.currentCard_ = 0; |
| 63 | 62 |
| 64 /** | 63 /** |
| 65 * @type {number} | 64 * @type {number} |
| 66 * @private | 65 * @private |
| 67 */ | 66 */ |
| 68 this.cardWidth_ = cardWidth; | 67 this.cardWidth_ = cardWidth; |
| 69 | 68 |
| 70 /** | 69 /** |
| 71 * @type {!TouchHandler} | 70 * @type {!cr.ui.TouchHandler} |
| 72 * @private | 71 * @private |
| 73 */ | 72 */ |
| 74 this.touchHandler_ = new TouchHandler(this.container_); | 73 this.touchHandler_ = new cr.ui.TouchHandler(this.container_); |
| 75 | |
| 76 } | 74 } |
| 77 | 75 |
| 78 /** | 76 /** |
| 79 * Events fired by the slider. | 77 * Events fired by the slider. |
| 80 * Events are fired at the container. | 78 * Events are fired at the container. |
| 81 */ | 79 */ |
| 82 CardSlider.EventType = { | 80 CardSlider.EventType = { |
| 83 // Fired when the user slides to another card. | 81 // Fired when the user slides to another card. |
| 84 CARD_CHANGED: 'cardSlider:card_changed' | 82 CARD_CHANGED: 'cardSlider:card_changed' |
| 85 }; | 83 }; |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 128 this.updateCardWidths_(); | 126 this.updateCardWidths_(); |
| 129 | 127 |
| 130 this.mouseWheelScrollAmount_ = 0; | 128 this.mouseWheelScrollAmount_ = 0; |
| 131 this.mouseWheelCardSelected_ = false; | 129 this.mouseWheelCardSelected_ = false; |
| 132 this.mouseWheelIsContinuous_ = false; | 130 this.mouseWheelIsContinuous_ = false; |
| 133 this.scrollClearTimeout_ = null; | 131 this.scrollClearTimeout_ = null; |
| 134 this.frame_.addEventListener('mousewheel', | 132 this.frame_.addEventListener('mousewheel', |
| 135 this.onMouseWheel_.bind(this)); | 133 this.onMouseWheel_.bind(this)); |
| 136 | 134 |
| 137 if (document.documentElement.getAttribute('touchui')) { | 135 if (document.documentElement.getAttribute('touchui')) { |
| 136 var TouchHandler = cr.ui.TouchHandler; |
| 138 this.container_.addEventListener(TouchHandler.EventType.TOUCH_START, | 137 this.container_.addEventListener(TouchHandler.EventType.TOUCH_START, |
| 139 this.onTouchStart_.bind(this)); | 138 this.onTouchStart_.bind(this)); |
| 140 this.container_.addEventListener(TouchHandler.EventType.DRAG_START, | 139 this.container_.addEventListener(TouchHandler.EventType.DRAG_START, |
| 141 this.onDragStart_.bind(this)); | 140 this.onDragStart_.bind(this)); |
| 142 this.container_.addEventListener(TouchHandler.EventType.DRAG_MOVE, | 141 this.container_.addEventListener(TouchHandler.EventType.DRAG_MOVE, |
| 143 this.onDragMove_.bind(this)); | 142 this.onDragMove_.bind(this)); |
| 144 this.container_.addEventListener(TouchHandler.EventType.DRAG_END, | 143 this.container_.addEventListener(TouchHandler.EventType.DRAG_END, |
| 145 this.onDragEnd_.bind(this)); | 144 this.onDragEnd_.bind(this)); |
| 146 | 145 |
| 147 this.touchHandler_.enable(/* opt_capture */ false); | 146 this.touchHandler_.enable(/* opt_capture */ false); |
| (...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 246 this.mouseWheelIsContinuous_ = true; | 245 this.mouseWheelIsContinuous_ = true; |
| 247 | 246 |
| 248 if (this.mouseWheelIsContinuous_) { | 247 if (this.mouseWheelIsContinuous_) { |
| 249 // For continuous devices, detect a page swipe when the accumulated | 248 // For continuous devices, detect a page swipe when the accumulated |
| 250 // delta matches a pre-defined threshhold. After changing the page, | 249 // delta matches a pre-defined threshhold. After changing the page, |
| 251 // ignore wheel events for a short time before repeating this process. | 250 // ignore wheel events for a short time before repeating this process. |
| 252 if (this.mouseWheelCardSelected_) return; | 251 if (this.mouseWheelCardSelected_) return; |
| 253 this.mouseWheelScrollAmount_ += e.wheelDeltaX; | 252 this.mouseWheelScrollAmount_ += e.wheelDeltaX; |
| 254 if (Math.abs(this.mouseWheelScrollAmount_) >= 600) { | 253 if (Math.abs(this.mouseWheelScrollAmount_) >= 600) { |
| 255 var pagesToScroll = this.mouseWheelScrollAmount_ > 0 ? 1 : -1; | 254 var pagesToScroll = this.mouseWheelScrollAmount_ > 0 ? 1 : -1; |
| 256 if (!ntp4.isRTL()) | 255 if (!isRTL()) |
| 257 pagesToScroll *= -1; | 256 pagesToScroll *= -1; |
| 258 var newCardIndex = this.currentCard + pagesToScroll; | 257 var newCardIndex = this.currentCard + pagesToScroll; |
| 259 newCardIndex = Math.min(this.cards_.length - 1, | 258 newCardIndex = Math.min(this.cards_.length - 1, |
| 260 Math.max(0, newCardIndex)); | 259 Math.max(0, newCardIndex)); |
| 261 this.selectCard(newCardIndex, true); | 260 this.selectCard(newCardIndex, true); |
| 262 this.mouseWheelCardSelected_ = true; | 261 this.mouseWheelCardSelected_ = true; |
| 263 } | 262 } |
| 264 } else { | 263 } else { |
| 265 // For discrete devices, consider each wheel tick a page change. | 264 // For discrete devices, consider each wheel tick a page change. |
| 266 var pagesToScroll = e.wheelDeltaX / DISCRETE_DELTA; | 265 var pagesToScroll = e.wheelDeltaX / DISCRETE_DELTA; |
| 267 if (!ntp4.isRTL()) | 266 if (!isRTL()) |
| 268 pagesToScroll *= -1; | 267 pagesToScroll *= -1; |
| 269 var newCardIndex = this.currentCard + pagesToScroll; | 268 var newCardIndex = this.currentCard + pagesToScroll; |
| 270 newCardIndex = Math.min(this.cards_.length - 1, | 269 newCardIndex = Math.min(this.cards_.length - 1, |
| 271 Math.max(0, newCardIndex)); | 270 Math.max(0, newCardIndex)); |
| 272 this.selectCard(newCardIndex, true); | 271 this.selectCard(newCardIndex, true); |
| 273 } | 272 } |
| 274 | 273 |
| 275 // We got a mouse wheel event, so cancel any pending scroll wheel timeout. | 274 // We got a mouse wheel event, so cancel any pending scroll wheel timeout. |
| 276 if (this.scrollClearTimeout_ != null) | 275 if (this.scrollClearTimeout_ != null) |
| 277 clearTimeout(this.scrollClearTimeout_); | 276 clearTimeout(this.scrollClearTimeout_); |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 345 | 344 |
| 346 /** | 345 /** |
| 347 * Centers the view on the card denoted by this.currentCard. Can either | 346 * Centers the view on the card denoted by this.currentCard. Can either |
| 348 * animate to that card or snap to it. | 347 * animate to that card or snap to it. |
| 349 * @param {boolean=} opt_animate If true will animate transition from | 348 * @param {boolean=} opt_animate If true will animate transition from |
| 350 * current position to new position. | 349 * current position to new position. |
| 351 * @private | 350 * @private |
| 352 */ | 351 */ |
| 353 transformToCurrentCard_: function(opt_animate) { | 352 transformToCurrentCard_: function(opt_animate) { |
| 354 this.currentLeft_ = -this.cardWidth_ * | 353 this.currentLeft_ = -this.cardWidth_ * |
| 355 (ntp4.isRTL() ? this.cards_.length - this.currentCard - 1 : | 354 (isRTL() ? this.cards_.length - this.currentCard - 1 : |
| 356 this.currentCard); | 355 this.currentCard); |
| 357 | 356 |
| 358 // Animate to the current card, which will either transition if the | 357 // Animate to the current card, which will either transition if the |
| 359 // current card is new, or reset the existing card if we didn't drag | 358 // current card is new, or reset the existing card if we didn't drag |
| 360 // enough to change cards. | 359 // enough to change cards. |
| 361 var transition = ''; | 360 var transition = ''; |
| 362 if (opt_animate) { | 361 if (opt_animate) { |
| 363 transition = '-webkit-transform ' + CardSlider.TRANSITION_TIME_ + | 362 transition = '-webkit-transform ' + CardSlider.TRANSITION_TIME_ + |
| 364 'ms ease-in-out'; | 363 'ms ease-in-out'; |
| 365 } | 364 } |
| 366 this.container_.style.WebkitTransition = transition; | 365 this.container_.style.WebkitTransition = transition; |
| (...skipping 11 matching lines...) Expand all Loading... |
| 378 // fixed-layout elements we could simply set the element's position to | 377 // fixed-layout elements we could simply set the element's position to |
| 379 // fixed and modify 'left' instead. | 378 // fixed and modify 'left' instead. |
| 380 this.container_.style.WebkitTransform = 'translate3d(' + x + 'px, 0, 0)'; | 379 this.container_.style.WebkitTransform = 'translate3d(' + x + 'px, 0, 0)'; |
| 381 }, | 380 }, |
| 382 | 381 |
| 383 /* Touch ******************************************************************/ | 382 /* Touch ******************************************************************/ |
| 384 | 383 |
| 385 /** | 384 /** |
| 386 * Clear any transition that is in progress and enable dragging for the | 385 * Clear any transition that is in progress and enable dragging for the |
| 387 * touch. | 386 * touch. |
| 388 * @param {!TouchHandler.Event} e The TouchHandler event. | 387 * @param {!cr.ui.TouchHandler.Event} e The TouchHandler event. |
| 389 * @private | 388 * @private |
| 390 */ | 389 */ |
| 391 onTouchStart_: function(e) { | 390 onTouchStart_: function(e) { |
| 392 this.container_.style.WebkitTransition = ''; | 391 this.container_.style.WebkitTransition = ''; |
| 393 e.enableDrag = true; | 392 e.enableDrag = true; |
| 394 }, | 393 }, |
| 395 | 394 |
| 396 /** | 395 /** |
| 397 * Tell the TouchHandler that dragging is acceptable when the user begins by | 396 * Tell the TouchHandler that dragging is acceptable when the user begins by |
| 398 * scrolling horizontally. | 397 * scrolling horizontally. |
| 399 * @param {!TouchHandler.Event} e The TouchHandler event. | 398 * @param {!cr.ui.TouchHandler.Event} e The TouchHandler event. |
| 400 * @private | 399 * @private |
| 401 */ | 400 */ |
| 402 onDragStart_: function(e) { | 401 onDragStart_: function(e) { |
| 403 e.enableDrag = Math.abs(e.dragDeltaX) > Math.abs(e.dragDeltaY); | 402 e.enableDrag = Math.abs(e.dragDeltaX) > Math.abs(e.dragDeltaY); |
| 404 }, | 403 }, |
| 405 | 404 |
| 406 /** | 405 /** |
| 407 * On each drag move event reposition the container appropriately so the | 406 * On each drag move event reposition the container appropriately so the |
| 408 * cards look like they are sliding. | 407 * cards look like they are sliding. |
| 409 * @param {!TouchHandler.Event} e The TouchHandler event. | 408 * @param {!cr.ui.TouchHandler.Event} e The TouchHandler event. |
| 410 * @private | 409 * @private |
| 411 */ | 410 */ |
| 412 onDragMove_: function(e) { | 411 onDragMove_: function(e) { |
| 413 var deltaX = e.dragDeltaX; | 412 var deltaX = e.dragDeltaX; |
| 414 // If dragging beyond the first or last card then apply a backoff so the | 413 // If dragging beyond the first or last card then apply a backoff so the |
| 415 // dragging feels stickier than usual. | 414 // dragging feels stickier than usual. |
| 416 if (!this.currentCard && deltaX > 0 || | 415 if (!this.currentCard && deltaX > 0 || |
| 417 this.currentCard == (this.cards_.length - 1) && deltaX < 0) { | 416 this.currentCard == (this.cards_.length - 1) && deltaX < 0) { |
| 418 deltaX /= 2; | 417 deltaX /= 2; |
| 419 } | 418 } |
| 420 this.translateTo_(this.currentLeft_ + deltaX); | 419 this.translateTo_(this.currentLeft_ + deltaX); |
| 421 }, | 420 }, |
| 422 | 421 |
| 423 /** | 422 /** |
| 424 * On drag end events we may want to transition to another card, depending | 423 * On drag end events we may want to transition to another card, depending |
| 425 * on the ending position of the drag and the velocity of the drag. | 424 * on the ending position of the drag and the velocity of the drag. |
| 426 * @param {!TouchHandler.Event} e The TouchHandler event. | 425 * @param {!cr.ui.TouchHandler.Event} e The TouchHandler event. |
| 427 * @private | 426 * @private |
| 428 */ | 427 */ |
| 429 onDragEnd_: function(e) { | 428 onDragEnd_: function(e) { |
| 430 var deltaX = e.dragDeltaX; | 429 var deltaX = e.dragDeltaX; |
| 431 var velocity = this.touchHandler_.getEndVelocity().x; | 430 var velocity = this.touchHandler_.getEndVelocity().x; |
| 432 var newX = this.currentLeft_ + deltaX; | 431 var newX = this.currentLeft_ + deltaX; |
| 433 var newCardIndex = Math.round(-newX / this.cardWidth_); | 432 var newCardIndex = Math.round(-newX / this.cardWidth_); |
| 434 | 433 |
| 435 if (newCardIndex == this.currentCard && Math.abs(velocity) > | 434 if (newCardIndex == this.currentCard && Math.abs(velocity) > |
| 436 CardSlider.TRANSITION_VELOCITY_THRESHOLD_) { | 435 CardSlider.TRANSITION_VELOCITY_THRESHOLD_) { |
| (...skipping 11 matching lines...) Expand all Loading... |
| 448 */ | 447 */ |
| 449 cancelTouch: function() { | 448 cancelTouch: function() { |
| 450 // Stop listening to any current touch | 449 // Stop listening to any current touch |
| 451 this.touchHandler_.cancelTouch(); | 450 this.touchHandler_.cancelTouch(); |
| 452 | 451 |
| 453 // Ensure we're at a card bounary | 452 // Ensure we're at a card bounary |
| 454 this.transformToCurrentCard_(true); | 453 this.transformToCurrentCard_(true); |
| 455 }, | 454 }, |
| 456 }; | 455 }; |
| 457 | 456 |
| 458 return CardSlider; | 457 return { |
| 459 })(); | 458 CardSlider: CardSlider |
| 459 }; |
| 460 }); |
| OLD | NEW |