1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
6 * The overlay displaying the image.
8 * @param {HTMLElement} container The container element.
9 * @param {Viewport} viewport The viewport.
11 * @extends {ImageBuffer.Overlay}
13 function ImageView(container
, viewport
) {
14 ImageBuffer
.Overlay
.call(this);
16 this.container_
= container
;
17 this.viewport_
= viewport
;
18 this.document_
= container
.ownerDocument
;
19 this.contentGeneration_
= 0;
20 this.displayedContentGeneration_
= 0;
22 this.imageLoader_
= new ImageUtil
.ImageLoader(this.document_
);
23 // We have a separate image loader for prefetch which does not get cancelled
24 // when the selection changes.
25 this.prefetchLoader_
= new ImageUtil
.ImageLoader(this.document_
);
27 this.contentCallbacks_
= [];
30 * The element displaying the current content.
32 * @type {HTMLCanvasElement}
35 this.screenImage_
= null;
39 * Duration of transition between modes in ms.
41 ImageView
.MODE_TRANSITION_DURATION
= 350;
44 * If the user flips though images faster than this interval we do not apply
45 * the slide-in/slide-out transition.
47 ImageView
.FAST_SCROLL_INTERVAL
= 300;
50 * Image load type: full resolution image loaded from cache.
52 ImageView
.LOAD_TYPE_CACHED_FULL
= 0;
55 * Image load type: screen resolution preview loaded from cache.
57 ImageView
.LOAD_TYPE_CACHED_SCREEN
= 1;
60 * Image load type: image read from file.
62 ImageView
.LOAD_TYPE_IMAGE_FILE
= 2;
65 * Image load type: error occurred.
67 ImageView
.LOAD_TYPE_ERROR
= 3;
70 * Image load type: the file contents is not available offline.
72 ImageView
.LOAD_TYPE_OFFLINE
= 4;
75 * The total number of load types.
77 ImageView
.LOAD_TYPE_TOTAL
= 5;
79 ImageView
.prototype = {__proto__
: ImageBuffer
.Overlay
.prototype};
84 ImageView
.prototype.getZIndex = function() { return -1; };
89 ImageView
.prototype.draw = function() {
90 if (!this.contentCanvas_
) // Do nothing if the image content is not set.
92 if (this.setupDeviceBuffer(this.screenImage_
) ||
93 this.displayedContentGeneration_
!== this.contentGeneration_
) {
94 this.displayedContentGeneration_
= this.contentGeneration_
;
95 ImageUtil
.trace
.resetTimer('paint');
97 this.contentCanvas_
, new ImageRect(this.contentCanvas_
));
98 ImageUtil
.trace
.reportTimer('paint');
103 * Applies the viewport change that does not affect the screen cache size (zoom
104 * change or offset change) with animation.
106 ImageView
.prototype.applyViewportChange = function() {
107 if (this.screenImage_
) {
111 new ImageView
.Effect
.None(),
112 ImageView
.Effect
.DEFAULT_DURATION
);
117 * @return {number} The cache generation.
119 ImageView
.prototype.getCacheGeneration = function() {
120 return this.contentGeneration_
;
124 * Invalidates the caches to force redrawing the screen canvas.
126 ImageView
.prototype.invalidateCaches = function() {
127 this.contentGeneration_
++;
131 * @return {HTMLCanvasElement} The content canvas element.
133 ImageView
.prototype.getCanvas = function() { return this.contentCanvas_
; };
136 * @return {boolean} True if the a valid image is currently loaded.
138 ImageView
.prototype.hasValidImage = function() {
139 return !this.preview_
&& this.contentCanvas_
&& this.contentCanvas_
.width
;
143 * @return {HTMLCanvasElement} The cached thumbnail image.
145 ImageView
.prototype.getThumbnail = function() { return this.thumbnailCanvas_
; };
148 * @return {number} The content revision number.
150 ImageView
.prototype.getContentRevision = function() {
151 return this.contentRevision_
;
155 * Copies an image fragment from a full resolution canvas to a device resolution
158 * @param {HTMLCanvasElement} canvas Canvas containing whole image. The canvas
159 * may not be full resolution (scaled).
160 * @param {ImageRect} imageRect Rectangle region of the canvas to be rendered.
162 ImageView
.prototype.paintDeviceRect = function(canvas
, imageRect
) {
163 // Map the rectangle in full resolution image to the rectangle in the device
165 var deviceBounds
= this.viewport_
.getDeviceBounds();
166 var scaleX
= deviceBounds
.width
/ canvas
.width
;
167 var scaleY
= deviceBounds
.height
/ canvas
.height
;
168 var deviceRect
= new ImageRect(
169 imageRect
.left
* scaleX
,
170 imageRect
.top
* scaleY
,
171 imageRect
.width
* scaleX
,
172 imageRect
.height
* scaleY
);
175 this.screenImage_
.getContext('2d'), canvas
, deviceRect
, imageRect
);
179 * Creates an overlay canvas with properties similar to the screen canvas.
180 * Useful for showing quick feedback when editing.
182 * @return {HTMLCanvasElement} Overlay canvas.
184 ImageView
.prototype.createOverlayCanvas = function() {
185 var canvas
= this.document_
.createElement('canvas');
186 canvas
.className
= 'image';
187 this.container_
.appendChild(canvas
);
192 * Sets up the canvas as a buffer in the device resolution.
194 * @param {HTMLCanvasElement} canvas The buffer canvas.
195 * @return {boolean} True if the canvas needs to be rendered.
197 ImageView
.prototype.setupDeviceBuffer = function(canvas
) {
198 // Set the canvas position and size in device pixels.
199 var deviceRect
= this.viewport_
.getDeviceBounds();
200 var needRepaint
= false;
201 if (canvas
.width
!== deviceRect
.width
) {
202 canvas
.width
= deviceRect
.width
;
205 if (canvas
.height
!== deviceRect
.height
) {
206 canvas
.height
= deviceRect
.height
;
211 var imageBounds
= this.viewport_
.getImageElementBoundsOnScreen();
212 canvas
.style
.left
= imageBounds
.left
+ 'px';
213 canvas
.style
.top
= imageBounds
.top
+ 'px';
214 canvas
.style
.width
= imageBounds
.width
+ 'px';
215 canvas
.style
.height
= imageBounds
.height
+ 'px';
217 this.setTransform_(canvas
, this.viewport_
);
223 * @return {ImageData} A new ImageData object with a copy of the content.
225 ImageView
.prototype.copyScreenImageData = function() {
226 return this.screenImage_
.getContext('2d').getImageData(
227 0, 0, this.screenImage_
.width
, this.screenImage_
.height
);
231 * @return {boolean} True if the image is currently being loaded.
233 ImageView
.prototype.isLoading = function() {
234 return this.imageLoader_
.isBusy();
238 * Cancels the current image loading operation. The callbacks will be ignored.
240 ImageView
.prototype.cancelLoad = function() {
241 this.imageLoader_
.cancel();
245 * Loads and display a new image.
247 * Loads the thumbnail first, then replaces it with the main image.
248 * Takes into account the image orientation encoded in the metadata.
250 * @param {Gallery.Item} item Gallery item to be loaded.
251 * @param {Object} effect Transition effect object.
252 * @param {function(number)} displayCallback Called when the image is displayed
253 * (possibly as a preview).
254 * @param {function(number, number, *=)} loadCallback Called when the image is
255 * fully loaded. The first parameter is the load type.
257 ImageView
.prototype.load
=
258 function(item
, effect
, displayCallback
, loadCallback
) {
259 var entry
= item
.getEntry();
260 var metadata
= item
.getMetadata() || {};
263 // Skip effects when reloading repeatedly very quickly.
264 var time
= Date
.now();
265 if (this.lastLoadTime_
&&
266 (time
- this.lastLoadTime_
) < ImageView
.FAST_SCROLL_INTERVAL
) {
269 this.lastLoadTime_
= time
;
272 ImageUtil
.metrics
.startInterval(ImageUtil
.getMetricName('DisplayTime'));
276 this.contentItem_
= item
;
277 this.contentRevision_
= -1;
279 var cached
= item
.contentImage
;
281 displayMainImage(ImageView
.LOAD_TYPE_CACHED_FULL
,
282 false /* no preview */, cached
);
284 var cachedScreen
= item
.screenImage
;
285 var imageWidth
= metadata
.media
&& metadata
.media
.width
||
286 metadata
.external
&& metadata
.external
.imageWidth
;
287 var imageHeight
= metadata
.media
&& metadata
.media
.height
||
288 metadata
.external
&& metadata
.external
.imageHeight
;
290 // We have a cached screen-scale canvas, use it instead of a thumbnail.
291 displayThumbnail(ImageView
.LOAD_TYPE_CACHED_SCREEN
, cachedScreen
);
292 // As far as the user can tell the image is loaded. We still need to load
293 // the full res image to make editing possible, but we can report now.
294 ImageUtil
.metrics
.recordInterval(ImageUtil
.getMetricName('DisplayTime'));
295 } else if ((effect
&& effect
.constructor.name
=== 'Slide') &&
296 (metadata
.thumbnail
&& metadata
.thumbnail
.url
)) {
297 // Only show thumbnails if there is no effect or the effect is Slide.
298 // Also no thumbnail if the image is too large to be loaded.
299 var thumbnailLoader
= new ThumbnailLoader(
301 ThumbnailLoader
.LoaderType
.CANVAS
,
303 thumbnailLoader
.loadDetachedImage(function(success
) {
304 displayThumbnail(ImageView
.LOAD_TYPE_IMAGE_FILE
,
305 success
? thumbnailLoader
.getImage() : null);
308 loadMainImage(ImageView
.LOAD_TYPE_IMAGE_FILE
, entry
,
309 false /* no preview*/, 0 /* delay */);
313 function displayThumbnail(loadType
, canvas
) {
317 if (metadata
.media
) {
318 width
= metadata
.media
.width
;
319 height
= metadata
.media
.height
;
321 // If metadata.external.present is true, the image data is loaded directly
322 // from local cache, whose size may be out of sync with the drive
324 if (metadata
.external
&& !metadata
.external
.present
) {
325 width
= metadata
.external
.imageWidth
;
326 height
= metadata
.external
.imageHeight
;
334 if (displayCallback
) displayCallback();
336 loadMainImage(loadType
, entry
, !!canvas
,
337 (effect
&& canvas
) ? effect
.getSafeInterval() : 0);
340 function loadMainImage(loadType
, contentEntry
, previewShown
, delay
) {
341 if (self
.prefetchLoader_
.isLoading(contentEntry
)) {
342 // The image we need is already being prefetched. Initiating another load
343 // would be a waste. Hijack the load instead by overriding the callback.
344 self
.prefetchLoader_
.setCallback(
345 displayMainImage
.bind(null, loadType
, previewShown
));
347 // Swap the loaders so that the self.isLoading works correctly.
348 var temp
= self
.prefetchLoader_
;
349 self
.prefetchLoader_
= self
.imageLoader_
;
350 self
.imageLoader_
= temp
;
353 self
.prefetchLoader_
.cancel(); // The prefetch was doing something useless.
355 self
.imageLoader_
.load(
357 displayMainImage
.bind(null, loadType
, previewShown
),
361 function displayMainImage(loadType
, previewShown
, content
, opt_error
) {
363 loadType
= ImageView
.LOAD_TYPE_ERROR
;
365 // If we already displayed the preview we should not replace the content if
366 // the full content failed to load.
367 var animationDuration
= 0;
368 if (!(previewShown
&& loadType
=== ImageView
.LOAD_TYPE_ERROR
)) {
369 var replaceEffect
= previewShown
? null : effect
;
370 animationDuration
= replaceEffect
? replaceEffect
.getSafeInterval() : 0;
371 self
.replace(content
, replaceEffect
);
372 if (!previewShown
&& displayCallback
) displayCallback();
375 if (loadType
!== ImageView
.LOAD_TYPE_ERROR
&&
376 loadType
!== ImageView
.LOAD_TYPE_CACHED_SCREEN
) {
377 ImageUtil
.metrics
.recordInterval(ImageUtil
.getMetricName('DisplayTime'));
379 ImageUtil
.metrics
.recordEnum(ImageUtil
.getMetricName('LoadMode'),
380 loadType
, ImageView
.LOAD_TYPE_TOTAL
);
382 if (loadType
=== ImageView
.LOAD_TYPE_ERROR
&&
383 !navigator
.onLine
&& !metadata
.external
.present
) {
384 loadType
= ImageView
.LOAD_TYPE_OFFLINE
;
386 if (loadCallback
) loadCallback(loadType
, animationDuration
, opt_error
);
391 * Prefetches an image.
392 * @param {Gallery.Item} item The image item.
393 * @param {number=} opt_delay Image load delay in ms.
395 ImageView
.prototype.prefetch = function(item
, opt_delay
) {
396 if (item
.contentImage
)
398 this.prefetchLoader_
.load(item
, function(canvas
) {
399 if (canvas
.width
&& canvas
.height
&& !item
.contentImage
)
400 item
.contentImage
= canvas
;
406 * @param {ImageRect=} opt_zoomToRect Target rectangle for zoom-out-effect.
408 ImageView
.prototype.unload = function(opt_zoomToRect
) {
409 if (this.unloadTimer_
) {
410 clearTimeout(this.unloadTimer_
);
411 this.unloadTimer_
= null;
413 if (opt_zoomToRect
&& this.screenImage_
) {
414 var effect
= this.createZoomEffect(opt_zoomToRect
);
415 this.setTransform_(this.screenImage_
, this.viewport_
, effect
);
416 this.screenImage_
.setAttribute('fade', true);
417 this.unloadTimer_
= setTimeout(function() {
418 this.unloadTimer_
= null;
419 this.unload(null /* force unload */);
420 }.bind(this), effect
.getSafeInterval());
423 this.container_
.textContent
= '';
424 this.contentCanvas_
= null;
425 this.screenImage_
= null;
429 * @param {HTMLCanvasElement} content The image element.
430 * @param {number=} opt_width Image width.
431 * @param {number=} opt_height Image height.
432 * @param {boolean=} opt_preview True if the image is a preview (not full res).
435 ImageView
.prototype.replaceContent_ = function(
436 content
, opt_width
, opt_height
, opt_preview
) {
438 if (this.contentCanvas_
&& this.contentCanvas_
.parentNode
=== this.container_
)
439 this.container_
.removeChild(this.contentCanvas_
);
441 this.screenImage_
= this.document_
.createElement('canvas');
442 this.screenImage_
.className
= 'image';
444 this.contentCanvas_
= content
;
445 this.invalidateCaches();
446 this.viewport_
.setImageSize(
447 opt_width
|| this.contentCanvas_
.width
,
448 opt_height
|| this.contentCanvas_
.height
);
451 this.container_
.appendChild(this.screenImage_
);
453 this.preview_
= opt_preview
;
454 // If this is not a thumbnail, cache the content and the screen-scale image.
455 if (this.hasValidImage()) {
456 // Insert the full resolution canvas into DOM so that it can be printed.
457 this.container_
.appendChild(this.contentCanvas_
);
458 this.contentCanvas_
.classList
.add('fullres');
460 this.contentItem_
.contentImage
= this.contentCanvas_
;
461 this.contentItem_
.screenImage
= this.screenImage_
;
463 // TODO(kaznacheev): It is better to pass screenImage_ as it is usually
464 // much smaller than contentCanvas_ and still contains the entire image.
465 // Once we implement zoom/pan we should pass contentCanvas_ instead.
466 this.updateThumbnail_(this.screenImage_
);
468 this.contentRevision_
++;
469 for (var i
= 0; i
!== this.contentCallbacks_
.length
; i
++) {
471 this.contentCallbacks_
[i
]();
480 * Adds a listener for content changes.
481 * @param {function()} callback Callback.
483 ImageView
.prototype.addContentCallback = function(callback
) {
484 this.contentCallbacks_
.push(callback
);
488 * Updates the cached thumbnail image.
490 * @param {HTMLCanvasElement} canvas The source canvas.
493 ImageView
.prototype.updateThumbnail_ = function(canvas
) {
494 ImageUtil
.trace
.resetTimer('thumb');
495 var pixelCount
= 10000;
497 Math
.max(1, Math
.sqrt(canvas
.width
* canvas
.height
/ pixelCount
));
499 this.thumbnailCanvas_
= canvas
.ownerDocument
.createElement('canvas');
500 this.thumbnailCanvas_
.width
= Math
.round(canvas
.width
/ downScale
);
501 this.thumbnailCanvas_
.height
= Math
.round(canvas
.height
/ downScale
);
502 ImageRect
.drawImage(this.thumbnailCanvas_
.getContext('2d'), canvas
);
503 ImageUtil
.trace
.reportTimer('thumb');
507 * Replaces the displayed image, possibly with slide-in animation.
509 * @param {HTMLCanvasElement} content The image element.
510 * @param {Object=} opt_effect Transition effect object.
511 * @param {number=} opt_width Image width.
512 * @param {number=} opt_height Image height.
513 * @param {boolean=} opt_preview True if the image is a preview (not full res).
515 ImageView
.prototype.replace = function(
516 content
, opt_effect
, opt_width
, opt_height
, opt_preview
) {
517 var oldScreenImage
= this.screenImage_
;
518 var oldViewport
= this.viewport_
.clone();
520 this.replaceContent_(content
, opt_width
, opt_height
, opt_preview
);
523 oldScreenImage
.parentNode
.removeChild(oldScreenImage
);
527 var newScreenImage
= this.screenImage_
;
528 this.viewport_
.resetView();
531 ImageUtil
.setAttribute(newScreenImage
, 'fade', true);
533 newScreenImage
, this.viewport_
, opt_effect
, 0 /* instant */);
535 setTimeout(function() {
540 opt_effect
&& opt_effect
.getDuration());
541 if (oldScreenImage
) {
542 ImageUtil
.setAttribute(newScreenImage
, 'fade', false);
543 ImageUtil
.setAttribute(oldScreenImage
, 'fade', true);
544 console
.assert(opt_effect
.getReverse
, 'Cannot revert an effect.');
545 var reverse
= opt_effect
.getReverse();
546 this.setTransform_(oldScreenImage
, oldViewport
, reverse
);
547 setTimeout(function() {
548 if (oldScreenImage
.parentNode
)
549 oldScreenImage
.parentNode
.removeChild(oldScreenImage
);
550 }, reverse
.getSafeInterval());
556 * @param {HTMLCanvasElement} element The element to transform.
557 * @param {Viewport} viewport Viewport to be used for calculating
559 * @param {ImageView.Effect=} opt_effect The effect to apply.
560 * @param {number=} opt_duration Transition duration.
563 ImageView
.prototype.setTransform_ = function(
564 element
, viewport
, opt_effect
, opt_duration
) {
566 opt_effect
= new ImageView
.Effect
.None();
567 if (typeof opt_duration
!== 'number')
568 opt_duration
= opt_effect
.getDuration();
569 element
.style
.webkitTransitionDuration
= opt_duration
+ 'ms';
570 element
.style
.webkitTransitionTimingFunction
= opt_effect
.getTiming();
571 element
.style
.webkitTransform
= opt_effect
.transform(element
, viewport
);
575 * @param {ImageRect} screenRect Target rectangle in screen coordinates.
576 * @return {ImageView.Effect.Zoom} Zoom effect object.
578 ImageView
.prototype.createZoomEffect = function(screenRect
) {
579 return new ImageView
.Effect
.ZoomToScreen(
581 ImageView
.MODE_TRANSITION_DURATION
);
585 * Visualizes crop or rotate operation. Hide the old image instantly, animate
586 * the new image to visualize the operation.
588 * @param {HTMLCanvasElement} canvas New content canvas.
589 * @param {ImageRect} imageCropRect The crop rectangle in image coordinates.
590 * Null for rotation operations.
591 * @param {number} rotate90 Rotation angle in 90 degree increments.
592 * @return {number} Animation duration.
594 ImageView
.prototype.replaceAndAnimate = function(
595 canvas
, imageCropRect
, rotate90
) {
596 var oldImageBounds
= {
597 width
: this.viewport_
.getImageBounds().width
,
598 height
: this.viewport_
.getImageBounds().height
600 var oldScreenImage
= this.screenImage_
;
601 this.replaceContent_(canvas
);
602 var newScreenImage
= this.screenImage_
;
603 var effect
= rotate90
?
604 new ImageView
.Effect
.Rotate(rotate90
> 0) :
605 new ImageView
.Effect
.Zoom(
606 oldImageBounds
.width
, oldImageBounds
.height
, imageCropRect
);
608 this.setTransform_(newScreenImage
, this.viewport_
, effect
, 0 /* instant */);
610 oldScreenImage
.parentNode
.appendChild(newScreenImage
);
611 oldScreenImage
.parentNode
.removeChild(oldScreenImage
);
613 // Let the layout fire, then animate back to non-transformed state.
615 this.setTransform_
.bind(
616 this, newScreenImage
, this.viewport_
, null, effect
.getDuration()),
619 return effect
.getSafeInterval();
623 * Visualizes "undo crop". Shrink the current image to the given crop rectangle
624 * while fading in the new image.
626 * @param {HTMLCanvasElement} canvas New content canvas.
627 * @param {ImageRect} imageCropRect The crop rectangle in image coordinates.
628 * @return {number} Animation duration.
630 ImageView
.prototype.animateAndReplace = function(canvas
, imageCropRect
) {
631 var oldScreenImage
= this.screenImage_
;
632 this.replaceContent_(canvas
);
633 var newScreenImage
= this.screenImage_
;
634 var setFade
= ImageUtil
.setAttribute
.bind(null, newScreenImage
, 'fade');
636 oldScreenImage
.parentNode
.insertBefore(newScreenImage
, oldScreenImage
);
637 var effect
= new ImageView
.Effect
.Zoom(
638 this.viewport_
.getImageBounds().width
,
639 this.viewport_
.getImageBounds().height
,
642 // Animate to the transformed state.
643 this.setTransform_(oldScreenImage
, this.viewport_
, effect
);
644 setTimeout(setFade
.bind(null, false), 0);
645 setTimeout(function() {
646 if (oldScreenImage
.parentNode
)
647 oldScreenImage
.parentNode
.removeChild(oldScreenImage
);
648 }, effect
.getSafeInterval());
650 return effect
.getSafeInterval();
653 /* Transition effects */
656 * Base class for effects.
658 * @param {number} duration Duration in ms.
659 * @param {string=} opt_timing CSS transition timing function name.
662 ImageView
.Effect = function(duration
, opt_timing
) {
663 this.duration_
= duration
;
664 this.timing_
= opt_timing
|| 'linear';
670 ImageView
.Effect
.DEFAULT_DURATION
= 180;
675 ImageView
.Effect
.MARGIN
= 100;
678 * @return {number} Effect duration in ms.
680 ImageView
.Effect
.prototype.getDuration = function() { return this.duration_
; };
683 * @return {number} Delay in ms since the beginning of the animation after which
684 * it is safe to perform CPU-heavy operations without disrupting the animation.
686 ImageView
.Effect
.prototype.getSafeInterval = function() {
687 return this.getDuration() + ImageView
.Effect
.MARGIN
;
691 * @return {string} CSS transition timing function name.
693 ImageView
.Effect
.prototype.getTiming = function() { return this.timing_
; };
696 * Obtains the CSS transformation string of the effect.
697 * @param {HTMLCanvasElement} element Canvas element to be applied the
699 * @param {Viewport} viewport Current viewport.
700 * @return {string} CSS transformation description.
702 ImageView
.Effect
.prototype.transform = function(element
, viewport
) {
703 throw new Error('Not implemented.');
711 * @extends {ImageView.Effect}
713 ImageView
.Effect
.None = function() {
714 ImageView
.Effect
.call(this, 0, 'easy-out');
718 * Inherits from ImageView.Effect.
720 ImageView
.Effect
.None
.prototype = { __proto__
: ImageView
.Effect
.prototype };
723 * @param {HTMLCanvasElement} element Element.
724 * @param {Viewport} viewport Current viewport.
725 * @return {string} Transform string.
727 ImageView
.Effect
.None
.prototype.transform = function(element
, viewport
) {
728 return viewport
.getTransformation();
734 * @param {number} direction -1 for left, 1 for right.
735 * @param {boolean=} opt_slow True if slow (as in slideshow).
737 * @extends {ImageView.Effect}
739 ImageView
.Effect
.Slide
= function Slide(direction
, opt_slow
) {
740 ImageView
.Effect
.call(this,
741 opt_slow
? 800 : ImageView
.Effect
.DEFAULT_DURATION
, 'ease-out');
742 this.direction_
= direction
;
743 this.slow_
= opt_slow
;
744 this.shift_
= opt_slow
? 100 : 40;
745 if (this.direction_
< 0) this.shift_
= -this.shift_
;
748 ImageView
.Effect
.Slide
.prototype = { __proto__
: ImageView
.Effect
.prototype };
751 * Reverses the slide effect.
752 * @return {ImageView.Effect.Slide} Reversed effect.
754 ImageView
.Effect
.Slide
.prototype.getReverse = function() {
755 return new ImageView
.Effect
.Slide(-this.direction_
, this.slow_
);
761 ImageView
.Effect
.Slide
.prototype.transform = function(element
, viewport
) {
762 return viewport
.getShiftTransformation(this.shift_
);
768 * Animates the original rectangle to the target rectangle.
770 * @param {number} previousImageWidth Width of the full resolution image.
771 * @param {number} previousImageHeight Height of the full resolution image.
772 * @param {ImageRect} imageCropRect Crop rectangle in the full resolution image.
773 * @param {number=} opt_duration Duration of the effect.
775 * @extends {ImageView.Effect}
777 ImageView
.Effect
.Zoom = function(
778 previousImageWidth
, previousImageHeight
, imageCropRect
, opt_duration
) {
779 ImageView
.Effect
.call(this,
780 opt_duration
|| ImageView
.Effect
.DEFAULT_DURATION
, 'ease-out');
781 this.previousImageWidth_
= previousImageWidth
;
782 this.previousImageHeight_
= previousImageHeight
;
783 this.imageCropRect_
= imageCropRect
;
786 ImageView
.Effect
.Zoom
.prototype = { __proto__
: ImageView
.Effect
.prototype };
791 ImageView
.Effect
.Zoom
.prototype.transform = function(element
, viewport
) {
792 return viewport
.getInverseTransformForCroppedImage(
793 this.previousImageWidth_
, this.previousImageHeight_
, this.imageCropRect_
);
797 * Effect to zoom to a screen rectangle.
799 * @param {ImageRect} screenRect Rectangle in the application window's
801 * @param {number=} opt_duration Duration of effect.
803 * @extends {ImageView.Effect}
805 ImageView
.Effect
.ZoomToScreen = function(screenRect
, opt_duration
) {
806 ImageView
.Effect
.call(this, opt_duration
);
807 this.screenRect_
= screenRect
;
810 ImageView
.Effect
.ZoomToScreen
.prototype = {
811 __proto__
: ImageView
.Effect
.prototype
817 ImageView
.Effect
.ZoomToScreen
.prototype.transform = function(
819 return viewport
.getScreenRectTransformForImage(this.screenRect_
);
825 * @param {boolean} orientation Orientation of rotation. True is for clockwise
826 * and false is for counterclockwise.
828 * @extends {ImageView.Effect}
830 ImageView
.Effect
.Rotate = function(orientation
) {
831 ImageView
.Effect
.call(this, ImageView
.Effect
.DEFAULT_DURATION
);
832 this.orientation_
= orientation
;
835 ImageView
.Effect
.Rotate
.prototype = { __proto__
: ImageView
.Effect
.prototype };
840 ImageView
.Effect
.Rotate
.prototype.transform = function(element
, viewport
) {
841 return viewport
.getInverseTransformForRotatedImage(this.orientation_
);