diff options
author | estade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-05-03 14:55:58 +0000 |
---|---|---|
committer | estade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-05-03 14:55:58 +0000 |
commit | c40520ee3eb467320be787111bdcb180fe27dbdd (patch) | |
tree | 98b188668fbc5a08ea5bb972864e49ec48f1dfd6 /chrome | |
parent | 59744fee620a78ef5fc80f26a0d88007ca245a23 (diff) | |
download | chromium_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.js | 10 | ||||
-rw-r--r-- | chrome/browser/resources/ntp4/tile_page.css | 8 | ||||
-rw-r--r-- | chrome/browser/resources/ntp4/tile_page.js | 132 | ||||
-rw-r--r-- | chrome/test/data/webui/most_visited_page_test.js | 10 |
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)); } |