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

Side by Side Diff: chrome/browser/resources/file_manager/js/photo/mosaic_mode.js

Issue 12208042: Smooth scrolling effect to mosaic view. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Cleaned up. Created 7 years, 10 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
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 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 * @param {Element} container Content container. 6 * @param {Element} container Content container.
7 * @param {cr.ui.ArrayDataModel} dataModel Data model. 7 * @param {cr.ui.ArrayDataModel} dataModel Data model.
8 * @param {cr.ui.ListSelectionModel} selectionModel Selection model. 8 * @param {cr.ui.ListSelectionModel} selectionModel Selection model.
9 * @param {MetadataCache} metadataCache Metadata cache. 9 * @param {MetadataCache} metadataCache Metadata cache.
10 * @param {function} toggleMode Function to switch to the Slide mode. 10 * @param {function} toggleMode Function to switch to the Slide mode.
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after
82 * Inherit from HTMLDivElement. 82 * Inherit from HTMLDivElement.
83 */ 83 */
84 Mosaic.prototype.__proto__ = HTMLDivElement.prototype; 84 Mosaic.prototype.__proto__ = HTMLDivElement.prototype;
85 85
86 /** 86 /**
87 * Default layout delay in ms. 87 * Default layout delay in ms.
88 */ 88 */
89 Mosaic.LAYOUT_DELAY = 200; 89 Mosaic.LAYOUT_DELAY = 200;
90 90
91 /** 91 /**
92 * Smooth scroll animation duration when scrolling using keyboard or
93 * clicking on a partly visible tile. In ms.
94 */
95 Mosaic.ANIMATED_SCROLL_DURATION = 500;
96
97 /**
92 * Decorate a Mosaic instance. 98 * Decorate a Mosaic instance.
93 * 99 *
94 * @param {Mosaic} self Self pointer. 100 * @param {Mosaic} self Self pointer.
95 * @param {cr.ui.ArrayDataModel} dataModel Data model. 101 * @param {cr.ui.ArrayDataModel} dataModel Data model.
96 * @param {cr.ui.ListSelectionModel} selectionModel Selection model. 102 * @param {cr.ui.ListSelectionModel} selectionModel Selection model.
97 * @param {MetadataCache} metadataCache Metadata cache. 103 * @param {MetadataCache} metadataCache Metadata cache.
98 * @param {function(string)} onThumbnailError Thumbnail load error handler. 104 * @param {function(string)} onThumbnailError Thumbnail load error handler.
99 */ 105 */
100 Mosaic.decorate = function(self, dataModel, selectionModel, metadataCache, 106 Mosaic.decorate = function(self, dataModel, selectionModel, metadataCache,
101 onThumbnailError) { 107 onThumbnailError) {
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
163 169
164 this.selectionModel_.addEventListener('change', this.onSelection_.bind(this)); 170 this.selectionModel_.addEventListener('change', this.onSelection_.bind(this));
165 this.selectionModel_.addEventListener('leadIndexChange', 171 this.selectionModel_.addEventListener('leadIndexChange',
166 this.onLeadChange_.bind(this)); 172 this.onLeadChange_.bind(this));
167 173
168 this.dataModel_.addEventListener('splice', this.onSplice_.bind(this)); 174 this.dataModel_.addEventListener('splice', this.onSplice_.bind(this));
169 this.dataModel_.addEventListener('content', this.onContentChange_.bind(this)); 175 this.dataModel_.addEventListener('content', this.onContentChange_.bind(this));
170 }; 176 };
171 177
172 /** 178 /**
179 * Smoothly scrolls the container to the specified position using
180 * f(x) = sqrt(x) speed function normalized to animation duration.
181 * @param {number} targetPosition Horizontal scroll position in pixels.
182 */
183 Mosaic.prototype.animatedScrollTo = function(targetPosition) {
184 if (this.scrollTimer_)
185 clearInterval(this.scrollTimer_);
yoshiki 2013/02/12 01:30:52 Assign null or undefined after clearing the interv
mtomasz 2013/02/12 04:19:21 Done.
186
187 // Mouse move events are fired without touching the mouse because of scrolling
188 // the container. Therefore, these events have to be suppressed.
189 this.suppressHovering_ = true;
190
191 // Calculates integral area from t1 to t2 of f(x) = sqrt(x) dx.
192 var integral = function(t1, t2) {
193 return 2.0 / 3.0 * Math.pow(t2, 3.0 / 2.0) -
194 2.0 / 3.0 * Math.pow(t1, 3.0 / 2.0);
195 };
196
197 var delta = targetPosition - this.scrollLeft;
198 var factor = delta / integral(0, Mosaic.ANIMATED_SCROLL_DURATION);
199 var startTime = Date.now();
200 var lastPosition = 0;
201
202 this.scrollTimer_ = setInterval(function() {
yoshiki 2013/02/12 01:30:52 It is better to use "requestAnimationFrame" on ani
mtomasz 2013/02/12 04:19:21 This is great, I didn't know about it! Done.
203 var position = Date.now() - startTime;
204 var step = factor *
205 integral(Math.max(0, Mosaic.ANIMATED_SCROLL_DURATION - position),
206 Math.max(0, Mosaic.ANIMATED_SCROLL_DURATION - lastPosition));
207
208 var lastScrollLeft = this.scrollLeft;
209 console.log(step);
yoshiki 2013/02/12 01:30:52 Remove?
mtomasz 2013/02/12 04:19:21 Done.
210 this.scrollLeft += step;
211
212 if (lastScrollLeft == this.scrollLeft) {
yoshiki 2013/02/12 01:30:52 We can replace this condition with just "step == 0
mtomasz 2013/02/12 04:19:21 Nope, this didn't work when reaching edges, eg. wh
213 clearInterval(this.scrollTimer_);
214 this.scrollTimer_ = null;
215
216 // Release the hovering lock after a safe delay to avoid hovering
217 // a tile because of altering |this.scrollLeft|.
218 setTimeout(function() {
219 if (!this.scrollTimer_)
220 this.suppressHovering_ = false;
221 }.bind(this), 100);
222 }
223
224 lastPosition = position;
225 }.bind(this), 10);
226 };
227
228 /**
173 * @return {Mosaic.Tile} Selected tile or undefined if no selection. 229 * @return {Mosaic.Tile} Selected tile or undefined if no selection.
174 */ 230 */
175 Mosaic.prototype.getSelectedTile = function() { 231 Mosaic.prototype.getSelectedTile = function() {
176 return this.tiles_ && this.tiles_[this.selectionModel_.selectedIndex]; 232 return this.tiles_ && this.tiles_[this.selectionModel_.selectedIndex];
177 }; 233 };
178 234
179 /** 235 /**
180 * @param {number} index Tile index. 236 * @param {number} index Tile index.
181 * @return {Rect} Tile's image rectangle. 237 * @return {Rect} Tile's image rectangle.
182 */ 238 */
183 Mosaic.prototype.getTileRect = function(index) { 239 Mosaic.prototype.getTileRect = function(index) {
184 var tile = this.tiles_[index]; 240 var tile = this.tiles_[index];
185 return tile && tile.getImageRect(); 241 return tile && tile.getImageRect();
186 }; 242 };
187 243
188 /** 244 /**
189 * @param {number} index Tile index. 245 * @param {number} index Tile index.
190 * Scroll the given tile into the viewport. 246 * Scroll the given tile into the viewport.
191 */ 247 */
192 Mosaic.prototype.scrollIntoView = function(index) { 248 Mosaic.prototype.scrollIntoView = function(index) {
193 var tile = this.tiles_[index]; 249 var tile = this.tiles_[index];
194 if (tile) tile.scrollIntoView(); 250 if (tile) tile.scrollIntoView();
yoshiki 2013/02/12 01:30:52 Add the boolean argument |opt_animated|.
mtomasz 2013/02/12 04:19:21 Can you clarify? This argument is optional.
yoshiki 2013/02/12 04:49:10 Sorry, it was my mistake. It's optional. On 2013/
195 }; 251 };
196 252
197 /** 253 /**
198 * Load multiple tiles. 254 * Load multiple tiles.
199 * 255 *
200 * @param {Array.<Mosaic.Tile>} tiles Array of tiles. 256 * @param {Array.<Mosaic.Tile>} tiles Array of tiles.
201 * @param {function} opt_callback Completion callback. 257 * @param {function} opt_callback Completion callback.
202 * @private 258 * @private
203 */ 259 */
204 Mosaic.prototype.loadTiles_ = function(tiles, opt_callback) { 260 Mosaic.prototype.loadTiles_ = function(tiles, opt_callback) {
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after
305 }; 361 };
306 362
307 /** 363 /**
308 * Mouse event handler. 364 * Mouse event handler.
309 * 365 *
310 * @param {Event} event Event. 366 * @param {Event} event Event.
311 * @private 367 * @private
312 */ 368 */
313 Mosaic.prototype.onMouseEvent_ = function(event) { 369 Mosaic.prototype.onMouseEvent_ = function(event) {
314 // Navigating with mouse, enable hover state. 370 // Navigating with mouse, enable hover state.
315 this.classList.add('hover-visible'); 371 if (!this.suppressHovering_)
372 this.classList.add('hover-visible');
316 373
317 if (event.type == 'mousemove') 374 if (event.type == 'mousemove')
318 return; 375 return;
319 376
320 var index = -1; 377 var index = -1;
321 for (var target = event.target; 378 for (var target = event.target;
322 target && (target != this); 379 target && (target != this);
323 target = target.parentNode) { 380 target = target.parentNode) {
324 if (target.classList.contains('mosaic-tile')) { 381 if (target.classList.contains('mosaic-tile')) {
325 index = this.dataModel_.indexOf(target.getItem()); 382 index = this.dataModel_.indexOf(target.getItem());
(...skipping 20 matching lines...) Expand all
346 /** 403 /**
347 * Lead item change handler. 404 * Lead item change handler.
348 * 405 *
349 * @param {Event} event Event. 406 * @param {Event} event Event.
350 * @private 407 * @private
351 */ 408 */
352 Mosaic.prototype.onLeadChange_ = function(event) { 409 Mosaic.prototype.onLeadChange_ = function(event) {
353 var index = event.newValue; 410 var index = event.newValue;
354 if (index >= 0) { 411 if (index >= 0) {
355 var tile = this.tiles_[index]; 412 var tile = this.tiles_[index];
356 if (tile) tile.scrollIntoView(); 413 if (tile) tile.scrollIntoView();
yoshiki 2013/02/12 01:30:52 Add the boolean argument |opt_animated|.
mtomasz 2013/02/12 04:19:21 As above, please clarify.
357 } 414 }
358 }; 415 };
359 416
360 /** 417 /**
361 * Splice event handler. 418 * Splice event handler.
362 * 419 *
363 * @param {Event} event Event. 420 * @param {Event} event Event.
364 * @private 421 * @private
365 */ 422 */
366 Mosaic.prototype.onSplice_ = function(event) { 423 Mosaic.prototype.onSplice_ = function(event) {
(...skipping 196 matching lines...) Expand 10 before | Expand all | Expand 10 after
563 */ 620 */
564 Mosaic.Layout.PADDING_BOTTOM = 70; 621 Mosaic.Layout.PADDING_BOTTOM = 70;
565 622
566 /** 623 /**
567 * Horizontal and vertical spacing between images. Should be kept in sync 624 * Horizontal and vertical spacing between images. Should be kept in sync
568 * with the style of .mosaic-item in gallery.css (= 2 * ( 4 + 1)) 625 * with the style of .mosaic-item in gallery.css (= 2 * ( 4 + 1))
569 */ 626 */
570 Mosaic.Layout.SPACING = 10; 627 Mosaic.Layout.SPACING = 10;
571 628
572 /** 629 /**
630 * Margin for scrolling using keyboard. Distance between a selected tile
631 * and window border.
632 */
633 Mosaic.Layout.SCROLL_MARGIN = 30;
634
635 /**
573 * Layout mode: commit to DOM immediately. 636 * Layout mode: commit to DOM immediately.
574 */ 637 */
575 Mosaic.Layout.MODE_FINAL = 'final'; 638 Mosaic.Layout.MODE_FINAL = 'final';
576 639
577 /** 640 /**
578 * Layout mode: do not commit layout to DOM until it is complete or the viewport 641 * Layout mode: do not commit layout to DOM until it is complete or the viewport
579 * overflows. 642 * overflows.
580 */ 643 */
581 Mosaic.Layout.MODE_TENTATIVE = 'tentative'; 644 Mosaic.Layout.MODE_TENTATIVE = 'tentative';
582 645
(...skipping 1013 matching lines...) Expand 10 before | Expand all | Expand 10 after
1596 this.style.top = top + 'px'; 1659 this.style.top = top + 'px';
1597 this.style.width = width + 'px'; 1660 this.style.width = width + 'px';
1598 this.style.height = height + 'px'; 1661 this.style.height = height + 'px';
1599 1662
1600 if (!this.wrapper_) { // First time, create DOM. 1663 if (!this.wrapper_) { // First time, create DOM.
1601 this.container_.appendChild(this); 1664 this.container_.appendChild(this);
1602 var border = util.createChild(this, 'img-border'); 1665 var border = util.createChild(this, 'img-border');
1603 this.wrapper_ = util.createChild(border, 'img-wrapper'); 1666 this.wrapper_ = util.createChild(border, 'img-wrapper');
1604 } 1667 }
1605 if (this.hasAttribute('selected')) 1668 if (this.hasAttribute('selected'))
1606 this.scrollIntoView(); 1669 this.scrollIntoView(false);
1607 1670
1608 this.thumbnailLoader_.attachImage(this.wrapper_, 1671 this.thumbnailLoader_.attachImage(this.wrapper_,
1609 ThumbnailLoader.FillMode.FILL); 1672 ThumbnailLoader.FillMode.FILL);
1610 }; 1673 };
1611 1674
1612 /** 1675 /**
1613 * If the tile is not fully visible scroll the parent to make it fully visible. 1676 * If the tile is not fully visible scroll the parent to make it fully visible.
1677 * @param {boolean} opt_animated True, if scroll should be animated,
1678 * default: true.
1614 */ 1679 */
1615 Mosaic.Tile.prototype.scrollIntoView = function() { 1680 Mosaic.Tile.prototype.scrollIntoView = function(opt_animated) {
1616 if (this.left_ == null) // Not laid out. 1681 if (this.left_ == null) // Not laid out.
1617 return; 1682 return;
1618 1683
1619 if (this.left_ < this.container_.scrollLeft) { 1684 var targetPosition;
1620 this.container_.scrollLeft = this.left_; 1685 var tileLeft = this.left_ - Mosaic.Layout.SCROLL_MARGIN;
1686 if (tileLeft < this.container_.scrollLeft) {
1687 targetPosition = tileLeft;
1621 } else { 1688 } else {
1622 var tileRight = this.left_ + this.width_; 1689 var tileRight = this.left_ + this.width_ + Mosaic.Layout.SCROLL_MARGIN;
1623 var scrollRight = this.container_.scrollLeft + this.container_.clientWidth; 1690 var scrollRight = this.container_.scrollLeft + this.container_.clientWidth;
1624 if (tileRight > scrollRight) 1691 if (tileRight > scrollRight)
1625 this.container_.scrollLeft = tileRight - this.container_.clientWidth; 1692 targetPosition = tileRight - this.container_.clientWidth;
1693 }
1694
1695 if (targetPosition) {
1696 if (opt_animated === false)
1697 this.container_.scrollLeft = targetPosition;
1698 else
1699 this.container_.animatedScrollTo(targetPosition);
1626 } 1700 }
1627 }; 1701 };
1628 1702
1629 /** 1703 /**
1630 * @return {Rect} Rectangle occupied by the tile's image, 1704 * @return {Rect} Rectangle occupied by the tile's image,
1631 * relative to the viewport. 1705 * relative to the viewport.
1632 */ 1706 */
1633 Mosaic.Tile.prototype.getImageRect = function() { 1707 Mosaic.Tile.prototype.getImageRect = function() {
1634 if (this.left_ == null) // Not laid out. 1708 if (this.left_ == null) // Not laid out.
1635 return null; 1709 return null;
1636 1710
1637 var margin = Mosaic.Layout.SPACING / 2; 1711 var margin = Mosaic.Layout.SPACING / 2;
1638 return new Rect(this.left_ - this.container_.scrollLeft, this.top_, 1712 return new Rect(this.left_ - this.container_.scrollLeft, this.top_,
1639 this.width_, this.height_).inflate(-margin, -margin); 1713 this.width_, this.height_).inflate(-margin, -margin);
1640 }; 1714 };
1641 1715
1642 /** 1716 /**
1643 * @return {number} X coordinate of the tile center. 1717 * @return {number} X coordinate of the tile center.
1644 */ 1718 */
1645 Mosaic.Tile.prototype.getCenterX = function() { 1719 Mosaic.Tile.prototype.getCenterX = function() {
1646 return this.left_ + Math.round(this.width_ / 2); 1720 return this.left_ + Math.round(this.width_ / 2);
1647 }; 1721 };
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698