Hybrid View

  1. #1
    Sencha User
    Join Date
    Dec 2011
    Location
    Brazil
    Posts
    106
    Vote Rating
    0
    Perdiga is an unknown quantity at this point

      -1  

    Default Pinch Image with carousel and working fine

    Pinch Image with carousel and working fine


    index.html
    Code:
    <!DOCTYPE html>
    <html lang="en">
        <head>
            <meta charset="UTF-8">
            <title>ImageViewer Example</title>
            
            <link rel="stylesheet" type="text/css" href="http://docs.sencha.com/touch/2-0/touch/resources/css/sencha-touch.css">
            <script type="text/javascript" src="http://docs.sencha.com/touch/2-0/touch/sencha-touch-all-debug.js"></script>
    
    
            <script type="text/javascript" src="ImageViewer.js"></script>
            
            <script type="text/javascript">
                Ext.application({
                
                    name: 'ImageExample'
                    
                    ,launch: function() {
                    
                        Ext.define('WPMobile.Viewport', {
                            extend: 'Ext.Carousel',
                            xtype: 'my-viewport',
                            config: {
                                fullscreen: true,
                                layout: 'fit',
                                items: [
                                    {
                                        xtype: 'imageviewer',
                                        style: {
                                            backgroundColor: '#255'
                                        },
                                        imageSrc: 'http://cdn.macrumors.com/article/2010/09/03/145454-itunes_10_icon.jpg'
                                    },
                                    {
                                        xtype: 'imageviewer',
                                        style: {
                                            backgroundColor: '#255'
                                        },
                                        imageSrc: 'http://www.baixandowallpapers.com/wallpapers/03-2011/lua-cheia-paisagem-noite-1299711122.jpg'
                                    },
                                    {
                                        xtype: 'imageviewer',
                                        style: {
                                            backgroundColor: '#255'
                                        },
                                        imageSrc: 'http://www.baixandowallpapers.com/wallpapers/03-2011/lua-cheia-paisagem-noite-1299711122.jpg'
                                    }
                                ],
                                listeners: {
                                    activeitemchange: function( container,value, oldValue,eOpts ){
                                        oldValue.resetZoom();
                                        this.getActiveItem().rotate();
                                    },
                                    resize: function( component , eOpts ){
                                        this.getActiveItem().rotate();
                                    }
                                
                                },
                            },
                            
                            onDragStart: function (e) {
                                var scroller = this.getActiveItem().getScrollable().getScroller();
                                if (e.targetTouches.length == 1 && (e.deltaX < 0 && scroller.getMaxPosition().x === scroller.position.x) || (e.deltaX > 0 && scroller.position.x == 0)) {
                                    this.callParent(arguments);
                                }
                            },
                            onDrag: function (e) {
                                if (e.targetTouches.length == 1)
                                    this.callParent(arguments);
                            },
                            onDragEnd: function (e) {
                                if (e.targetTouches.length < 2)
                                    this.callParent(arguments);
                            }
    
    
                        });
    
    
    
    
                        Ext.create('WPMobile.Viewport', {
                            id: 'viewport'
                        });              
                                         
                    }
                });
            </script>
            
            
        </head>
    
    
        <body></body>
    </html>
    ImageViewer.js
    Code:
      Ext.define('Arkivus.ImageViewer',{
            
        extend: 'Ext.Container',
        config: {
                doubleTapScale: 1
                ,maxScale: 4
                ,loadingMask: true
                ,previewSrc: false
                ,resizeOnLoad:true
                ,imageSrc: false
                ,initOnActivate: false
                ,cls: 'imageBox'
                ,scrollable: 'both'            
                ,html: '<figure><img></figure>'
        }
        ,xtype: 'imageviewer'
        ,initialize: function() {
            if(this.initOnActivate)
                this.addListener('activate', this.initViewer, this, {delay: 10, single: true});
            else
                this.addListener('painted', this.initViewer, this, {delay: 10, single: true});        
        }
        
        ,initViewer: function() {
            
            //disable scroller
            var scroller = this.getScrollable().getScroller();
            scroller.setDisabled(true);
            
            // mask image viewer
            if(this.getLoadingMask())
                this.setMasked({
                       xtype: 'loadmask',
                });
    
    
            // retrieve DOM els
            this.figEl = this.element.down('figure');
            this.imgEl = this.figEl.down('img');
    
    
            // apply required styles
            this.figEl.setStyle({
                overflow: 'hidden'
                ,display: 'block'
                ,margin: 0
            });
    
    
            this.imgEl.setStyle({
                '-webkit-user-drag': 'none'
                ,'-webkit-transform-origin': '0 0'
                ,'visibility': 'hidden'
            });
    
    
            // show preview
            if(this.getPreviewSrc())
            {
                this.element.setStyle({
                    backgroundImage: 'url('+this.getPreviewSrc()+')'
                    ,backgroundPosition: 'center center'
                    ,backgroundRepeat: 'no-repeat'
                    ,webkitBackgroundSize: 'contain'
                });
            }
    
    
            // attach event listeners
            this.on('load', this.onImageLoad, this);
            this.imgEl.addListener({
                scope: this
                ,doubletap: this.onDoubleTap
                ,pinchstart: this.onImagePinchStart
                ,pinch: this.onImagePinch
                ,pinchend: this.onImagePinchEnd
            });    
    
    
            // load image
            if(this.getImageSrc())
                this.loadImage(this.getImageSrc());
        }
        
        ,loadImage: function(src) {    
            if(this.imgEl){
                this.imgEl.dom.src = src;
                this.imgEl.dom.onload = Ext.Function.bind(this.onLoad, this, this.imgEl, 0);
            }
            else
                this.getImageSrc() = src;
        }
    
    
        ,onLoad : function(el, e) {
            this.fireEvent('load', this, el, e);
        }
        
        ,onImageLoad: function() {
            // get viewport size
            this.viewportWidth = this.viewportWidth || this.getWidth() || this.parent.element.getWidth();
            this.viewportHeight = this.viewportHeight || this.getHeight() || this.parent.element.getHeight();
                
            // grab image size
            this.imgWidth = this.imgEl.dom.width
            this.imgHeight = this.imgEl.dom.height;
                    
            // calculate and apply initial scale to fit image to screen
            if(this.getResizeOnLoad()){
                this.scale = this.baseScale = Math.min(this.viewportWidth/this.imgWidth, this.viewportHeight/this.imgHeight);
                this.setMaxScale(this.scale*4);
            }else{
                this.scale = this.baseScale = 1;
            }
            
            // set initial translation to center
            this.translateX = this.translateBaseX = (this.viewportWidth - this.baseScale * this.imgWidth) / 2;
            this.translateY = this.translateBaseY = (this.viewportHeight - this.baseScale * this.imgHeight) / 2;
            
            // apply initial scale and translation
            this.applyTransform();
            
            // initialize scroller configuration
            this.adjustScroller();
    
    
            // show image and remove mask
            this.imgEl.setStyle({ visibility: 'visible' });
    
    
            // remove preview
            if(this.getPreviewSrc())
            {
                this.element.setStyle({
                    backgroundImage: 'none'
                });
            }
    
    
            if(this.getLoadingMask())
                this.setMasked(false);
    
    
            this.fireEvent('imageLoaded', this);
        }
        
        ,onImagePinchStart: function(ev) {
            var scroller = this.getScrollable().getScroller();
    
    
            // disable scrolling during pinch
            scroller.stopAnimation();
            scroller.setDisabled(true);
            
            // store beginning scale
            this.startScale = this.scale;
            
            // calculate touch midpoint relative to image viewport
            this.originViewportX = (ev.touches[0].pageX + ev.touches[1].pageX) / 2 - this.element.getX();
            this.originViewportY = (ev.touches[0].pageY + ev.touches[1].pageY) / 2 - this.element.getY();
            
            // translate viewport origin to position on scaled image
            this.originScaledImgX = this.originViewportX + scroller.position.x - this.translateX;
            this.originScaledImgY = this.originViewportY + scroller.position.y - this.translateY;
            
            // unscale to find origin on full size image
            this.originFullImgX = this.originScaledImgX / this.scale;
            this.originFullImgY = this.originScaledImgY / this.scale;
            
            // calculate translation needed to counteract new origin and keep image in same position on screen
            this.translateX += (-1 * ((this.imgWidth*(1-this.scale)) * (this.originFullImgX/this.imgWidth)));
            this.translateY += (-1 * ((this.imgHeight*(1-this.scale)) * (this.originFullImgY/this.imgHeight)))
        
            // apply new origin
            this.setOrigin(this.originFullImgX, this.originFullImgY);
        
            // apply translate and scale CSS
            this.applyTransform();
        }
        
        ,onImagePinch: function(ev) {
            // prevent scaling to smaller than screen size
            this.scale = Ext.Number.constrain(ev.scale * this.startScale, this.baseScale-2, this.getMaxScale());
            this.applyTransform();
        }
        
        ,onImagePinchEnd: function(ev) {
            
            // set new translation
            if(this.scale == this.baseScale)
            {
                // move to center
                this.setTranslation(this.translateBaseX, this.translateBaseY);
            }
            else
            {    
                //Resize to init size like ios
                if(this.scale < this.baseScale && this.getResizeOnLoad()){
                    this.resetZoom();
                    return;
                }
                // calculate rescaled origin
                this.originReScaledImgX = this.originScaledImgX * (this.scale / this.startScale);
                this.originReScaledImgY = this.originScaledImgY * (this.scale / this.startScale);
                
                // maintain zoom position
                this.setTranslation(this.originViewportX - this.originReScaledImgX, this.originViewportY - this.originReScaledImgY);            
            }
            // reset origin and update transform with new translation
            this.setOrigin(0, 0);
            this.applyTransform();
    
    
            // adjust scroll container
            this.adjustScroller();
        }
    
    
        
        ,onDoubleTap: function(ev, t) {
            var that = this;
            var scroller = this.getScrollable().getScroller();
            if(!this.getDoubleTapScale())
                return false;
            
            // set scale and translation
            if(this.scale > this.baseScale)
            {
                // zoom out to base view
                this.scale = this.baseScale;
                this.setTranslation(this.translateBaseX, this.translateBaseY);
                // reset origin and update transform with new translation
                this.applyTransform();
    
    
                // adjust scroll container
                this.adjustScroller();
                
                // force repaint to solve occasional iOS rendering delay
                Ext.repaint();
            }
            else
            {
                // zoom in toward tap position
                var oldScale = this.scale
                    ,newScale = this.baseScale*4
                    ,originViewportX = ev ? (ev.pageX - this.element.getX()) : 0
                    ,originViewportY = ev ? (ev.pageY - this.element.getY()) : 0
                    ,originScaledImgX = originViewportX + scroller.position.x - this.translateX
                    ,originScaledImgY = originViewportY + scroller.position.y - this.translateY
                    ,originReScaledImgX = originScaledImgX * (newScale / oldScale)
                    ,originReScaledImgY = originScaledImgY * (newScale / oldScale);
                
                this.scale = newScale;
                
                //smoothes the transition
                setTimeout(function(){
                    that.setTranslation(originViewportX - originReScaledImgX, originViewportY - originReScaledImgY);
                    // reset origin and update transform with new translation
                    that.applyTransform();
    
    
                    // adjust scroll container
                    that.adjustScroller();
                    
                    // force repaint to solve occasional iOS rendering delay
                    Ext.repaint();
                },50)
                
            }
                
            
        }
        
        ,setOrigin: function(x, y) {
            this.imgEl.dom.style.webkitTransformOrigin = x+'px '+y+'px';
        }
        
        ,setTranslation:  function(translateX, translateY) {
            this.translateX = translateX;
            this.translateY = translateY;
                
            // transfer negative translations to scroll offset
            this.scrollX = this.scrollY = 0;
            
            if(this.translateX < 0)
            {
                this.scrollX = this.translateX;
                this.translateX = 0;
            }
            if(this.translateY < 0)
            {
                this.scrollY = this.translateY;
                this.translateY = 0;
            }
        }
        
        ,resetZoom:function(){
            //Resize to init size like ios
            this.scale = this.baseScale;
            
            this.setTranslation(this.translateBaseX, this.translateBaseY);
            
            // reset origin and update transform with new translation
            this.setOrigin(0, 0);
            this.applyTransform();
    
    
            // adjust scroll container
            this.adjustScroller();
            
        }
        ,rotate:function(){
            // get viewport size
            this.viewportWidth = this.parent.element.getWidth() ||this.viewportWidth || this.getWidth();
            this.viewportHeight = this.parent.element.getHeight() || this.viewportHeight || this.getHeight();
    
    
            // grab image size
            this.imgWidth = this.imgEl.dom.width
            this.imgHeight = this.imgEl.dom.height;
                    
            // calculate and apply initial scale to fit image to screen
            if(this.getResizeOnLoad()){
                this.scale = this.baseScale = Math.min(this.viewportWidth/this.imgWidth, this.viewportHeight/this.imgHeight);
                this.setMaxScale(this.scale*4);
            }else{
                this.scale = this.baseScale = 1;
            }
            
            // set initial translation to center
            this.translateX = this.translateBaseX = (this.viewportWidth - this.baseScale * this.imgWidth) / 2;
            this.translateY = this.translateBaseY = (this.viewportHeight - this.baseScale * this.imgHeight) / 2;
            
            // apply initial scale and translation
            this.applyTransform();
            
            // initialize scroller configuration
            this.adjustScroller();
             
        }
    
    
        ,applyTransform: function() {
            var fixedX = Ext.Number.toFixed(this.translateX,5)
                ,fixedY = Ext.Number.toFixed(this.translateY,5)
                ,fixedScale = Ext.Number.toFixed(this.scale, 8);
            
            if(Ext.os.is.Android)
            {
                this.imgEl.dom.style.webkitTransform = 
                    //'translate('+fixedX+'px, '+fixedY+'px)'
                    //+' scale('+fixedScale+','+fixedScale+')';
                    'matrix('+fixedScale+',0,0,'+fixedScale+','+fixedX+','+fixedY+')'
            }
            else
            {
                this.imgEl.dom.style.webkitTransform =
                    'translate3d('+fixedX+'px, '+fixedY+'px, 0)'
                    +' scale3d('+fixedScale+','+fixedScale+',1)';
            }
        }
    
    
    
    
        ,adjustScroller: function() {
            var scroller = this.getScrollable().getScroller();    
            
            // disable scrolling if zoomed out completely, else enable it
            if(this.scale == this.baseScale)
                scroller.setDisabled(true);
            else
                scroller.setDisabled(false);
            
            // size container to final image size
            var boundWidth = Math.max(this.imgWidth * this.scale, this.viewportWidth);
            var boundHeight = Math.max(this.imgHeight * this.scale, this.viewportHeight);
    
    
            this.figEl.setStyle({
                width: boundWidth + 'px'
                ,height: boundHeight + 'px'
            });
            
            // update scroller to new content size
            scroller.refresh();
    
    
            // apply scroll
            var x = 0;
            if(this.scrollX){
                x = this.scrollX
            }
            var y = 0;
            if(this.scrollY){
                y = this.scrollY
            }
            scroller.scrollTo(x*-1,y*-1)
        }        
        });
    thanks to Armode for the help

    I was tested in iOS 5.0, 5.1 with ipad 1,2,3 and with galaxy tab 8.9 with android 3.2
    works fine.

  2. #2
    Sencha User
    Join Date
    Feb 2012
    Location
    Ireland
    Posts
    6
    Vote Rating
    0
    Gaelmart is on a distinguished road

      0  

    Default


    Well done lads.
    Double click on android 2.3 works great, best we can hope for.
    Did someone mention adding 'zoomIn/zoomOut Buttons for Android' as 2.3 is 95% of market.

    Gary

  3. #3
    Sencha User
    Join Date
    Apr 2012
    Posts
    2
    Vote Rating
    0
    sebayo is on a distinguished road

      0  

    Default


    Hi
    Perdiga great job!
    i have some problems when i compile an apk using phonegap. i´m testing in a motorola zoom tablet with the last android, the problems are:
    flicking when i pinch the image, sometimes disapear and appears again zoomed,
    sometimes i can´t swipe when the image it´s zoomed.
    thanks for this great example

  4. #4
    Sencha User armode's Avatar
    Join Date
    Nov 2011
    Location
    Germany / Darmstadt
    Posts
    64
    Vote Rating
    4
    armode is on a distinguished road

      0  

    Default


    Quote Originally Posted by sebayo View Post
    flicking when i pinch the image, sometimes disapear and appears again zoomed,
    sometimes i can´t swipe when the image it´s zoomed.
    I had simular problems and think, that the problem is to listen to the "resize" event. If I recognize the "flicking-issue", the resize event is fired permantly. Maybe we have to listen to the orientationchange event from Viewport?

  5. #5
    Sencha User armode's Avatar
    Join Date
    Nov 2011
    Location
    Germany / Darmstadt
    Posts
    64
    Vote Rating
    4
    armode is on a distinguished road

      0  

    Default


    Quote Originally Posted by armode View Post
    Maybe we have to listen to the orientationchange event from Viewport?
    Adding a listener to the Viewport for example in line 70 seems to solve that flicking-issue:

    Code:
    Ext.Viewport.on('orientationchange', this.rotate, this, {buffer: 50 });
    The resize event is maybe not the best choice, see ST API:
    "Important note: For the best performance on mobile devices, use this only when you absolutely need to monitor a Component's size.
    Note: This event is not available to be used with event delegation. Instead 'resize' only fires if you explicily add at least one listener to it, due to performance reason."

  6. #6
    Sencha User
    Join Date
    Apr 2012
    Posts
    2
    Vote Rating
    0
    sebayo is on a distinguished road

      0  

    Default


    Quote Originally Posted by armode View Post
    Adding a listener to the Viewport for example in line 70 seems to solve that flicking-issue:
    Works better but when you pinch in the first place the image flickering and try to change it´s scale, you can see the rectangle changing positions, then works for moments but the zooming isn't fluid.

  7. #7
    Sencha User
    Join Date
    Dec 2011
    Location
    Brazil
    Posts
    106
    Vote Rating
    0
    Perdiga is an unknown quantity at this point

      0  

    Default


    Quote Originally Posted by armode View Post
    Adding a listener to the Viewport for example in line 70 seems to solve that flicking-issue:

    Code:
    Ext.Viewport.on('orientationchange', this.rotate, this, {buffer: 50 });
    The resize event is maybe not the best choice, see ST API:
    "Important note: For the best performance on mobile devices, use this only when you absolutely need to monitor a Component's size.
    Note: This event is not available to be used with event delegation. Instead 'resize' only fires if you explicily add at least one listener to it, due to performance reason."
    thanks ,I already added this in code

    Well done lads.
    Double click on android 2.3 works great, best we can hope for.
    Did someone mention adding 'zoomIn/zoomOut Buttons for Android' as 2.3 is 95% of market.

    Gary
    you can use doubletap,

  8. #8
    Sencha User SunboX's Avatar
    Join Date
    Mar 2010
    Posts
    238
    Vote Rating
    28
    SunboX has a spectacular aura about SunboX has a spectacular aura about

      0  

    Default


    here´s a nice SlideShare from one of Flickr´s mobile developers on native zoomin into images:

    Quote: "Why you can’t use native Pinch to Zoom"
    http://www.slideshare.net/ysaw/creat...-interfaces/31

    greetings Sunny

  9. #9
    Sencha User SunboX's Avatar
    Join Date
    Mar 2010
    Posts
    238
    Vote Rating
    28
    SunboX has a spectacular aura about SunboX has a spectacular aura about

      0  

    Default


    and the corresponding video presentation:
    http://www.youtube.com/watch?v=lcD9CF0bxyk

    greetings Sunny