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

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: Rebased. 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.scrollAnimation_) {
185 webkitCancelAnimationFrame(this.scrollAnimation_);
186 this.scrollAnimation_ = null;
187 }
188
189 // Mouse move events are fired without touching the mouse because of scrolling
190 // the container. Therefore, these events have to be suppressed.
191 this.suppressHovering_ = true;
192
193 // Calculates integral area from t1 to t2 of f(x) = sqrt(x) dx.
194 var integral = function(t1, t2) {
195 return 2.0 / 3.0 * Math.pow(t2, 3.0 / 2.0) -
196 2.0 / 3.0 * Math.pow(t1, 3.0 / 2.0);
197 };
198
199 var delta = targetPosition - this.scrollLeft;
200 var factor = delta / integral(0, Mosaic.ANIMATED_SCROLL_DURATION);
201 var startTime = Date.now();
202 var lastPosition = 0;
203 var scrollOffset = this.scrollLeft;
204
205 var animationFrame = function() {
206 var position = Date.now() - startTime;
207 var step = factor *
208 integral(Math.max(0, Mosaic.ANIMATED_SCROLL_DURATION - position),
209 Math.max(0, Mosaic.ANIMATED_SCROLL_DURATION - lastPosition));
210 scrollOffset += step;
211
212 var oldScrollLeft = this.scrollLeft;
213 var newScrollLeft = Math.round(scrollOffset);
214
215 if (oldScrollLeft != newScrollLeft)
216 this.scrollLeft = newScrollLeft;
217
218 if (step == 0 || this.scrollLeft != newScrollLeft) {
219 this.scrollAnimation_ = null;
220 // Release the hovering lock after a safe delay to avoid hovering
221 // a tile because of altering |this.scrollLeft|.
222 setTimeout(function() {
223 if (!this.scrollAnimation_)
224 this.suppressHovering_ = false;
225 }.bind(this), 100);
226 } else {
227 // Continue the animation.
228 this.scrollAnimation_ = requestAnimationFrame(animationFrame);
229 }
230
231 lastPosition = position;
232 }.bind(this);
233
234 // Start the animation.
235 this.scrollAnimation_ = requestAnimationFrame(animationFrame);
236 };
237
238 /**
173 * @return {Mosaic.Tile} Selected tile or undefined if no selection. 239 * @return {Mosaic.Tile} Selected tile or undefined if no selection.
174 */ 240 */
175 Mosaic.prototype.getSelectedTile = function() { 241 Mosaic.prototype.getSelectedTile = function() {
176 return this.tiles_ && this.tiles_[this.selectionModel_.selectedIndex]; 242 return this.tiles_ && this.tiles_[this.selectionModel_.selectedIndex];
177 }; 243 };
178 244
179 /** 245 /**
180 * @param {number} index Tile index. 246 * @param {number} index Tile index.
181 * @return {Rect} Tile's image rectangle. 247 * @return {Rect} Tile's image rectangle.
182 */ 248 */
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after
305 }; 371 };
306 372
307 /** 373 /**
308 * Mouse event handler. 374 * Mouse event handler.
309 * 375 *
310 * @param {Event} event Event. 376 * @param {Event} event Event.
311 * @private 377 * @private
312 */ 378 */
313 Mosaic.prototype.onMouseEvent_ = function(event) { 379 Mosaic.prototype.onMouseEvent_ = function(event) {
314 // Navigating with mouse, enable hover state. 380 // Navigating with mouse, enable hover state.
315 this.classList.add('hover-visible'); 381 if (!this.suppressHovering_)
382 this.classList.add('hover-visible');
316 383
317 if (event.type == 'mousemove') 384 if (event.type == 'mousemove')
318 return; 385 return;
319 386
320 var index = -1; 387 var index = -1;
321 for (var target = event.target; 388 for (var target = event.target;
322 target && (target != this); 389 target && (target != this);
323 target = target.parentNode) { 390 target = target.parentNode) {
324 if (target.classList.contains('mosaic-tile')) { 391 if (target.classList.contains('mosaic-tile')) {
325 index = this.dataModel_.indexOf(target.getItem()); 392 index = this.dataModel_.indexOf(target.getItem());
(...skipping 236 matching lines...) Expand 10 before | Expand all | Expand 10 after
562 */ 629 */
563 Mosaic.Layout.PADDING_BOTTOM = 50; 630 Mosaic.Layout.PADDING_BOTTOM = 50;
564 631
565 /** 632 /**
566 * Horizontal and vertical spacing between images. Should be kept in sync 633 * Horizontal and vertical spacing between images. Should be kept in sync
567 * with the style of .mosaic-item in gallery.css (= 2 * ( 4 + 1)) 634 * with the style of .mosaic-item in gallery.css (= 2 * ( 4 + 1))
568 */ 635 */
569 Mosaic.Layout.SPACING = 10; 636 Mosaic.Layout.SPACING = 10;
570 637
571 /** 638 /**
639 * Margin for scrolling using keyboard. Distance between a selected tile
640 * and window border.
641 */
642 Mosaic.Layout.SCROLL_MARGIN = 30;
643
644 /**
572 * Layout mode: commit to DOM immediately. 645 * Layout mode: commit to DOM immediately.
573 */ 646 */
574 Mosaic.Layout.MODE_FINAL = 'final'; 647 Mosaic.Layout.MODE_FINAL = 'final';
575 648
576 /** 649 /**
577 * Layout mode: do not commit layout to DOM until it is complete or the viewport 650 * Layout mode: do not commit layout to DOM until it is complete or the viewport
578 * overflows. 651 * overflows.
579 */ 652 */
580 Mosaic.Layout.MODE_TENTATIVE = 'tentative'; 653 Mosaic.Layout.MODE_TENTATIVE = 'tentative';
581 654
(...skipping 1014 matching lines...) Expand 10 before | Expand all | Expand 10 after
1596 this.style.top = top + 'px'; 1669 this.style.top = top + 'px';
1597 this.style.width = width + 'px'; 1670 this.style.width = width + 'px';
1598 this.style.height = height + 'px'; 1671 this.style.height = height + 'px';
1599 1672
1600 if (!this.wrapper_) { // First time, create DOM. 1673 if (!this.wrapper_) { // First time, create DOM.
1601 this.container_.appendChild(this); 1674 this.container_.appendChild(this);
1602 var border = util.createChild(this, 'img-border'); 1675 var border = util.createChild(this, 'img-border');
1603 this.wrapper_ = util.createChild(border, 'img-wrapper'); 1676 this.wrapper_ = util.createChild(border, 'img-wrapper');
1604 } 1677 }
1605 if (this.hasAttribute('selected')) 1678 if (this.hasAttribute('selected'))
1606 this.scrollIntoView(); 1679 this.scrollIntoView(false);
1607 1680
1608 this.thumbnailLoader_.attachImage(this.wrapper_, 1681 this.thumbnailLoader_.attachImage(this.wrapper_,
1609 ThumbnailLoader.FillMode.FILL); 1682 ThumbnailLoader.FillMode.FILL);
1610 }; 1683 };
1611 1684
1612 /** 1685 /**
1613 * If the tile is not fully visible scroll the parent to make it fully visible. 1686 * If the tile is not fully visible scroll the parent to make it fully visible.
1687 * @param {boolean} opt_animated True, if scroll should be animated,
1688 * default: true.
1614 */ 1689 */
1615 Mosaic.Tile.prototype.scrollIntoView = function() { 1690 Mosaic.Tile.prototype.scrollIntoView = function(opt_animated) {
1616 if (this.left_ == null) // Not laid out. 1691 if (this.left_ == null) // Not laid out.
1617 return; 1692 return;
1618 1693
1619 if (this.left_ < this.container_.scrollLeft) { 1694 var targetPosition;
1620 this.container_.scrollLeft = this.left_; 1695 var tileLeft = this.left_ - Mosaic.Layout.SCROLL_MARGIN;
1696 if (tileLeft < this.container_.scrollLeft) {
1697 targetPosition = tileLeft;
1621 } else { 1698 } else {
1622 var tileRight = this.left_ + this.width_; 1699 var tileRight = this.left_ + this.width_ + Mosaic.Layout.SCROLL_MARGIN;
1623 var scrollRight = this.container_.scrollLeft + this.container_.clientWidth; 1700 var scrollRight = this.container_.scrollLeft + this.container_.clientWidth;
1624 if (tileRight > scrollRight) 1701 if (tileRight > scrollRight)
1625 this.container_.scrollLeft = tileRight - this.container_.clientWidth; 1702 targetPosition = tileRight - this.container_.clientWidth;
1703 }
1704
1705 if (targetPosition) {
1706 if (opt_animated === false)
1707 this.container_.scrollLeft = targetPosition;
1708 else
1709 this.container_.animatedScrollTo(targetPosition);
1626 } 1710 }
1627 }; 1711 };
1628 1712
1629 /** 1713 /**
1630 * @return {Rect} Rectangle occupied by the tile's image, 1714 * @return {Rect} Rectangle occupied by the tile's image,
1631 * relative to the viewport. 1715 * relative to the viewport.
1632 */ 1716 */
1633 Mosaic.Tile.prototype.getImageRect = function() { 1717 Mosaic.Tile.prototype.getImageRect = function() {
1634 if (this.left_ == null) // Not laid out. 1718 if (this.left_ == null) // Not laid out.
1635 return null; 1719 return null;
1636 1720
1637 var margin = Mosaic.Layout.SPACING / 2; 1721 var margin = Mosaic.Layout.SPACING / 2;
1638 return new Rect(this.left_ - this.container_.scrollLeft, this.top_, 1722 return new Rect(this.left_ - this.container_.scrollLeft, this.top_,
1639 this.width_, this.height_).inflate(-margin, -margin); 1723 this.width_, this.height_).inflate(-margin, -margin);
1640 }; 1724 };
1641 1725
1642 /** 1726 /**
1643 * @return {number} X coordinate of the tile center. 1727 * @return {number} X coordinate of the tile center.
1644 */ 1728 */
1645 Mosaic.Tile.prototype.getCenterX = function() { 1729 Mosaic.Tile.prototype.getCenterX = function() {
1646 return this.left_ + Math.round(this.width_ / 2); 1730 return this.left_ + Math.round(this.width_ / 2);
1647 }; 1731 };
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