summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authorestade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-05-03 14:55:58 +0000
committerestade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-05-03 14:55:58 +0000
commitc40520ee3eb467320be787111bdcb180fe27dbdd (patch)
tree98b188668fbc5a08ea5bb972864e49ec48f1dfd6 /chrome
parent59744fee620a78ef5fc80f26a0d88007ca245a23 (diff)
downloadchromium_src-c40520ee3eb467320be787111bdcb180fe27dbdd.zip
chromium_src-c40520ee3eb467320be787111bdcb180fe27dbdd.tar.gz
chromium_src-c40520ee3eb467320be787111bdcb180fe27dbdd.tar.bz2
Improve DnD effects on ntp4
dragging from one row to another should cause an effect where tiles, instead of jumping between rows, slide off the side and reappear/slide into the correct place. BUG=none TEST=manual Review URL: http://codereview.chromium.org/6902186 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@83887 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/browser/resources/ntp4/most_visited_page.js10
-rw-r--r--chrome/browser/resources/ntp4/tile_page.css8
-rw-r--r--chrome/browser/resources/ntp4/tile_page.js132
-rw-r--r--chrome/test/data/webui/most_visited_page_test.js10
4 files changed, 145 insertions, 15 deletions
diff --git a/chrome/browser/resources/ntp4/most_visited_page.js b/chrome/browser/resources/ntp4/most_visited_page.js
index 4fdd390..a01281a 100644
--- a/chrome/browser/resources/ntp4/most_visited_page.js
+++ b/chrome/browser/resources/ntp4/most_visited_page.js
@@ -40,7 +40,7 @@ cr.define('ntp4', function() {
* for a blank thumbnail.
*/
reset: function() {
- this.className = 'most-visited filler';
+ this.className = 'most-visited filler real';
// TODO(estade): why do we need edit-mode-border?
this.innerHTML =
'<div class="edit-mode-border fills-parent">' +
@@ -225,7 +225,7 @@ cr.define('ntp4', function() {
this.classList.add('most-visited-page');
this.data_ = null;
- this.mostVisitedTiles_ = this.getElementsByClassName('most-visited');
+ this.mostVisitedTiles_ = this.getElementsByClassName('most-visited real');
},
/**
@@ -339,8 +339,10 @@ cr.define('ntp4', function() {
}
// Clear 'updated' flags so this function will work next time it's called.
- for (var i = 0; i < oldData.length; i++)
- oldData[i].updated = false;
+ for (var i = 0; i < THUMBNAIL_COUNT; i++) {
+ if (oldData[i])
+ oldData[i].updated = false;
+ }
return oldData;
};
diff --git a/chrome/browser/resources/ntp4/tile_page.css b/chrome/browser/resources/ntp4/tile_page.css
index c7e966f..7fe3499 100644
--- a/chrome/browser/resources/ntp4/tile_page.css
+++ b/chrome/browser/resources/ntp4/tile_page.css
@@ -32,6 +32,11 @@
-webkit-user-drag: element;
}
+.doppleganger {
+ left: 0 !important;
+ top: 0 !important;
+}
+
.tile.dragging {
z-index: 10;
-webkit-transition-duration: 0 !important;
@@ -41,6 +46,7 @@
background-color: green;
}
-.animating-tile-page .tile {
+.animating-tile-page .tile,
+.tile.placing {
-webkit-transition: left 200ms, top 200ms;
}
diff --git a/chrome/browser/resources/ntp4/tile_page.js b/chrome/browser/resources/ntp4/tile_page.js
index 9147d24..e1dcd43 100644
--- a/chrome/browser/resources/ntp4/tile_page.js
+++ b/chrome/browser/resources/ntp4/tile_page.js
@@ -23,13 +23,16 @@ cr.define('ntp4', function() {
__proto__: HTMLDivElement.prototype,
initialize: function(contents) {
- this.className = 'tile';
+ // 'real' as opposed to doppleganger.
+ this.className = 'tile real';
this.appendChild(contents);
contents.tile = this;
this.addEventListener('dragstart', this.onDragStart_);
this.addEventListener('drag', this.onDragMove_);
this.addEventListener('dragend', this.onDragEnd_);
+
+ this.addEventListener('webkitTransitionEnd', this.onTransitionEnd_);
},
get index() {
@@ -95,8 +98,57 @@ cr.define('ntp4', function() {
onDragEnd_: function(e) {
TilePage.currentlyDraggingTile = null;
this.classList.remove('dragging');
+ // This class is required for the tile to animate to its final position.
+ this.classList.add('placing');
this.tilePage.positionTile_(this.index);
},
+
+ /**
+ * Creates a clone of this node offset by the coordinates. Used for the
+ * dragging effect where a tile appears to float off one side of the grid
+ * and re-appear on the other.
+ * @param {number} x x-axis offset, in pixels.
+ * @param {number} y y-axis offset, in pixels.
+ */
+ showDoppleganger: function(x, y) {
+ // We always have to clear the previous doppleganger to make sure we get
+ // style updates for the contents of this tile.
+ this.clearDoppleganger();
+
+ var clone = this.cloneNode(true);
+ clone.classList.remove('real');
+ clone.classList.add('doppleganger');
+ var clonelets = clone.querySelectorAll('.real');
+ for (var i = 0; i < clonelets.length; i++) {
+ clonelets[i].classList.remove('real');
+ }
+
+ this.appendChild(clone);
+ this.doppleganger_ = clone;
+
+ this.doppleganger_.style.WebkitTransform = 'translate(' + x + 'px, ' +
+ y + 'px)';
+ },
+
+ /**
+ * Destroys the current doppleganger.
+ */
+ clearDoppleganger: function() {
+ if (this.doppleganger_) {
+ this.removeChild(this.doppleganger_);
+ this.doppleganger_ = null;
+ }
+ },
+
+ /**
+ * When a positioning transition ends, remove the 'placing' class so further
+ * positioning won't necessarily be animated.
+ * @param {Event} e The transition end event.
+ */
+ onTransitionEnd_: function(e) {
+ if (e.propertyName == 'top' || e.propertyName == 'left')
+ this.classList.remove('placing');
+ }
};
/**
@@ -200,9 +252,9 @@ cr.define('ntp4', function() {
this.appendChild(this.tileGrid_);
// Ordered list of our tiles.
- this.tileElements_ = this.tileGrid_.getElementsByClassName('tile');
+ this.tileElements_ = this.tileGrid_.getElementsByClassName('tile real');
- this.lastWidth_ = this.clientWidth;
+ this.maskWidth_ = this.clientWidth;
this.eventTracker = new EventTracker();
this.eventTracker.add(window, 'resize', this.onResize_.bind(this));
@@ -233,6 +285,10 @@ cr.define('ntp4', function() {
this.positionTile_(this.tileElements_.length - 1);
this.classList.remove('animating-tile-page');
+
+ // This would be in initialize(), but at that point we don't yet know our
+ // width. This won't do any work if it doesn't need to update the mask.
+ this.updateMask_();
},
/**
@@ -282,8 +338,10 @@ cr.define('ntp4', function() {
var layout = this.calculateLayoutValues_();
indexOffset = typeof indexOffset != 'undefined' ? indexOffset : 0;
- var col = (index + indexOffset) % layout.numRowTiles;
- var row = Math.floor((index + indexOffset) / layout.numRowTiles);
+ // Add the offset _after_ the modulus division. We might want to show the
+ // tile off the side of the grid.
+ var col = index % layout.numRowTiles + indexOffset;
+ var row = Math.floor(index / layout.numRowTiles);
// Calculate the final on-screen position for the tile.
var realX = col * layout.colWidth + layout.leftMargin;
var realY = row * layout.rowHeight;
@@ -303,6 +361,18 @@ cr.define('ntp4', function() {
tile.firstChild.setBounds(layout.tileWidth,
realX - animatedX,
realY - animatedY);
+
+ // This code calculates whether the tile needs to show a clone of itself
+ // wrapped around the other side of the tile grid.
+ var offTheRight = col - indexOffset == layout.numRowTiles - 1;
+ var offTheLeft = col - indexOffset == 0;
+ if (this.dragEnters_ > 0 && (offTheRight || offTheLeft)) {
+ var sign = offTheRight ? 1 : -1;
+ tile.showDoppleganger(-layout.numRowTiles * layout.colWidth * sign,
+ layout.rowHeight * sign);
+ } else {
+ tile.clearDoppleganger();
+ }
},
/**
@@ -320,6 +390,9 @@ cr.define('ntp4', function() {
var layout = this.calculateLayoutValues_();
var col = Math.floor((x - layout.leftMargin) / layout.colWidth);
+ if (col < 0 || col >= layout.numRowTiles)
+ return -1;
+
var row = Math.floor((y - this.tileGrid_.offsetTop) / layout.rowHeight);
return row * layout.numRowTiles + col;
},
@@ -330,10 +403,10 @@ cr.define('ntp4', function() {
*/
onResize_: function(e) {
// Do nothing if the width didn't change.
- if (this.lastWidth_ == this.clientWidth)
+ if (this.maskWidth_ == this.clientWidth)
return;
- this.lastWidth_ = this.clientWidth;
+ this.updateMask_();
this.classList.add('animating-tile-page');
for (var i = 0; i < this.tileElements_.length; i++) {
@@ -342,6 +415,29 @@ cr.define('ntp4', function() {
},
/**
+ * The tile grid has an image mask which fades at the edges. This is only
+ * noticeable when doppleganger tiles are entering or exiting the grid.
+ */
+ updateMask_: function() {
+ if (this.clientWidth == this.maskWidth_)
+ return;
+ this.maskWidth_ = this.clientWidth;
+
+ var leftMargin = this.calculateLayoutValues_().leftMargin;
+ var fadeDistance = 20;
+ var gradient =
+ '-webkit-linear-gradient(left,' +
+ 'transparent, ' +
+ 'transparent ' + (leftMargin - fadeDistance) + 'px, ' +
+ 'black ' + leftMargin + 'px, ' +
+ 'black ' + (this.clientWidth - leftMargin) + 'px, ' +
+ 'transparent ' + (this.clientWidth - leftMargin + fadeDistance) +
+ 'px, ' +
+ 'transparent)';
+ this.style.WebkitMaskBoxImage = gradient;
+ },
+
+ /**
* Get the height for a tile of a certain width. Override this function to
* get non-square tiles.
* @param {number} width The pixel width of a tile.
@@ -402,6 +498,9 @@ cr.define('ntp4', function() {
* @private
*/
onDrop_: function(e) {
+ this.dragEnters_ = 0;
+ e.stopPropagation();
+
var index = this.currentDropIndex_;
if (index == this.dragItemIndex_)
return;
@@ -410,8 +509,7 @@ cr.define('ntp4', function() {
this.tileGrid_.insertBefore(
TilePage.currentlyDraggingTile,
this.tileElements_[this.currentDropIndex_ + adjustment]);
- e.stopPropagation();
- this.dragEnters_ = 0;
+ this.cleanUpDrag_();
},
/**
@@ -423,7 +521,21 @@ cr.define('ntp4', function() {
if (--this.dragEnters_ > 0)
return;
- this.updateDropIndicator_(this.dragItemIndex_);
+ this.cleanUpDrag_();
+ },
+
+ /**
+ * Makes sure all the tiles are in the right place after a drag is over.
+ * @private
+ */
+ cleanUpDrag_: function() {
+ for (var i = 0; i < this.tileElements_.length; i++) {
+ // The current drag tile will be positioned in its dragend handler.
+ if (this.tileElements_[i] == this.currentlyDraggingTile)
+ continue;
+ this.positionTile_(i);
+ }
+ this.classList.remove('animating-tile-page');
},
/**
diff --git a/chrome/test/data/webui/most_visited_page_test.js b/chrome/test/data/webui/most_visited_page_test.js
index 5d79975..f080ed6 100644
--- a/chrome/test/data/webui/most_visited_page_test.js
+++ b/chrome/test/data/webui/most_visited_page_test.js
@@ -11,6 +11,9 @@ function pageObjectArrayEquals(a, b) {
return false;
for (var i = 0; i < a.length; i++) {
+ if (!a && !b)
+ continue;
+
if (!pageObjectEquals(a[i], b[i]))
return false;
}
@@ -57,4 +60,11 @@ function refreshDataPinning() {
var mergedData = ntp4.refreshData(oldData, newData);
assertTrue(pageObjectArrayEquals(mergedData, newData));
+
+ // Tests we don't choke on empty data before a pinned entry.
+ oldData = [{url: 'foo', title: 'foo (1)'}];
+ newData = [{}, {url: 'foo', title: 'foo (2)', pinned: true}];
+
+ mergedData = ntp4.refreshData(oldData, newData);
+ assertTrue(pageObjectArrayEquals(mergedData, newData));
}