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

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

Issue 10919292: Photo Editor: better mode transitions (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 3 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
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.
11 * @constructor 11 * @constructor
12 */ 12 */
13 function MosaicMode(container, dataModel, selectionModel, 13 function MosaicMode(container, dataModel, selectionModel,
14 metadataCache, toggleMode) { 14 metadataCache, toggleMode) {
15 this.mosaic_ = new Mosaic(container.ownerDocument, 15 this.mosaic_ = new Mosaic(container.ownerDocument,
16 dataModel, selectionModel, metadataCache); 16 dataModel, selectionModel, metadataCache);
17 container.appendChild(this.mosaic_); 17 container.appendChild(this.mosaic_);
18 18
19 this.toggleMode_ = toggleMode; 19 this.toggleMode_ = toggleMode;
20 this.mosaic_.addEventListener('dblclick', this.toggleMode_); 20 this.mosaic_.addEventListener('dblclick', this.toggleMode_);
21 } 21 }
22 22
23 /** 23 /**
24 * @return {Mosaic} The mosaic control.
25 */
26 MosaicMode.prototype.getMosaic = function() { return this.mosaic_ };
27
28 /**
24 * @return {string} Mode name. 29 * @return {string} Mode name.
25 */ 30 */
26 MosaicMode.prototype.getName = function() { return 'mosaic' }; 31 MosaicMode.prototype.getName = function() { return 'mosaic' };
27 32
28 /** 33 /**
29 * Initialize the mosaic.
30 */
31 MosaicMode.prototype.init = function() { this.mosaic_.init() };
32
33 /**
34 * Enter the mosaic mode.
35 */
36 MosaicMode.prototype.enter = function() { this.mosaic_.enter() };
37
38 /**
39 * Execute an action (this mode has no busy state). 34 * Execute an action (this mode has no busy state).
40 * @param {function} action Action to execute. 35 * @param {function} action Action to execute.
41 */ 36 */
42 MosaicMode.prototype.executeWhenReady = function(action) { action() }; 37 MosaicMode.prototype.executeWhenReady = function(action) { action() };
43 38
44 /** 39 /**
45 * @return {boolean} Always true (no toolbar fading in this mode). 40 * @return {boolean} Always true (no toolbar fading in this mode).
46 */ 41 */
47 MosaicMode.prototype.hasActiveTool = function() { return true }; 42 MosaicMode.prototype.hasActiveTool = function() { return true };
48 43
49 /** 44 /**
50 * Keydown handler. 45 * Keydown handler.
51 * 46 *
52 * @param {Event} event Event. 47 * @param {Event} event Event.
53 * @return {boolean} True if processed. 48 * @return {boolean} True if processed.
54 */ 49 */
55 MosaicMode.prototype.onKeyDown = function(event) { 50 MosaicMode.prototype.onKeyDown = function(event) {
56 switch (util.getKeyModifiers(event) + event.keyIdentifier) { 51 switch (util.getKeyModifiers(event) + event.keyIdentifier) {
57 case 'Enter': 52 case 'Enter':
58 this.toggleMode_(); 53 this.toggleMode_();
59 return true; 54 return true;
60 } 55 }
61 return this.mosaic_.onKeyDown(event); 56 return this.mosaic_.onKeyDown(event);
62 }; 57 };
63 58
64 /**
65 * @return {Rect} Selected tile image rectangle. Used for animated transition
66 * to/from Slide mode.
67 */
68 MosaicMode.prototype.getSelectedTileRect = function() {
69 return this.mosaic_.getSelectedTileRect();
70 };
71
72 //////////////////////////////////////////////////////////////////////////////// 59 ////////////////////////////////////////////////////////////////////////////////
73 60
74 /** 61 /**
75 * Mosaic control. 62 * Mosaic control.
76 * 63 *
77 * @param {Document} document Document. 64 * @param {Document} document Document.
78 * @param {cr.ui.ArrayDataModel} dataModel Data model. 65 * @param {cr.ui.ArrayDataModel} dataModel Data model.
79 * @param {cr.ui.ListSelectionModel} selectionModel Selection model. 66 * @param {cr.ui.ListSelectionModel} selectionModel Selection model.
80 * @param {MetadataCache} metadataCache Metadata cache. 67 * @param {MetadataCache} metadataCache Metadata cache.
81 * @return {Element} Mosaic element. 68 * @return {Element} Mosaic element.
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after
167 }; 154 };
168 155
169 /** 156 /**
170 * @return {Mosaic.Tile} Selected tile or undefined if no selection. 157 * @return {Mosaic.Tile} Selected tile or undefined if no selection.
171 */ 158 */
172 Mosaic.prototype.getSelectedTile = function() { 159 Mosaic.prototype.getSelectedTile = function() {
173 return this.tiles_ && this.tiles_[this.selectionModel_.selectedIndex]; 160 return this.tiles_ && this.tiles_[this.selectionModel_.selectedIndex];
174 }; 161 };
175 162
176 /** 163 /**
177 * @return {Rect} Selected tile's image rectangle. 164 * @param {number} index Tile index.
165 * @return {Rect} Tile's image rectangle.
178 */ 166 */
179 Mosaic.prototype.getSelectedTileRect = function() { 167 Mosaic.prototype.getTileRect = function(index) {
180 var tile = this.getSelectedTile(); 168 var tile = this.tiles_[index];
181 return tile && tile.getImageRect(); 169 return tile && tile.getImageRect();
182 }; 170 };
183 171
184 /** 172 /**
185 * Called when showing the mosaic after the slide mode. 173 * @param {number} index Tile index.
174 * Scroll the given tile into the viewport.
186 */ 175 */
187 Mosaic.prototype.enter = function() { 176 Mosaic.prototype.scrollIntoView = function(index) {
188 var tile = this.getSelectedTile(); 177 var tile = this.tiles_[index];
189 if (tile) tile.scrollIntoView(); 178 if (tile) tile.scrollIntoView();
190 }; 179 };
191 180
192 /** 181 /**
193 * Load multiple tiles. 182 * Load multiple tiles.
194 * 183 *
195 * @param {Array.<Mosaic.Tile>} tiles Array of tiles. 184 * @param {Array.<Mosaic.Tile>} tiles Array of tiles.
196 * @param {function} opt_callback Completion callback. 185 * @param {function} opt_callback Completion callback.
197 * @private 186 * @private
198 */ 187 */
(...skipping 195 matching lines...) Expand 10 before | Expand all | Expand 10 after
394 * @param {Event} event Event. 383 * @param {Event} event Event.
395 * @return {boolean} True if the event has been consumed. 384 * @return {boolean} True if the event has been consumed.
396 */ 385 */
397 Mosaic.prototype.onKeyDown = function(event) { 386 Mosaic.prototype.onKeyDown = function(event) {
398 this.selectionController_.handleKeyDown(event); 387 this.selectionController_.handleKeyDown(event);
399 if (event.defaultPrevented) // Navigating with keyboard, hide hover state. 388 if (event.defaultPrevented) // Navigating with keyboard, hide hover state.
400 this.classList.remove('hover-visible'); 389 this.classList.remove('hover-visible');
401 return event.defaultPrevented; 390 return event.defaultPrevented;
402 }; 391 };
403 392
393 /**
394 * @return {boolean} True if the mosaic zoom effect can be applied. It is
395 * too slow if there are to many images.
396 * TODO(kaznacheev): Consider unloading the images that are out of the viewport.
397 */
398 Mosaic.prototype.canZoom = function() {
399 return this.layoutModel_.getTileCount() < 100;
400 };
401
402 /**
403 * Show the mosaic.
404 */
405 Mosaic.prototype.show = function() {
406 var duration = ImageView.ZOOM_ANIMATION_DURATION;
407 if (this.canZoom()) {
408 // Fade in in parallel with the zoom effect.
409 this.setAttribute('visible', 'zooming');
410 } else {
411 // Mosaic is not animating but the large image is. Fade in the mosaic
412 // shortly before the large image animation is done.
413 duration -= 100;
414 }
415 setTimeout(function() {
416 // Make the selection visible.
417 // If the mosaic is not animated it will start fading in now.
418 this.setAttribute('visible', 'normal');
419 }.bind(this), duration);
420
421 };
422
423 /**
424 * Hide the mosaic.
425 */
426 Mosaic.prototype.hide = function() {
427 this.removeAttribute('visible');
428 };
429
430 /**
431 * Apply or reset the zoom transform.
432 *
433 * @param {Rect} tileRect Tile rectangle. Reset the transform if null.
434 * @param {Rect} imageRect Large image rectangle. Reset the transform if null.
435 * @param {boolean} opt_instant True of the transition should be instant.
436 */
437 Mosaic.prototype.transform = function(tileRect, imageRect, opt_instant) {
438 if (opt_instant)
dgozman 2012/09/14 15:17:38 braces must be present when else clause is present
439 this.style.webkitTransitionDuration = '0';
440 else
441 this.style.webkitTransitionDuration =
442 ImageView.ZOOM_ANIMATION_DURATION + 'ms';
443
444 if (this.canZoom() && tileRect && imageRect) {
445 var scaleX = imageRect.width / tileRect.width;
446 var scaleY = imageRect.height / tileRect.height;
447 var shiftX = (imageRect.left + imageRect.width / 2) -
448 (tileRect.left + tileRect.width / 2);
449 var shiftY = (imageRect.top + imageRect.height / 2) -
450 (tileRect.top + tileRect.height / 2);
451 this.style.webkitTransform =
452 'translate(' + shiftX * scaleX + 'px, ' + shiftY * scaleY + 'px)' +
453 'scaleX(' + scaleX + ') scaleY(' + scaleY + ')';
454 } else {
455 this.style.webkitTransform = '';
456 }
457 };
458
404 //////////////////////////////////////////////////////////////////////////////// 459 ////////////////////////////////////////////////////////////////////////////////
405 460
406 /** 461 /**
407 * Creates a selection controller that is to be used with grid. 462 * Creates a selection controller that is to be used with grid.
408 * @param {cr.ui.ListSelectionModel} selectionModel The selection model to 463 * @param {cr.ui.ListSelectionModel} selectionModel The selection model to
409 * interact with. 464 * interact with.
410 * @param {Mosaic.Layout} layoutModel The layout model to use. 465 * @param {Mosaic.Layout} layoutModel The layout model to use.
411 * @constructor 466 * @constructor
412 * @extends {!cr.ui.ListSelectionController} 467 * @extends {!cr.ui.ListSelectionController}
413 */ 468 */
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
445 * @param {Element} container Container element. 500 * @param {Element} container Container element.
446 * @constructor 501 * @constructor
447 */ 502 */
448 Mosaic.Layout = function(container) { 503 Mosaic.Layout = function(container) {
449 this.container_ = container; 504 this.container_ = container;
450 this.columns_ = []; 505 this.columns_ = [];
451 this.newColumn_ = null; 506 this.newColumn_ = null;
452 }; 507 };
453 508
454 /** 509 /**
455 * Blank space at the top and the bottom of the mosaic element. 510 * Blank space at the top of the mosaic element. We do not do that is CSS
511 * because we want the mosaic to occupy the entire parent div which makes
512 * animated transitions easier.
456 */ 513 */
457 Mosaic.Layout.PADDING = 50; 514 Mosaic.Layout.PADDING_TOP = 50;
515
516 /**
517 * Blank space at the bottom of the mosaic element.
518 */
519 Mosaic.Layout.PADDING_BOTTOM = 70;
458 520
459 /** 521 /**
460 * Horizontal and vertical spacing between images. Should be kept in sync 522 * Horizontal and vertical spacing between images. Should be kept in sync
461 * with the style of .mosaic-item in gallery.css (= 2 * ( 4 + 1)) 523 * with the style of .mosaic-item in gallery.css (= 2 * ( 4 + 1))
462 */ 524 */
463 Mosaic.Layout.SPACING = 10; 525 Mosaic.Layout.SPACING = 10;
464 526
465 /** 527 /**
466 * @return {number} Total number of tiles added to the layout. 528 * @return {number} Total number of tiles added to the layout.
467 */ 529 */
(...skipping 13 matching lines...) Expand all
481 /** 543 /**
482 * Add a tile to the layout. 544 * Add a tile to the layout.
483 * 545 *
484 * @param {Mosaic.Tile} tile The tile to be added. 546 * @param {Mosaic.Tile} tile The tile to be added.
485 * @param {boolean} isLast True if this tile is the last. 547 * @param {boolean} isLast True if this tile is the last.
486 */ 548 */
487 Mosaic.Layout.prototype.add = function(tile, isLast) { 549 Mosaic.Layout.prototype.add = function(tile, isLast) {
488 var layoutQueue = [tile]; 550 var layoutQueue = [tile];
489 var tilesPerRow = Mosaic.Row.TILES_PER_ROW; 551 var tilesPerRow = Mosaic.Row.TILES_PER_ROW;
490 552
491 var layoutHeight = this.container_.clientHeight - Mosaic.Layout.PADDING * 2; 553 var layoutHeight = this.container_.clientHeight -
554 (Mosaic.Layout.PADDING_TOP + Mosaic.Layout.PADDING_BOTTOM);
492 555
493 while (layoutQueue.length) { 556 while (layoutQueue.length) {
494 if (!this.newColumn_) { 557 if (!this.newColumn_) {
495 this.newColumn_ = new Mosaic.Column( 558 this.newColumn_ = new Mosaic.Column(
496 this.columns_.length, this.getTileCount(), layoutHeight, tilesPerRow); 559 this.columns_.length, this.getTileCount(), layoutHeight, tilesPerRow);
497 tilesPerRow = Mosaic.Row.TILES_PER_ROW; // Reset the default. 560 tilesPerRow = Mosaic.Row.TILES_PER_ROW; // Reset the default.
498 } 561 }
499 562
500 this.newColumn_.add(layoutQueue.shift()); 563 this.newColumn_.add(layoutQueue.shift());
501 564
502 if (this.newColumn_.prepareLayout(isLast && !layoutQueue.length)) { 565 if (this.newColumn_.prepareLayout(isLast && !layoutQueue.length)) {
503 if (this.newColumn_.isSuboptimal()) { 566 if (this.newColumn_.isSuboptimal()) {
504 // Forget the new column, put its tiles back into the layout queue. 567 // Forget the new column, put its tiles back into the layout queue.
505 layoutQueue = this.newColumn_.getTiles().concat(layoutQueue); 568 layoutQueue = this.newColumn_.getTiles().concat(layoutQueue);
506 tilesPerRow = 1; // Force 1 tile per row for the next column. 569 tilesPerRow = 1; // Force 1 tile per row for the next column.
507 } else { 570 } else {
508 var lastColumn = this.columns_[this.columns_.length - 1]; 571 var lastColumn = this.columns_[this.columns_.length - 1];
509 this.newColumn_.layout(lastColumn ? lastColumn.getRightEdge() : 0, 572 this.newColumn_.layout(lastColumn ? lastColumn.getRightEdge() : 0,
510 Mosaic.Layout.PADDING); 573 Mosaic.Layout.PADDING_TOP);
511 this.columns_.push(this.newColumn_); 574 this.columns_.push(this.newColumn_);
512 } 575 }
513 this.newColumn_ = null; 576 this.newColumn_ = null;
514 } 577 }
515 } 578 }
516 }; 579 };
517 580
518 /** 581 /**
519 * Backtrack the layout to the beginning of the column containing a tile with 582 * Backtrack the layout to the beginning of the column containing a tile with
520 * a given index. 583 * a given index.
(...skipping 609 matching lines...) Expand 10 before | Expand all | Expand 10 after
1130 * relative to the viewport. 1193 * relative to the viewport.
1131 */ 1194 */
1132 Mosaic.Tile.prototype.getImageRect = function() { 1195 Mosaic.Tile.prototype.getImageRect = function() {
1133 if (this.left_ == null) // Not laid out. 1196 if (this.left_ == null) // Not laid out.
1134 return null; 1197 return null;
1135 1198
1136 var margin = Mosaic.Layout.SPACING / 2; 1199 var margin = Mosaic.Layout.SPACING / 2;
1137 return new Rect(this.left_ - this.container_.scrollLeft, this.top_, 1200 return new Rect(this.left_ - this.container_.scrollLeft, this.top_,
1138 this.width_, this.height_).inflate(-margin, -margin); 1201 this.width_, this.height_).inflate(-margin, -margin);
1139 }; 1202 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698