Commit b989df73 authored by hirono@chromium.org's avatar hirono@chromium.org

Gallery: Add the offset feature in the slide mode.

 * Add the keyboard handler to modify the viewport's offset values.

 * Make the viewport class generate CSS transform expressions including the
   offset value.

BUG=245926
TEST=manually
R=mtomasz@chromium.org

Review URL: https://codereview.chromium.org/398283002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@284033 0039d316-1c4b-4281-b951-d872f2087c98
parent d460a7bc
......@@ -493,8 +493,6 @@ ImageView.prototype.replaceContent_ = function(
this.viewport_.setImageSize(
opt_width || this.contentCanvas_.width,
opt_height || this.contentCanvas_.height);
this.viewport_.fitImage();
this.viewport_.update();
this.draw();
this.container_.appendChild(this.screenImage_);
......@@ -846,7 +844,7 @@ ImageView.Effect.prototype.transform = function(element, viewport) {
* @extends {ImageView.Effect}
*/
ImageView.Effect.None = function() {
ImageView.Effect.call(this, 0);
ImageView.Effect.call(this, 0, 'easy-out');
};
/**
......@@ -873,7 +871,7 @@ ImageView.Effect.None.prototype.transform = function(element, viewport) {
*/
ImageView.Effect.Slide = function Slide(direction, opt_slow) {
ImageView.Effect.call(this,
opt_slow ? 800 : ImageView.Effect.DEFAULT_DURATION, 'ease-in-out');
opt_slow ? 800 : ImageView.Effect.DEFAULT_DURATION, 'ease-out');
this.direction_ = direction;
this.slow_ = opt_slow;
this.shift_ = opt_slow ? 100 : 40;
......@@ -913,7 +911,7 @@ ImageView.Effect.Slide.prototype.transform = function(element, viewport) {
ImageView.Effect.Zoom = function(
previousImageWidth, previousImageHeight, imageCropRect, opt_duration) {
ImageView.Effect.call(this,
opt_duration || ImageView.Effect.DEFAULT_DURATION);
opt_duration || ImageView.Effect.DEFAULT_DURATION, 'ease-out');
this.previousImageWidth_ = previousImageWidth;
this.previousImageHeight_ = previousImageHeight;
this.imageCropRect_ = imageCropRect;
......
......@@ -86,7 +86,7 @@ function Viewport() {
*/
this.generation_ = 0;
this.update();
this.update_();
Object.seal(this);
}
......@@ -112,8 +112,7 @@ Viewport.ZOOM_RATIOS = Object.freeze({
*/
Viewport.prototype.setImageSize = function(width, height) {
this.imageBounds_ = new Rect(width, height);
this.update();
this.invalidateCaches();
this.update_();
};
/**
......@@ -122,8 +121,7 @@ Viewport.prototype.setImageSize = function(width, height) {
*/
Viewport.prototype.setScreenSize = function(width, height) {
this.screenBounds_ = new Rect(width, height);
this.update();
this.invalidateCaches();
this.update_();
};
/**
......@@ -136,7 +134,7 @@ Viewport.prototype.setZoomIndex = function(zoomIndex) {
return;
this.zoomIndex_ = zoomIndex;
this.zoom_ = Viewport.ZOOM_RATIOS[zoomIndex];
this.update();
this.update_();
};
/**
......@@ -148,27 +146,11 @@ Viewport.prototype.getZoomIndex = function() {
};
/**
* @return {number} Scale.
* Returns the value of zoom.
* @return {number} Zoom value.
*/
Viewport.prototype.getScale = function() { return this.scale_; };
/**
* @param {number} scale The new scale.
*/
Viewport.prototype.setScale = function(scale) {
if (this.scale_ == scale)
return;
this.scale_ = scale;
this.update();
this.invalidateCaches();
};
/**
* @return {number} Best scale to fit the current image into the current screen.
*/
Viewport.prototype.getFittingScale = function() {
return this.getFittingScaleForImageSize_(
this.imageBounds_.width, this.imageBounds_.height);
Viewport.prototype.getZoom = function() {
return this.zoomIndex_;
};
/**
......@@ -187,13 +169,6 @@ Viewport.prototype.getFittingScaleForImageSize_ = function(width, height) {
return Math.min(1 / window.devicePixelRatio, scaleX, scaleY);
};
/**
* Set the scale to fit the image into the screen.
*/
Viewport.prototype.fitImage = function() {
this.setScale(this.getFittingScale());
};
/**
* @return {number} X-offset of the viewport.
*/
......@@ -208,17 +183,13 @@ Viewport.prototype.getOffsetY = function() { return this.offsetY_; };
* Set the image offset in the viewport.
* @param {number} x X-offset.
* @param {number} y Y-offset.
* @param {boolean} ignoreClipping True if no clipping should be applied.
*/
Viewport.prototype.setOffset = function(x, y, ignoreClipping) {
if (!ignoreClipping) {
x = this.clampOffsetX_(x);
y = this.clampOffsetY_(y);
}
if (this.offsetX_ == x && this.offsetY_ == y) return;
Viewport.prototype.setOffset = function(x, y) {
if (this.offsetX_ == x && this.offsetY_ == y)
return;
this.offsetX_ = x;
this.offsetY_ = y;
this.invalidateCaches();
this.update_();
};
/**
......@@ -249,11 +220,6 @@ Viewport.prototype.getDeviceBounds = function() {
*/
Viewport.prototype.getCacheGeneration = function() { return this.generation_; };
/**
* Called on event view port state change.
*/
Viewport.prototype.invalidateCaches = function() { this.generation_++; };
/**
* @return {Rect} The image bounds in screen coordinates.
*/
......@@ -283,7 +249,7 @@ Viewport.prototype.getImageBoundsOnScreenClipped = function() {
* @return {number} Size in image coordinates.
*/
Viewport.prototype.screenToImageSize = function(size) {
return size / this.getScale();
return size / this.scale_;
};
/**
......@@ -291,7 +257,7 @@ Viewport.prototype.screenToImageSize = function(size) {
* @return {number} X in image coordinates.
*/
Viewport.prototype.screenToImageX = function(x) {
return Math.round((x - this.imageBoundsOnScreen_.left) / this.getScale());
return Math.round((x - this.imageBoundsOnScreen_.left) / this.scale_);
};
/**
......@@ -299,7 +265,7 @@ Viewport.prototype.screenToImageX = function(x) {
* @return {number} Y in image coordinates.
*/
Viewport.prototype.screenToImageY = function(y) {
return Math.round((y - this.imageBoundsOnScreen_.top) / this.getScale());
return Math.round((y - this.imageBoundsOnScreen_.top) / this.scale_);
};
/**
......@@ -319,7 +285,7 @@ Viewport.prototype.screenToImageRect = function(rect) {
* @return {number} Size in screen coordinates.
*/
Viewport.prototype.imageToScreenSize = function(size) {
return size * this.getScale();
return size * this.scale_;
};
/**
......@@ -327,7 +293,7 @@ Viewport.prototype.imageToScreenSize = function(size) {
* @return {number} X in screen coordinates.
*/
Viewport.prototype.imageToScreenX = function(x) {
return Math.round(this.imageBoundsOnScreen_.left + x * this.getScale());
return Math.round(this.imageBoundsOnScreen_.left + x * this.scale_);
};
/**
......@@ -335,7 +301,7 @@ Viewport.prototype.imageToScreenX = function(x) {
* @return {number} Y in screen coordinates.
*/
Viewport.prototype.imageToScreenY = function(y) {
return Math.round(this.imageBoundsOnScreen_.top + y * this.getScale());
return Math.round(this.imageBoundsOnScreen_.top + y * this.scale_);
};
/**
......@@ -383,7 +349,7 @@ Viewport.prototype.getMarginY_ = function() {
* @private
*/
Viewport.prototype.clampOffsetX_ = function(x) {
var limit = Math.round(Math.max(0, -this.getMarginX_() / this.getScale()));
var limit = Math.round(Math.max(0, -this.getMarginX_() / this.scale_));
return ImageUtil.clamp(-limit, x, limit);
};
......@@ -393,7 +359,7 @@ Viewport.prototype.clampOffsetX_ = function(x) {
* @private
*/
Viewport.prototype.clampOffsetY_ = function(y) {
var limit = Math.round(Math.max(0, -this.getMarginY_() / this.getScale()));
var limit = Math.round(Math.max(0, -this.getMarginY_() / this.scale_));
return ImageUtil.clamp(-limit, y, limit);
};
......@@ -409,25 +375,50 @@ Viewport.prototype.getCenteredRect_ = function(
height);
};
/**
* Resets zoom and offset.
*/
Viewport.prototype.resetView = function() {
this.zoomIndex_ = 0;
this.zoom_ = 1;
this.offsetX_ = 0;
this.offsetY_ = 0;
this.update_();
};
/**
* Recalculate the viewport parameters.
* @private
*/
Viewport.prototype.update = function() {
var scale = this.getScale();
Viewport.prototype.update_ = function() {
// Update scale.
this.scale_ = this.getFittingScaleForImageSize_(
this.imageBounds_.width, this.imageBounds_.height);
// Limit offset values.
var zoomedWidht = ~~(this.imageBounds_.width * this.scale_ * this.zoom_);
var zoomedHeight = ~~(this.imageBounds_.height * this.scale_ * this.zoom_);
var dx = Math.max(zoomedWidht - this.screenBounds_.width, 0) / 2;
var dy = Math.max(zoomedHeight - this.screenBounds_.height, 0) /2;
this.offsetX_ = ImageUtil.clamp(-dx, this.offsetX_, dx);
this.offsetY_ = ImageUtil.clamp(-dy, this.offsetY_, dy);
// Image bounds on screen.
this.imageBoundsOnScreen_ = this.getCenteredRect_(
~~(this.imageBounds_.width * scale * this.zoom_),
~~(this.imageBounds_.height * scale * this.zoom_),
this.offsetX_,
this.offsetY_);
zoomedWidht, zoomedHeight, this.offsetX_, this.offsetY_);
// Image bounds of element (that is not applied zoom and offset) on screen.
var oldBounds = this.imageElementBoundsOnScreen_;
this.imageElementBoundsOnScreen_ = this.getCenteredRect_(
~~(this.imageBounds_.width * scale),
~~(this.imageBounds_.height * scale),
~~(this.imageBounds_.width * this.scale_),
~~(this.imageBounds_.height * this.scale_),
0,
0);
if (!oldBounds ||
this.imageElementBoundsOnScreen_.width != oldBounds.width ||
this.imageElementBoundsOnScreen_.height != oldBounds.height) {
this.generation_++;
}
// Image bounds on screen cliped with the screen bounds.
var left = Math.max(this.imageBoundsOnScreen_.left, 0);
......@@ -445,7 +436,8 @@ Viewport.prototype.update = function() {
* @return {string} Transformation description.
*/
Viewport.prototype.getTransformation = function() {
return 'scale(' + (1 / window.devicePixelRatio * this.zoom_) + ')';
return 'translate(' + this.offsetX_ + 'px, ' + this.offsetY_ + 'px) ' +
'scale(' + (1 / window.devicePixelRatio * this.zoom_) + ')';
};
/**
......@@ -472,7 +464,7 @@ Viewport.prototype.getInverseTransformForRotatedImage = function(orientation) {
var previousImageHeight = this.imageBounds_.width;
var oldScale = this.getFittingScaleForImageSize_(
previousImageWidth, previousImageHeight);
var scaleRatio = oldScale / this.getScale();
var scaleRatio = oldScale / this.scale_;
var degree = orientation ? '-90deg' : '90deg';
return [
'scale(' + scaleRatio + ')',
......
......@@ -48,16 +48,12 @@ function SlideMode(container, content, toolbar, prompt,
this.initDom_();
}
/**
* SlideMode extends cr.EventTarget.
*/
SlideMode.prototype.__proto__ = cr.EventTarget.prototype;
/**
* List of available editor modes.
* @type {Array.<ImageEditor.Mode>}
* @const
*/
SlideMode.editorModes = [
SlideMode.EDITOR_MODES = Object.freeze([
new ImageEditor.Mode.InstantAutofix(),
new ImageEditor.Mode.Crop(),
new ImageEditor.Mode.Exposure(),
......@@ -65,7 +61,24 @@ SlideMode.editorModes = [
'rotate_left', 'GALLERY_ROTATE_LEFT', new Command.Rotate(-1)),
new ImageEditor.Mode.OneClick(
'rotate_right', 'GALLERY_ROTATE_RIGHT', new Command.Rotate(1))
];
]);
/**
* Map of the key identifier and offset delta.
* @type {Object.<string, Array.<number>})
* @const
*/
SlideMode.KEY_OFFSET_MAP = Object.freeze({
'Up': Object.freeze([0, 20]),
'Down': Object.freeze([0, -20]),
'Left': Object.freeze([20, 0]),
'Right': Object.freeze([-20, 0])
});
/**
* SlideMode extends cr.EventTarget.
*/
SlideMode.prototype.__proto__ = cr.EventTarget.prototype;
/**
* @return {string} Mode name.
......@@ -216,7 +229,7 @@ SlideMode.prototype.initDom_ = function() {
toolbar: this.editBarMain_,
mode: this.editBarModeWrapper_
},
SlideMode.editorModes,
SlideMode.EDITOR_MODES,
this.displayStringFunction_,
this.onToolsVisibilityChanged_.bind(this));
......@@ -334,7 +347,7 @@ SlideMode.prototype.leave = function(zoomToRect, callback) {
callback();
}.bind(this);
this.viewport_.setZoomIndex(0);
this.viewport_.resetView();
if (this.getItemCount_() === 0) {
this.showErrorBanner_(false);
commitDone();
......@@ -846,9 +859,14 @@ SlideMode.prototype.onKeyDown = function(event) {
break;
case 'U+001B': // Escape
if (!this.isEditing())
if (this.isEditing()) {
this.toggleEditor(event);
} else if (this.viewport_.getZoomIndex() !== 0) {
this.viewport_.resetView();
this.imageView_.applyViewportChange();
} else {
return false; // Not handled.
this.toggleEditor(event);
}
break;
case 'Home':
......@@ -861,6 +879,19 @@ SlideMode.prototype.onKeyDown = function(event) {
case 'Down':
case 'Left':
case 'Right':
if (!this.isEditing() && this.viewport_.getZoomIndex() !== 0) {
var delta = SlideMode.KEY_OFFSET_MAP[keyID];
this.viewport_.setOffset(
~~(this.viewport_.getOffsetX() +
delta[0] * this.viewport_.getZoom()),
~~(this.viewport_.getOffsetY() +
delta[1] * this.viewport_.getZoom()),
true);
this.imageView_.applyViewportChange();
} else {
this.advanceWithKeyboard(keyID);
}
break;
case 'MediaNextTrack':
case 'MediaPreviousTrack':
this.advanceWithKeyboard(keyID);
......@@ -879,6 +910,13 @@ SlideMode.prototype.onKeyDown = function(event) {
this.imageView_.applyViewportChange();
}
break;
case 'Ctrl-U+0030': // Ctrl+'0' zoom reset.
if (!this.isEditing()) {
this.viewport_.resetView();
this.imageView_.applyViewportChange();
}
break;
}
return true;
......@@ -1046,7 +1084,7 @@ SlideMode.prototype.isSlideshowOn_ = function() {
*/
SlideMode.prototype.startSlideshow = function(opt_interval, opt_event) {
// Reset zoom.
this.viewport_.setZoomIndex(0);
this.viewport_.resetView();
this.imageView_.applyViewportChange();
// Set the attribute early to prevent the toolbar from flashing when
......@@ -1201,7 +1239,7 @@ SlideMode.prototype.toggleEditor = function(opt_event) {
if (this.isEditing()) { // isEditing has just been flipped to a new value.
// Reset zoom.
this.viewport_.setZoomIndex(0);
this.viewport_.resetView();
this.imageView_.applyViewportChange();
if (this.context_.readonlyDirName) {
this.editor_.getPrompt().showAt(
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment