-
5 Mar 2012 8:11 AM #31
Has anyone port the plugin to ST2?
I tried it by myself but was not successful so far...
-
7 Mar 2012 5:36 AM #32
Ok, I want to share my port-state to ST2!
I tried standard stuff like using the Ext.define for class defnition, etc
There's maybe a problem with the initialize function that was the initComponent in ST1...
I get no more errors or hints, but the componet still doesn't work!
ImageViewer.js:
Code:Ext.ns('Jarvus.mobile'); Ext.define('Jarvus.mobile.ImageViewer',{ extend: 'Ext.Container', config: { doubleTapScale: 1 ,maxScale: 1 ,loadingMask: true ,previewSrc: false ,imageSrc: false ,initOnActivate: false ,cls: 'imageBox' ,scroll: 'both' ,html: '<figure><img></figure>' } ,xtype: 'imageviewer' ,initialize: function() { /* * TypeError: 'undefined' is not an object (evaluating 'Jarvus.mobile.ImageViewer.superclass.initComponent.apply') */ // Jarvus.mobile.ImageViewer.superclass.initComponent.apply(this, arguments); if(this.initOnActivate) this.on('activate', this.initViewer, this, {delay: 10, single: true}); else this.on('afterrender', this.initViewer, this, {delay: 10, single: true}); } ,initViewer: function() { // disable scroller this.scroller.disable(); // mask image viewer if(this.loadingMask) this.el.mask(Ext.LoadingSpinner); // retrieve DOM els this.figEl = this.el.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.previewSrc) { this.el.setStyle({ backgroundImage: 'url('+this.previewSrc+')' ,backgroundPosition: 'center center' ,backgroundRepeat: 'no-repeat' ,webkitBackgroundSize: 'contain' }); } // attach event listeners this.mon(this.imgEl, { scope: this ,load: this.onImageLoad ,doubletap: this.onDoubleTap ,pinchstart: this.onImagePinchStart ,pinch: this.onImagePinch ,pinchend: this.onImagePinchEnd }); // load image if(this.imageSrc) this.loadImage(this.imageSrc); } ,loadImage: function(src) { if(this.imgEl) this.imgEl.dom.src = src; else this.imageSrc = src; } ,onImageLoad: function() { // get viewport size this.viewportWidth = this.viewportWidth || this.getWidth() || this.ownerCt.body.getWidth(); this.viewportHeight = this.viewportHeight || this.getHeight() || this.ownerCt.body.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.imgWidth > this.viewportWidth || this.imgHeight > this.viewportHeight) this.scale = this.baseScale = Math.min(this.viewportWidth/this.imgWidth, this.viewportHeight/this.imgHeight); 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.previewSrc) { this.el.setStyle({ backgroundImage: 'none' }); } if(this.loadingMask) this.el.unmask(); this.fireEvent('imageLoaded', this); } ,onImagePinchStart: function(ev) { // disable scrolling during pinch this.scroller.stopMomentumAnimation(); this.scroller.disable(); // store beginning scale this.startScale = this.scale; // calculate touch midpoint relative to image viewport this.originViewportX = (ev.touches[0].clientX + ev.touches[1].clientX) / 2 - this.el.getX(); this.originViewportY = (ev.touches[0].clientY + ev.touches[1].clientY) / 2 - this.el.getY(); // translate viewport origin to position on scaled image this.originScaledImgX = this.originViewportX - this.scroller.offset.x - this.translateX; this.originScaledImgY = this.originViewportY - this.scroller.offset.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.util.Numbers.constrain(ev.scale * this.startScale, this.baseScale, this.maxScale); this.applyTransform(); } ,onImagePinchEnd: function(ev) { // set new translation if(this.scale == this.baseScale) { // move to center this.setTranslation(this.translateBaseX, this.translateBaseY); } else { // 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) { if(!this.doubleTapScale) return false; // set scale and translation if(this.scale >= .9) { // zoom out to base view this.scale = this.baseScale; this.setTranslation(this.translateBaseX, this.translateBaseY); } else { // zoom in toward tap position var oldScale = this.scale ,newScale = 1 ,originViewportX = ev ? (ev.pageX - this.el.getX()) : 0 ,originViewportY = ev ? (ev.pageY - this.el.getY()) : 0 ,originScaledImgX = originViewportX - this.scroller.offset.x - this.translateX ,originScaledImgY = originViewportY - this.scroller.offset.y - this.translateY ,originReScaledImgX = originScaledImgX * (newScale / oldScale) ,originReScaledImgY = originScaledImgY * (newScale / oldScale); this.scale = newScale; this.setTranslation(originViewportX - originReScaledImgX, originViewportY - originReScaledImgY); } // 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(); } ,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; } } ,applyTransform: function() { var fixedX = Ext.util.Numbers.toFixed(this.translateX,5) ,fixedY = Ext.util.Numbers.toFixed(this.translateY,5) ,fixedScale = Ext.util.Numbers.toFixed(this.scale, 8); if(Ext.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() { // disable scrolling if zoomed out completely, else enable it if(this.scale == this.baseScale) this.scroller.disable(); else this.scroller.enable(); // 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 this.scroller.updateBoundary(); // apply scroll this.scroller.setOffset({ x: this.scrollX || 0 ,y: this.scrollY || 0 }); } });
example-image.html:
any help is appreciated!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.Container', xtype: 'my-viewport', config: { fullscreen: true, layout: 'fit', items: [ { xtype: 'imageviewer', style: { backgroundColor: '#000' }, imageSrc: 'http://placekitten.com/2048/1024' /* if you already have a low resolution thumbnail loaded, supply its URL via previewSrc to provide a backdrop for loading the full-res version */ //,previewSrc: 'http://placekitten.com/20/20' } ] }, }); Ext.create('WPMobile.Viewport', { id: 'viewport' }); } }); </script> </head> <body></body> </html>​
P.S:
there's another thread about pinching images with ST2, but there's no smoothness in pinching, etc
http://www.sencha.com/forum/showthre...h-2.0&p=733212
-
7 Mar 2012 5:46 AM #33
@themightlychris:
I think I have noticed (by using your great plugin) that the images can be zoomed in until the original size of the image is reached... am I correct?
It would be great to be able so scale it 'endlleslly'
thanks
-
20 Apr 2012 4:34 AM #34
99% progress on porting ImageViewer to ST2
99% progress on porting ImageViewer to ST2
Hello everyone,
I just wanted to share my progress in porting the ImageViewer.js to ST2.
Overall it works fine, images are displayes, doubleclick works fine, just one hopefully last issue on pinching more then one times...
I had to fire the load event by myself or was not able to listen to the right point, anyway it works ;-)
Something seems to changed with the scroller coordinates!? To get the doubleclick zoomIn working, I had to convert the scroller coordinates from negative to positive numbers (see adjustScroller)
Maybe someone could have a look at the pinching math? If you pinch to zoom the first time everthing is fine, but the second pinch moves the image und resizes it the wrong way...
Here's the ImageViwer.js code
Code:Ext.define('Jarvus.mobile.ImageViewer',{ extend: 'Ext.Container', config: { doubleTapScale: 1 ,maxScale: 1 ,loadingMask: true ,previewSrc: false ,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.config.__proto__.loadingMask) 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._previewSrc) { this.element.setStyle({ backgroundImage: 'url('+this.previewSrc+')' ,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._imageSrc) this.loadImage(this._imageSrc); } ,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.imageSrc = 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.imgWidth > this.viewportWidth || this.imgHeight > this.viewportHeight) this.scale = this.baseScale = Math.min(this.viewportWidth/this.imgWidth, this.viewportHeight/this.imgHeight); 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._previewSrc) { this.element.setStyle({ backgroundImage: 'none' }); } if(this.config.__proto__.loadingMask) this.setMasked(false); this.fireEvent('imageLoaded', this); } ,onImagePinchStart: function(ev) { //TODO Pinching math after pinching the first time; strange resizing and moving... var scroller = this.getScrollable().getScroller(); // disable scrolling during pinch // TODO ? reactivate: scroller.stopMomentumAnimation(); 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, this.maxScale); this.applyTransform(); } ,onImagePinchEnd: function(ev) { // set new translation if(this.scale == this.baseScale) { // move to center this.setTranslation(this.translateBaseX, this.translateBaseY); } else { // 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 scroller = this.getScrollable().getScroller(); if(!this.config.__proto__.doubleTapScale) return false; // set scale and translation if(this.scale >= .9) { // zoom out to base view this.scale = this.baseScale; this.setTranslation(this.translateBaseX, this.translateBaseY); } else { // zoom in toward tap position var oldScale = this.scale ,newScale = 1 ,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; this.setTranslation(originViewportX - originReScaledImgX, originViewportY - originReScaledImgY); } // 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(); } ,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; } } ,applyTransform: function() { var fixedX = Ext.Number.toFixed(this.translateX,5) ,fixedY = Ext.Number.toFixed(this.translateY,5) ,fixedScale = Ext.Number.toFixed(this.scale, 8); console.log('Transform X: ' + fixedX + ' Y: ' + fixedY + ' Scale: ' + fixedScale); 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 } // TODO correct? scroller.scrollTo(x*-1,y*-1) } });
-
22 Apr 2012 7:04 AM #35
Does anyone have a online working example of this that I can try.
My client keeps showing me his android phone 2.3 and pinches in and out of any image he finds on any crap web page.
Then asks me why if after buying the software and spending 2 months developing an app which has one purpose- zoom his diagram images, I can't do it, despite his competitors all have the ability in, quote 'not this fancy html5 rubbish that's supposed to work on anything'. :-)
He's got a point.
I'm new to sencha but have all the database drill downs working etc but this is a show-stopper. I'll be back to regular html pages if its a no-go which is ridiculous if you read the sencha PR.
Users see an small image, their instinct is to pinch, full stop. If it can't do that, what's the point?.
Thanks
Gary
-
22 Apr 2012 11:57 AM #36
@gary re: pinch-zoom on android
On Android 2.3, the Android browser itself supports pinch-zooming but does not provide Javascript any access to the events. Sencha can't do anything about it, the blame for that ridiculous functionality gap belongs entirely in Google's hands.
The only way you could make use of pinch-zoom on Android 2.3 would be to open your images directly in a new browser window, or use PhoneGap/Cordova native wrappers and implement/find a plugin for overlaying a native image viewer. This plugin provides double-tap zoom in/out for Android -- that's the best we can do =/
@armode re: st2 port
awesome work armode! thanks for sharing, I'll give it a try and see if I can make any tweaks before adding it to the github repo
@gkatz re: zooming past original image size
The maxScale setting's default of 1 is the culprit, increase it to enable deeper zooming
-
22 Apr 2012 10:38 PM #37
-
23 Apr 2012 4:39 AM #38
armode very nice, I was doing the same
I found some bug in line 169
replace for thisCode:this.scale = Ext.Number.constrain(ev.scale * this.startScale, this.baseScale, this.maxScale);
with this maxScale will work fineCode:this.scale = Ext.Number.constrain(ev.scale * this.startScale, this.baseScale, this.getMaxScale());
in line 202
replace forCode:if(!this.config.__proto__.doubleTapScale)
its the same but more elegantCode:if(!this.getDoubleTapScale())
in line 30 replace
forCode:if(this.config.__proto__.loadingMask)
in line 74 replaceCode:if(this.getLoadingMask())
forCode:if(this._imageSrc)
in line 120 replaceCode:if(this.getImageSrc())
[CODE]if(this._previewSrc)/CODE]
for
in line 127 replaceCode:if(this.getPreviewSrc())
forCode:if(this.config.__proto__.loadingMask)
as to "scroller.stopMomentumAnimation();" we no longer need to use it because the scroller component already does this or use scroller.stopAnimation(); to ensureCode:if(this.getLoadingMask())
-
23 Apr 2012 5:22 AM #39
Ok, I have found the (self made?) mistake after a bit debugging...
It's not originViewport - scroller.position, it's originViewport + scroller.position after converting the scroller coordinates!
Another small issue was that maxScale was undefined, fixed now.
@Perdiga: oh didn't see your posting, will have a look...
Code:Ext.define('Jarvus.mobile.ImageViewer',{ extend: 'Ext.Container', config: { doubleTapScale: 1 ,maxScale: 1 ,loadingMask: true ,previewSrc: false ,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.imgWidth > this.viewportWidth || this.imgHeight > this.viewportHeight) this.scale = this.baseScale = Math.min(this.viewportWidth/this.imgWidth, this.viewportHeight/this.imgHeight); 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 // TODO ? reactivate: scroller.stopMomentumAnimation(); 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, 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 { // 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 scroller = this.getScrollable().getScroller(); if(!this.getDoubleTapScale()) return false; // set scale and translation if(this.scale >= .9) { // zoom out to base view this.scale = this.baseScale; this.setTranslation(this.translateBaseX, this.translateBaseY); } else { // zoom in toward tap position var oldScale = this.scale ,newScale = 1 ,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; this.setTranslation(originViewportX - originReScaledImgX, originViewportY - originReScaledImgY); } // 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(); } ,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; } } ,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) } });
-
23 Apr 2012 5:27 AM #40


Reply With Quote