1ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Use of this source code is governed by a BSD-style license that can be 3ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// found in the LICENSE file. 4ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 5ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen/** 6ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @fileoverview Card slider implementation. Allows you to create interactions 7ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * that have items that can slide left to right to reveal additional items. 8ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Works by adding the necessary event handlers to a specific DOM structure 9ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * including a frame, container and cards. 10ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * - The frame defines the boundary of one item. Each card will be expanded to 11ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * fill the width of the frame. This element is also overflow hidden so that 12ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * the additional items left / right do not trigger horizontal scrolling. 13ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * - The container is what all the touch events are attached to. This element 14ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * will be expanded to be the width of all cards. 15ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * - The cards are the individual viewable items. There should be one card for 16ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * each item in the list. Only one card will be visible at a time. Two cards 17ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * will be visible while you are transitioning between cards. 18ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * 19ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * This class is designed to work well on any hardware-accelerated touch device. 20ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * It should still work on pre-hardware accelerated devices it just won't feel 21ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * very good. It should also work well with a mouse. 22ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 23ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 24ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 25ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Use an anonymous function to enable strict mode just for this file (which 26ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// will be concatenated with other files when embedded in Chrome 27ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvar Slider = (function() { 28ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 'use strict'; 29ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 30ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 31ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @constructor 32ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {!Element} frame The bounding rectangle that cards are visible in. 33ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {!Element} container The surrounding element that will have event 34ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * listeners attached to it. 35ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {!Array.<!Element>} cards The individual viewable cards. 36ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {number} currentCard The index of the card that is currently 37ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * visible. 38ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {number} cardWidth The width of each card should have. 39ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 40ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen function Slider(frame, container, cards, currentCard, cardWidth) { 41ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 42ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @type {!Element} 43ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @private 44ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 45ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.frame_ = frame; 46ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 47ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 48ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @type {!Element} 49ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @private 50ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 51ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.container_ = container; 52ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 53ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 54ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @type {!Array.<!Element>} 55ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @private 56ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 57ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.cards_ = cards; 58ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 59ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 60ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @type {number} 61ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @private 62ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 63ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.currentCard_ = currentCard; 64ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 65ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 66ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @type {number} 67ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @private 68ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 69ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.cardWidth_ = cardWidth; 70ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 71ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 72ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @type {!TouchHandler} 73ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @private 74ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 75ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.touchHandler_ = new TouchHandler(this.container_); 76ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 77ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 78ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 79ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 80ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Events fired by the slider. 81ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Events are fired at the container. 82ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 83ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen Slider.EventType = { 84ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Fired when the user slides to another card. 85ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen CARD_CHANGED: 'slider:card_changed' 86ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen }; 87ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 88ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 89ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 90ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * The time to transition between cards when animating. Measured in ms. 91ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @type {number} 92ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @private 93ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @const 94ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 95ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen Slider.TRANSITION_TIME_ = 200; 96ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 97ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 98ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 99ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * The minimum velocity required to transition cards if they did not drag past 100ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * the halfway point between cards. Measured in pixels / ms. 101ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @type {number} 102ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @private 103ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @const 104ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 105ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen Slider.TRANSITION_VELOCITY_THRESHOLD_ = 0.2; 106ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 107ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 108ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen Slider.prototype = { 109ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 110ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * The current left offset of the container relative to the frame. 111ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @type {number} 112ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @private 113ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 114ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen currentLeft_: 0, 115ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 116ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 117ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Initialize all elements and event handlers. Must call after construction 118ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * and before usage. 119ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 120ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen initialize: function() { 121ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var view = this.container_.ownerDocument.defaultView; 122ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen assert(view.getComputedStyle(this.container_).display == '-webkit-box', 123ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 'Container should be display -webkit-box.'); 124ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen assert(view.getComputedStyle(this.frame_).overflow == 'hidden', 125ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 'Frame should be overflow hidden.'); 126ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen assert(view.getComputedStyle(this.container_).position == 'static', 127ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 'Container should be position static.'); 128ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen for (var i = 0, card; card = this.cards_[i]; i++) { 129ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen assert(view.getComputedStyle(card).position == 'static', 130ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 'Cards should be position static.'); 131ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 132ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 133ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.updateCardWidths_(); 134ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.transformToCurrentCard_(); 135ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 136ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.container_.addEventListener(TouchHandler.EventType.TOUCH_START, 137ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.onTouchStart_.bind(this)); 138ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.container_.addEventListener(TouchHandler.EventType.DRAG_START, 139ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.onDragStart_.bind(this)); 140ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.container_.addEventListener(TouchHandler.EventType.DRAG_MOVE, 141ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.onDragMove_.bind(this)); 142ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.container_.addEventListener(TouchHandler.EventType.DRAG_END, 143ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.onDragEnd_.bind(this)); 144ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 145ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.touchHandler_.enable(/* opt_capture */ false); 146ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen }, 147ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 148ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 149ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Use in cases where the width of the frame has changed in order to update 150ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * the width of cards. For example should be used when orientation changes 151ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * in full width sliders. 152ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {number} newCardWidth Width all cards should have, in pixels. 153ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 154ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen resize: function(newCardWidth) { 155ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (newCardWidth != this.cardWidth_) { 156ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.cardWidth_ = newCardWidth; 157ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 158ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.updateCardWidths_(); 159ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 160ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Must upate the transform on the container to show the correct card. 161ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.transformToCurrentCard_(); 162ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 163ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen }, 164ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 165ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 166ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Sets the cards used. Can be called more than once to switch card sets. 167ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {!Array.<!Element>} cards The individual viewable cards. 168ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {number} index Index of the card to in the new set of cards to 169ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * navigate to. 170ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 171ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen setCards: function(cards, index) { 172ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen assert(index >= 0 && index < cards.length, 173ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 'Invalid index in Slider#setCards'); 174ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.cards_ = cards; 175ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 176ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.updateCardWidths_(); 177ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 178ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Jump to the given card index. 179ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.selectCard(index); 180ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen }, 181ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 182ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 183ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Updates the width of each card. 184ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @private 185ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 186ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen updateCardWidths_: function() { 187ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen for (var i = 0, card; card = this.cards_[i]; i++) 188ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen card.style.width = this.cardWidth_ + 'px'; 189ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen }, 190ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 191ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 192ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Returns the index of the current card. 193ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @return {number} index of the current card. 194ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 195ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen get currentCard() { 196ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen return this.currentCard_; 197ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen }, 198ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 199ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 200ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Clear any transition that is in progress and enable dragging for the 201ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * touch. 202ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {!TouchHandler.Event} e The TouchHandler event. 203ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @private 204ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 205ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen onTouchStart_: function(e) { 206ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.container_.style.WebkitTransition = ''; 207ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen e.enableDrag = true; 208ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen }, 209ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 210ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 211ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 212ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Tell the TouchHandler that dragging is acceptable when the user begins by 213ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * scrolling horizontally. 214ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {!TouchHandler.Event} e The TouchHandler event. 215ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @private 216ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 217ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen onDragStart_: function(e) { 218ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen e.enableDrag = Math.abs(e.dragDeltaX) > Math.abs(e.dragDeltaY); 219ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen }, 220ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 221ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 222ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * On each drag move event reposition the container appropriately so the 223ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * cards look like they are sliding. 224ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {!TouchHandler.Event} e The TouchHandler event. 225ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @private 226ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 227ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen onDragMove_: function(e) { 228ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var deltaX = e.dragDeltaX; 229ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // If dragging beyond the first or last card then apply a backoff so the 230ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // dragging feels stickier than usual. 231ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (!this.currentCard && deltaX > 0 || 232ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.currentCard == (this.cards_.length - 1) && deltaX < 0) { 233ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen deltaX /= 2; 234ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 235ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.translateTo_(this.currentLeft_ + deltaX); 236ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen }, 237ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 238ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 239ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Moves the view to the specified position. 240ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {number} x Horizontal position to move to. 241ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @private 242ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 243ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen translateTo_: function(x) { 244ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // We use a webkitTransform to slide because this is GPU accelerated on 245ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Chrome and iOS. Once Chrome does GPU acceleration on the position 246ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // fixed-layout elements we could simply set the element's position to 247ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // fixed and modify 'left' instead. 248ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.container_.style.WebkitTransform = 'translate3d(' + x + 'px, 0, 0)'; 249ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen }, 250ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 251ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 252ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * On drag end events we may want to transition to another card, depending 253ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * on the ending position of the drag and the velocity of the drag. 254ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {!TouchHandler.Event} e The TouchHandler event. 255ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @private 256ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 257ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen onDragEnd_: function(e) { 258ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var deltaX = e.dragDeltaX; 259ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var velocity = this.touchHandler_.getEndVelocity().x; 260ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var newX = this.currentLeft_ + deltaX; 261ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var newCardIndex = Math.round(-newX / this.cardWidth_); 262ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 263ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (newCardIndex == this.currentCard && Math.abs(velocity) > 264ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen Slider.TRANSITION_VELOCITY_THRESHOLD_) { 265ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // If the drag wasn't far enough to change cards but the velocity was 266ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // high enough to transition anyways. If the velocity is to the left 267ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // (negative) then the user wishes to go right (card +1). 268ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen newCardIndex += velocity > 0 ? -1 : 1; 269ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 270ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 271ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.selectCard(newCardIndex, /* animate */ true); 272ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen }, 273ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 274ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 275ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Cancel any current touch/slide as if we saw a touch end 276ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 277ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen cancelTouch: function() { 278ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Stop listening to any current touch 279ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.touchHandler_.cancelTouch(); 280ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 281ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Ensure we're at a card bounary 282ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.transformToCurrentCard_(true); 283ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen }, 284ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 285ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 286ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Selects a new card, ensuring that it is a valid index, transforming the 287ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * view and possibly calling the change card callback. 288ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {number} newCardIndex Index of card to show. 289ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {boolean=} opt_animate If true will animate transition from 290ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * current position to new position. 291ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 292ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen selectCard: function(newCardIndex, opt_animate) { 293ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var isChangingCard = newCardIndex >= 0 && 294ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen newCardIndex < this.cards_.length && 295ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen newCardIndex != this.currentCard; 296ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (isChangingCard) { 297ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // If we have a new card index and it is valid then update the left 298ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // position and current card index. 299ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.currentCard_ = newCardIndex; 300ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 301ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 302ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.transformToCurrentCard_(opt_animate); 303ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 304ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (isChangingCard) { 305ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var event = document.createEvent('Event'); 306ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen event.initEvent(Slider.EventType.CARD_CHANGED, true, true); 307ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen event.slider = this; 308ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.container_.dispatchEvent(event); 309ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 310ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen }, 311ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 312ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen /** 313ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * Centers the view on the card denoted by this.currentCard. Can either 314ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * animate to that card or snap to it. 315ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @param {boolean=} opt_animate If true will animate transition from 316ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * current position to new position. 317ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen * @private 318ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen */ 319ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen transformToCurrentCard_: function(opt_animate) { 320ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.currentLeft_ = -this.currentCard * this.cardWidth_; 321ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 322ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Animate to the current card, which will either transition if the 323ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // current card is new, or reset the existing card if we didn't drag 324ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // enough to change cards. 325ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen var transition = ''; 326ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (opt_animate) { 327ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen transition = '-webkit-transform ' + Slider.TRANSITION_TIME_ + 328ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 'ms ease-in-out'; 329ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 330ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.container_.style.WebkitTransition = transition; 331ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen this.translateTo_(this.currentLeft_); 332ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 333ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen }; 334ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 335ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen return Slider; 336ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen})(); 337