OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 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 'use strict'; | 5 'use strict'; |
6 | 6 |
7 /** | 7 /** |
8 * The overlay displaying the image. | 8 * The overlay displaying the image. |
9 * | 9 * |
10 * @param {HTMLElement} container The container element. | 10 * @param {HTMLElement} container The container element. |
(...skipping 608 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
619 element.style.webkitTransitionDuration = opt_duration + 'ms'; | 619 element.style.webkitTransitionDuration = opt_duration + 'ms'; |
620 element.style.webkitTransitionTimingFunction = opt_effect.getTiming(); | 620 element.style.webkitTransitionTimingFunction = opt_effect.getTiming(); |
621 element.style.webkitTransform = opt_effect.transform(element, this.viewport_); | 621 element.style.webkitTransform = opt_effect.transform(element, this.viewport_); |
622 }; | 622 }; |
623 | 623 |
624 /** | 624 /** |
625 * @param {Rect} screenRect Target rectangle in screen coordinates. | 625 * @param {Rect} screenRect Target rectangle in screen coordinates. |
626 * @return {ImageView.Effect.Zoom} Zoom effect object. | 626 * @return {ImageView.Effect.Zoom} Zoom effect object. |
627 */ | 627 */ |
628 ImageView.prototype.createZoomEffect = function(screenRect) { | 628 ImageView.prototype.createZoomEffect = function(screenRect) { |
629 return new ImageView.Effect.Zoom( | 629 return new ImageView.Effect.ZoomToScreen( |
630 this.viewport_.screenToDeviceRect(screenRect), | 630 screenRect, |
631 null /* use viewport */, | |
632 ImageView.MODE_TRANSITION_DURATION); | 631 ImageView.MODE_TRANSITION_DURATION); |
633 }; | 632 }; |
634 | 633 |
635 /** | 634 /** |
636 * Visualizes crop or rotate operation. Hide the old image instantly, animate | 635 * Visualizes crop or rotate operation. Hide the old image instantly, animate |
637 * the new image to visualize the operation. | 636 * the new image to visualize the operation. |
638 * | 637 * |
639 * @param {HTMLCanvasElement} canvas New content canvas. | 638 * @param {HTMLCanvasElement} canvas New content canvas. |
640 * @param {Rect} imageCropRect The crop rectangle in image coordinates. | 639 * @param {Rect} imageCropRect The crop rectangle in image coordinates. |
641 * Null for rotation operations. | 640 * Null for rotation operations. |
642 * @param {number} rotate90 Rotation angle in 90 degree increments. | 641 * @param {number} rotate90 Rotation angle in 90 degree increments. |
643 * @return {number} Animation duration. | 642 * @return {number} Animation duration. |
644 */ | 643 */ |
645 ImageView.prototype.replaceAndAnimate = function( | 644 ImageView.prototype.replaceAndAnimate = function( |
646 canvas, imageCropRect, rotate90) { | 645 canvas, imageCropRect, rotate90) { |
647 var oldScale = this.viewport_.getScale(); | 646 var oldImageBounds = { |
648 var deviceCropRect = imageCropRect && this.viewport_.screenToDeviceRect( | 647 width: this.viewport_.getImageBounds().width, |
649 this.viewport_.imageToScreenRect(imageCropRect)); | 648 height: this.viewport_.getImageBounds().height |
650 | 649 }; |
651 var oldScreenImage = this.screenImage_; | 650 var oldScreenImage = this.screenImage_; |
652 this.replaceContent_(canvas); | 651 this.replaceContent_(canvas); |
653 var newScreenImage = this.screenImage_; | 652 var newScreenImage = this.screenImage_; |
654 | |
655 // Display the new canvas, initially transformed. | |
656 var deviceFullRect = this.viewport_.getDeviceClipped(); | |
657 | |
658 var effect = rotate90 ? | 653 var effect = rotate90 ? |
659 new ImageView.Effect.Rotate( | 654 new ImageView.Effect.Rotate(rotate90 > 0) : |
660 oldScale / this.viewport_.getScale(), -rotate90) : | 655 new ImageView.Effect.Zoom( |
661 new ImageView.Effect.Zoom(deviceCropRect, deviceFullRect); | 656 oldImageBounds.width, oldImageBounds.height, imageCropRect); |
662 | 657 |
663 this.setTransform(newScreenImage, effect, 0 /* instant */); | 658 this.setTransform(newScreenImage, effect, 0 /* instant */); |
664 | 659 |
665 oldScreenImage.parentNode.appendChild(newScreenImage); | 660 oldScreenImage.parentNode.appendChild(newScreenImage); |
666 oldScreenImage.parentNode.removeChild(oldScreenImage); | 661 oldScreenImage.parentNode.removeChild(oldScreenImage); |
667 | 662 |
668 // Let the layout fire, then animate back to non-transformed state. | 663 // Let the layout fire, then animate back to non-transformed state. |
669 setTimeout( | 664 setTimeout( |
670 this.setTransform.bind( | 665 this.setTransform.bind( |
671 this, newScreenImage, null, effect.getDuration()), | 666 this, newScreenImage, null, effect.getDuration()), |
672 0); | 667 0); |
673 | 668 |
674 return effect.getSafeInterval(); | 669 return effect.getSafeInterval(); |
675 }; | 670 }; |
676 | 671 |
677 /** | 672 /** |
678 * Visualizes "undo crop". Shrink the current image to the given crop rectangle | 673 * Visualizes "undo crop". Shrink the current image to the given crop rectangle |
679 * while fading in the new image. | 674 * while fading in the new image. |
680 * | 675 * |
681 * @param {HTMLCanvasElement} canvas New content canvas. | 676 * @param {HTMLCanvasElement} canvas New content canvas. |
682 * @param {Rect} imageCropRect The crop rectangle in image coordinates. | 677 * @param {Rect} imageCropRect The crop rectangle in image coordinates. |
683 * @return {number} Animation duration. | 678 * @return {number} Animation duration. |
684 */ | 679 */ |
685 ImageView.prototype.animateAndReplace = function(canvas, imageCropRect) { | 680 ImageView.prototype.animateAndReplace = function(canvas, imageCropRect) { |
686 var deviceFullRect = this.viewport_.getDeviceClipped(); | |
687 var oldScale = this.viewport_.getScale(); | |
688 | |
689 var oldScreenImage = this.screenImage_; | 681 var oldScreenImage = this.screenImage_; |
690 this.replaceContent_(canvas); | 682 this.replaceContent_(canvas); |
691 var newScreenImage = this.screenImage_; | 683 var newScreenImage = this.screenImage_; |
692 | |
693 var deviceCropRect = this.viewport_.screenToDeviceRect( | |
694 this.viewport_.imageToScreenRect(imageCropRect)); | |
695 | |
696 var setFade = ImageUtil.setAttribute.bind(null, newScreenImage, 'fade'); | 684 var setFade = ImageUtil.setAttribute.bind(null, newScreenImage, 'fade'); |
697 setFade(true); | 685 setFade(true); |
698 oldScreenImage.parentNode.insertBefore(newScreenImage, oldScreenImage); | 686 oldScreenImage.parentNode.insertBefore(newScreenImage, oldScreenImage); |
| 687 var effect = new ImageView.Effect.Zoom( |
| 688 this.viewport_.getImageBounds().width, |
| 689 this.viewport_.getImageBounds().height, |
| 690 imageCropRect); |
699 | 691 |
700 var effect = new ImageView.Effect.Zoom(deviceCropRect, deviceFullRect); | |
701 // Animate to the transformed state. | 692 // Animate to the transformed state. |
702 this.setTransform(oldScreenImage, effect); | 693 this.setTransform(oldScreenImage, effect); |
703 | |
704 setTimeout(setFade.bind(null, false), 0); | 694 setTimeout(setFade.bind(null, false), 0); |
705 | |
706 setTimeout(function() { | 695 setTimeout(function() { |
707 if (oldScreenImage.parentNode) | 696 if (oldScreenImage.parentNode) |
708 oldScreenImage.parentNode.removeChild(oldScreenImage); | 697 oldScreenImage.parentNode.removeChild(oldScreenImage); |
709 }, effect.getSafeInterval()); | 698 }, effect.getSafeInterval()); |
710 | 699 |
711 return effect.getSafeInterval(); | 700 return effect.getSafeInterval(); |
712 }; | 701 }; |
713 | 702 |
714 | |
715 /** | 703 /** |
716 * Generic cache with a limited capacity and LRU eviction. | 704 * Generic cache with a limited capacity and LRU eviction. |
717 * @param {number} capacity Maximum number of cached item. | 705 * @param {number} capacity Maximum number of cached item. |
718 * @constructor | 706 * @constructor |
719 */ | 707 */ |
720 ImageView.Cache = function(capacity) { | 708 ImageView.Cache = function(capacity) { |
721 this.capacity_ = capacity; | 709 this.capacity_ = capacity; |
722 this.map_ = {}; | 710 this.map_ = {}; |
723 this.order_ = []; | 711 this.order_ = []; |
724 }; | 712 }; |
(...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
845 ImageView.Effect.prototype.getSafeInterval = function() { | 833 ImageView.Effect.prototype.getSafeInterval = function() { |
846 return this.getDuration() + ImageView.Effect.MARGIN; | 834 return this.getDuration() + ImageView.Effect.MARGIN; |
847 }; | 835 }; |
848 | 836 |
849 /** | 837 /** |
850 * @return {string} CSS transition timing function name. | 838 * @return {string} CSS transition timing function name. |
851 */ | 839 */ |
852 ImageView.Effect.prototype.getTiming = function() { return this.timing_; }; | 840 ImageView.Effect.prototype.getTiming = function() { return this.timing_; }; |
853 | 841 |
854 /** | 842 /** |
855 * @param {HTMLCanvasElement} element Element. | 843 * Obtains the CSS transformation string of the effect. |
856 * @return {number} Preferred pixel ration to use with this element. | 844 * @param {DOMCanvas} element Canvas element to be applied the transforamtion. |
857 * @private | 845 * @param {Viewport} viewport Current viewport. |
| 846 * @return CSS transformation description. |
858 */ | 847 */ |
859 ImageView.Effect.getPixelRatio_ = function(element) { | 848 ImageView.Effect.prototype.transform = function(element, viewport) { |
860 if (element.constructor.name === 'HTMLCanvasElement') | 849 throw new Error('Not implemented.'); |
861 return Viewport.getDevicePixelRatio(); | |
862 else | |
863 return 1; | |
864 }; | 850 }; |
865 | 851 |
866 /** | 852 /** |
867 * Default effect. It is not a no-op as it needs to adjust a canvas scale | 853 * Default effect. It is not a no-op as it needs to adjust a canvas scale |
868 * for devicePixelRatio. | 854 * for devicePixelRatio. |
869 * | 855 * |
870 * @constructor | 856 * @constructor |
| 857 * @extends {ImageView.Effect} |
871 */ | 858 */ |
872 ImageView.Effect.None = function() { | 859 ImageView.Effect.None = function() { |
873 ImageView.Effect.call(this, 0); | 860 ImageView.Effect.call(this, 0); |
874 }; | 861 }; |
875 | 862 |
876 /** | 863 /** |
877 * Inherits from ImageView.Effect. | 864 * Inherits from ImageView.Effect. |
878 */ | 865 */ |
879 ImageView.Effect.None.prototype = { __proto__: ImageView.Effect.prototype }; | 866 ImageView.Effect.None.prototype = { __proto__: ImageView.Effect.prototype }; |
880 | 867 |
881 /** | 868 /** |
882 * @param {HTMLCanvasElement} element Element. | 869 * @param {HTMLCanvasElement} element Element. |
| 870 * @param {Viewport} viewport Current viewport. |
883 * @return {string} Transform string. | 871 * @return {string} Transform string. |
884 */ | 872 */ |
885 ImageView.Effect.None.prototype.transform = function(element) { | 873 ImageView.Effect.None.prototype.transform = function(element, viewport) { |
886 var ratio = ImageView.Effect.getPixelRatio_(element); | 874 return viewport.getTransformation(); |
887 return 'scale(' + (1 / ratio) + ')'; | |
888 }; | 875 }; |
889 | 876 |
890 /** | 877 /** |
891 * Slide effect. | 878 * Slide effect. |
892 * | 879 * |
893 * @param {number} direction -1 for left, 1 for right. | 880 * @param {number} direction -1 for left, 1 for right. |
894 * @param {boolean=} opt_slow True if slow (as in slideshow). | 881 * @param {boolean=} opt_slow True if slow (as in slideshow). |
895 * @constructor | 882 * @constructor |
| 883 * @extends {ImageView.Effect} |
896 */ | 884 */ |
897 ImageView.Effect.Slide = function Slide(direction, opt_slow) { | 885 ImageView.Effect.Slide = function Slide(direction, opt_slow) { |
898 ImageView.Effect.call(this, | 886 ImageView.Effect.call(this, |
899 opt_slow ? 800 : ImageView.Effect.DEFAULT_DURATION, 'ease-in-out'); | 887 opt_slow ? 800 : ImageView.Effect.DEFAULT_DURATION, 'ease-in-out'); |
900 this.direction_ = direction; | 888 this.direction_ = direction; |
901 this.slow_ = opt_slow; | 889 this.slow_ = opt_slow; |
902 this.shift_ = opt_slow ? 100 : 40; | 890 this.shift_ = opt_slow ? 100 : 40; |
903 if (this.direction_ < 0) this.shift_ = -this.shift_; | 891 if (this.direction_ < 0) this.shift_ = -this.shift_; |
904 }; | 892 }; |
905 | 893 |
906 /** | |
907 * Inherits from ImageView.Effect. | |
908 */ | |
909 ImageView.Effect.Slide.prototype = { __proto__: ImageView.Effect.prototype }; | 894 ImageView.Effect.Slide.prototype = { __proto__: ImageView.Effect.prototype }; |
910 | 895 |
911 /** | 896 /** |
912 * @return {ImageView.Effect.Slide} Reverse Slide effect. | 897 * Reverses the slide effect. |
| 898 * @return {ImageView.Effect.Slide} Reversed effect. |
913 */ | 899 */ |
914 ImageView.Effect.Slide.prototype.getReverse = function() { | 900 ImageView.Effect.Slide.prototype.getReverse = function() { |
915 return new ImageView.Effect.Slide(-this.direction_, this.slow_); | 901 return new ImageView.Effect.Slide(-this.direction_, this.slow_); |
916 }; | 902 }; |
917 | 903 |
918 /** | 904 /** |
919 * @param {HTMLCanvasElement} element Element. | 905 * @override |
920 * @return {string} Transform string. | |
921 */ | 906 */ |
922 ImageView.Effect.Slide.prototype.transform = function(element) { | 907 ImageView.Effect.Slide.prototype.transform = function(element, viewport) { |
923 var ratio = ImageView.Effect.getPixelRatio_(element); | 908 return viewport.getShiftTransformation(this.shift_); |
924 return 'scale(' + (1 / ratio) + ') translate(' + this.shift_ + 'px, 0px)'; | |
925 }; | 909 }; |
926 | 910 |
927 /** | 911 /** |
928 * Zoom effect. | 912 * Zoom effect. |
929 * | 913 * |
930 * Animates the original rectangle to the target rectangle. Both parameters | 914 * Animates the original rectangle to the target rectangle. Both parameters |
931 * should be given in device coordinates (accounting for devicePixelRatio). | 915 * should be given in device coordinates (accounting for devicePixelRatio). |
932 * | 916 * |
933 * @param {Rect} deviceTargetRect Target rectangle. | 917 * @param {number} previousImageWidth Width of the full resolution image. |
934 * @param {Rect=} opt_deviceOriginalRect Original rectangle. If omitted, | 918 * @param {number} previousImageHeight Hieght of the full resolution image. |
935 * the full viewport will be used at the time of |transform| call. | 919 * @param {Rect} imageCropRect Crop rectangle in the full resolution image. |
936 * @param {number=} opt_duration Duration in ms. | 920 * @param {number=} opt_duration Duration of the effect. |
937 * @constructor | 921 * @constructor |
| 922 * @extends {ImageView.Effect} |
938 */ | 923 */ |
939 ImageView.Effect.Zoom = function( | 924 ImageView.Effect.Zoom = function( |
940 deviceTargetRect, opt_deviceOriginalRect, opt_duration) { | 925 previousImageWidth, previousImageHeight, imageCropRect, opt_duration) { |
941 ImageView.Effect.call(this, | 926 ImageView.Effect.call(this, |
942 opt_duration || ImageView.Effect.DEFAULT_DURATION); | 927 opt_duration || ImageView.Effect.DEFAULT_DURATION); |
943 this.target_ = deviceTargetRect; | 928 this.previousImageWidth_ = previousImageWidth; |
944 this.original_ = opt_deviceOriginalRect; | 929 this.previousImageHeight_ = previousImageHeight; |
| 930 this.imageCropRect_ = imageCropRect; |
| 931 }; |
| 932 |
| 933 ImageView.Effect.Zoom.prototype = { __proto__: ImageView.Effect.prototype }; |
| 934 |
| 935 /** |
| 936 * @override |
| 937 */ |
| 938 ImageView.Effect.Zoom.prototype.transform = function(element, viewport) { |
| 939 return viewport.getInverseTransformForCroppedImage( |
| 940 this.previousImageWidth_, this.previousImageHeight_, this.imageCropRect_); |
945 }; | 941 }; |
946 | 942 |
947 /** | 943 /** |
948 * Inherits from ImageView.Effect. | 944 * Effect to zoom to a screen rectangle. |
| 945 * |
| 946 * @param {Rect} screenRect Rectangle in the application window's coordinate. |
| 947 * @param {number=} opt_duration Duration of effect. |
| 948 * @constructor |
| 949 * @extends {ImageView.Effect} |
949 */ | 950 */ |
950 ImageView.Effect.Zoom.prototype = { __proto__: ImageView.Effect.prototype }; | 951 ImageView.Effect.ZoomToScreen = function(screenRect, opt_duration) { |
| 952 ImageView.Effect.call(this, opt_duration); |
| 953 this.screenRect_ = screenRect; |
| 954 }; |
951 | 955 |
952 /** | 956 ImageView.Effect.ZoomToScreen.prototype = { |
953 * @param {HTMLCanvasElement} element Element. | 957 __proto__: ImageView.Effect.prototype |
954 * @param {Viewport} viewport Viewport. | |
955 * @return {string} Transform string. | |
956 */ | |
957 ImageView.Effect.Zoom.prototype.transform = function(element, viewport) { | |
958 if (!this.original_) | |
959 this.original_ = viewport.getDeviceClipped(); | |
960 | |
961 var ratio = ImageView.Effect.getPixelRatio_(element); | |
962 | |
963 var dx = (this.target_.left + this.target_.width / 2) - | |
964 (this.original_.left + this.original_.width / 2); | |
965 var dy = (this.target_.top + this.target_.height / 2) - | |
966 (this.original_.top + this.original_.height / 2); | |
967 | |
968 var scaleX = this.target_.width / this.original_.width; | |
969 var scaleY = this.target_.height / this.original_.height; | |
970 | |
971 return 'translate(' + (dx / ratio) + 'px,' + (dy / ratio) + 'px) ' + | |
972 'scaleX(' + (scaleX / ratio) + ') scaleY(' + (scaleY / ratio) + ')'; | |
973 }; | 958 }; |
974 | 959 |
975 /** | 960 /** |
976 * Rotate effect. | 961 * @override |
977 * | |
978 * @param {number} scale Scale. | |
979 * @param {number} rotate90 Rotation in 90 degrees increments. | |
980 * @constructor | |
981 */ | 962 */ |
982 ImageView.Effect.Rotate = function(scale, rotate90) { | 963 ImageView.Effect.ZoomToScreen.prototype.transform = function( |
983 ImageView.Effect.call(this, ImageView.Effect.DEFAULT_DURATION); | 964 element, viewport) { |
984 this.scale_ = scale; | 965 return viewport.getScreenRectTransformForImage(this.screenRect_); |
985 this.rotate90_ = rotate90; | |
986 }; | 966 }; |
987 | 967 |
988 /** | 968 /** |
989 * Inherits from ImageView.Effect. | 969 * Rotation effect. |
| 970 * |
| 971 * @param {boolean} orientation Orientation of rotation. True is for clockwise |
| 972 * and false is for counterclockwise. |
| 973 * @constructor |
| 974 * @extends {ImageView.Effect} |
990 */ | 975 */ |
| 976 ImageView.Effect.Rotate = function(orientation) { |
| 977 ImageView.Effect.call(this, ImageView.Effect.DEFAULT_DURATION); |
| 978 this.orientation_ = orientation; |
| 979 }; |
| 980 |
991 ImageView.Effect.Rotate.prototype = { __proto__: ImageView.Effect.prototype }; | 981 ImageView.Effect.Rotate.prototype = { __proto__: ImageView.Effect.prototype }; |
992 | 982 |
993 /** | 983 /** |
994 * @param {HTMLCanvasElement} element Element. | 984 * @override |
995 * @return {string} Transform string. | |
996 */ | 985 */ |
997 ImageView.Effect.Rotate.prototype.transform = function(element) { | 986 ImageView.Effect.Rotate.prototype.transform = function(element, viewport) { |
998 var ratio = ImageView.Effect.getPixelRatio_(element); | 987 return viewport.getInverseTransformForRotatedImage(this.orientation_); |
999 return 'rotate(' + (this.rotate90_ * 90) + 'deg) ' + | |
1000 'scale(' + (this.scale_ / ratio) + ')'; | |
1001 }; | 988 }; |
OLD | NEW |