Index: chrome/browser/resources/file_manager/js/photo/mosaic_mode.js |
diff --git a/chrome/browser/resources/file_manager/js/photo/mosaic_mode.js b/chrome/browser/resources/file_manager/js/photo/mosaic_mode.js |
index 0bfac6b53ce1da337ba586049703ba08e3443f2a..dd359a53daf77edf4c537b43d0724b48d5cb90e8 100644 |
--- a/chrome/browser/resources/file_manager/js/photo/mosaic_mode.js |
+++ b/chrome/browser/resources/file_manager/js/photo/mosaic_mode.js |
@@ -89,6 +89,12 @@ Mosaic.prototype.__proto__ = HTMLDivElement.prototype; |
Mosaic.LAYOUT_DELAY = 200; |
/** |
+ * Smooth scroll animation duration when scrolling using keyboard or |
+ * clicking on a partly visible tile. In ms. |
+ */ |
+Mosaic.ANIMATED_SCROLL_DURATION = 500; |
+ |
+/** |
* Decorate a Mosaic instance. |
* |
* @param {Mosaic} self Self pointer. |
@@ -170,6 +176,56 @@ Mosaic.prototype.initListeners_ = function() { |
}; |
/** |
+ * Smoothly scrolls the container to the specified position using |
+ * f(x) = sqrt(x) speed function normalized to animation duration. |
+ * @param {number} targetPosition Horizontal scroll position in pixels. |
+ */ |
+Mosaic.prototype.animatedScrollTo = function(targetPosition) { |
+ if (this.scrollTimer_) |
+ 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.
|
+ |
+ // Mouse move events are fired without touching the mouse because of scrolling |
+ // the container. Therefore, these events have to be suppressed. |
+ this.suppressHovering_ = true; |
+ |
+ // Calculates integral area from t1 to t2 of f(x) = sqrt(x) dx. |
+ var integral = function(t1, t2) { |
+ return 2.0 / 3.0 * Math.pow(t2, 3.0 / 2.0) - |
+ 2.0 / 3.0 * Math.pow(t1, 3.0 / 2.0); |
+ }; |
+ |
+ var delta = targetPosition - this.scrollLeft; |
+ var factor = delta / integral(0, Mosaic.ANIMATED_SCROLL_DURATION); |
+ var startTime = Date.now(); |
+ var lastPosition = 0; |
+ |
+ 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.
|
+ var position = Date.now() - startTime; |
+ var step = factor * |
+ integral(Math.max(0, Mosaic.ANIMATED_SCROLL_DURATION - position), |
+ Math.max(0, Mosaic.ANIMATED_SCROLL_DURATION - lastPosition)); |
+ |
+ var lastScrollLeft = this.scrollLeft; |
+ console.log(step); |
yoshiki
2013/02/12 01:30:52
Remove?
mtomasz
2013/02/12 04:19:21
Done.
|
+ this.scrollLeft += step; |
+ |
+ 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
|
+ clearInterval(this.scrollTimer_); |
+ this.scrollTimer_ = null; |
+ |
+ // Release the hovering lock after a safe delay to avoid hovering |
+ // a tile because of altering |this.scrollLeft|. |
+ setTimeout(function() { |
+ if (!this.scrollTimer_) |
+ this.suppressHovering_ = false; |
+ }.bind(this), 100); |
+ } |
+ |
+ lastPosition = position; |
+ }.bind(this), 10); |
+}; |
+ |
+/** |
* @return {Mosaic.Tile} Selected tile or undefined if no selection. |
*/ |
Mosaic.prototype.getSelectedTile = function() { |
@@ -312,7 +368,8 @@ Mosaic.prototype.onResize_ = function() { |
*/ |
Mosaic.prototype.onMouseEvent_ = function(event) { |
// Navigating with mouse, enable hover state. |
- this.classList.add('hover-visible'); |
+ if (!this.suppressHovering_) |
+ this.classList.add('hover-visible'); |
if (event.type == 'mousemove') |
return; |
@@ -570,6 +627,12 @@ Mosaic.Layout.PADDING_BOTTOM = 70; |
Mosaic.Layout.SPACING = 10; |
/** |
+ * Margin for scrolling using keyboard. Distance between a selected tile |
+ * and window border. |
+ */ |
+Mosaic.Layout.SCROLL_MARGIN = 30; |
+ |
+/** |
* Layout mode: commit to DOM immediately. |
*/ |
Mosaic.Layout.MODE_FINAL = 'final'; |
@@ -1603,7 +1666,7 @@ Mosaic.Tile.prototype.layout = function(left, top, width, height) { |
this.wrapper_ = util.createChild(border, 'img-wrapper'); |
} |
if (this.hasAttribute('selected')) |
- this.scrollIntoView(); |
+ this.scrollIntoView(false); |
this.thumbnailLoader_.attachImage(this.wrapper_, |
ThumbnailLoader.FillMode.FILL); |
@@ -1611,18 +1674,29 @@ Mosaic.Tile.prototype.layout = function(left, top, width, height) { |
/** |
* If the tile is not fully visible scroll the parent to make it fully visible. |
+ * @param {boolean} opt_animated True, if scroll should be animated, |
+ * default: true. |
*/ |
-Mosaic.Tile.prototype.scrollIntoView = function() { |
+Mosaic.Tile.prototype.scrollIntoView = function(opt_animated) { |
if (this.left_ == null) // Not laid out. |
return; |
- if (this.left_ < this.container_.scrollLeft) { |
- this.container_.scrollLeft = this.left_; |
+ var targetPosition; |
+ var tileLeft = this.left_ - Mosaic.Layout.SCROLL_MARGIN; |
+ if (tileLeft < this.container_.scrollLeft) { |
+ targetPosition = tileLeft; |
} else { |
- var tileRight = this.left_ + this.width_; |
+ var tileRight = this.left_ + this.width_ + Mosaic.Layout.SCROLL_MARGIN; |
var scrollRight = this.container_.scrollLeft + this.container_.clientWidth; |
if (tileRight > scrollRight) |
- this.container_.scrollLeft = tileRight - this.container_.clientWidth; |
+ targetPosition = tileRight - this.container_.clientWidth; |
+ } |
+ |
+ if (targetPosition) { |
+ if (opt_animated === false) |
+ this.container_.scrollLeft = targetPosition; |
+ else |
+ this.container_.animatedScrollTo(targetPosition); |
} |
}; |