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

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

Powered by Google App Engine
This is Rietveld 408576698