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

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

Powered by Google App Engine
This is Rietveld 408576698