PDA

View Full Version : Is it possible to pinch and zoom a panel or a picture in the html of a panel?



achapman
6 Aug 2010, 6:42 AM
I tried using the pinch example, but I am pretty new to javascript and I am having some trouble trying to get pinch to work on a panel with a picture in its html.

Does anyone have any examples that could help me out?

Thanks.

achapman
10 Aug 2010, 4:26 AM
Anyone?

evant
10 Aug 2010, 5:38 AM
Ext.setup({
onReady: function(){
var img = Ext.getBody().createChild({
tag: 'img',
src: 'http://www.sencha.com/assets/images/logo-sencha-sm.png',
style: 'width: 400px; height: 400px;'
});

img.on('pinch', function(e){
var dimensions = img.getSize(),
factor = 5;
if(e.deltaScale < 0){
factor *= -1;
}
img.setSize(dimensions.width + factor, dimensions.height + factor);
});
}
});

achapman
10 Aug 2010, 6:35 AM
I got that code working, but it places the image behind everything in my app. Is there a way to add the image to only a specific panel?

evant
10 Aug 2010, 6:38 AM
It was only intended to be an example, just grab the appropriate image element, for example:



myPanel.body.child('img.foo');

achapman
10 Aug 2010, 6:44 AM
Ok, gotcha. Thanks.

achapman
10 Aug 2010, 9:20 AM
Sorry, I am starting to feel really stupid about this, but the image I have is being added to a panel dynamically by using an html img tag. I can't seem to get the myPanel.body.child('img.foo'); code to see it.

I tried creating an Image object with the picture and tried to set the pinch event, but I get an undefined error.

So basically, I have a panel, mypanel, that on a button click I add image.png to the panel


mypanel.add({
html: '<img src="image.png"></img>'
});


And I want to be able to pinch and scroll this image. Should I be doing this a different way?

evant
10 Aug 2010, 3:52 PM
Are you familiar with css selectors? Basically what the above code was doing was finding an image in the body of the panel with a class name of foo.

With your code:



mypanel.body.child('img').on(....);

achapman
11 Aug 2010, 4:40 AM
When that line of code runs, I get an 'undefined' error. I guess it can't find the img in the panel. I tried this code before and after the mypanel.doLayout() line, but it fails either way.

Here is simple version of what I am doing. The pinch code is in the makeGraph function.


Ext.setup({
tabletStartupScreen: 'tablet_startup.png',
phoneStartupScreen: 'phone_startup.png',
icon: 'icon.png',
glossOnIcon: false,
onReady: function(){

var getWellData = function(){

if (Ext.getCmp('wellsList').getValue() == 0) {
alert("Please select a well");
}
else {
var carousel = Ext.getCmp('carousel'),
temp = '0~0~1~2~3',
seperatedString = temp.split("~"),
i = 2,
len = seperatedString.length,
myAnswer,
card;

carousel.removeAll();

if (len < 2) { //bad return
carousel.add({
title: 'Card 1',
html: 'Error',
scroll: 'vertical'
});
} else {
for (; i < len; i++) {
myAnswer = seperatedString[i];

if (i > 2) {
carousel.add({
dockedItems: [{
dock: 'top',
xtype: 'toolbar',
ui: 'dark',
items: [{
text: 'Back',
ui: 'back',
maxWidth: '80',
handler: backToMain
},{
xtype: 'spacer'
},{
text: 'Graph',
handler: showGraph
}],
}],
id: 'card' + i,
title: 'Card ' + i,
scroll: 'vertical',
html: "<ul><li>" + myAnswer + "</li></ul>"
});
} else {
carousel.add({
dockedItems: [{
dock: 'top',
xtype: 'toolbar',
ui: 'dark',
items: [{
text: 'Back',
ui: 'back',
handler: backToMain
}],
}],
title: 'Card ' + i,
scroll: 'vertical',
html: "<ul><li>" + myAnswer + "</li></ul>"
});
}
}
}

Ext.getCmp('mainNav').setCard(1);
carousel.doLayout();
carousel.setCard(0);
}
};

var makeGraph = function(tableNum) {
try {
var graph = Ext.getCmp('graph');

graph.removeAll();

graph.add({
title: 'Graph',
html: "<img alt='loading graph' width=100% style='padding:10px 10px 10px 10px;' src='chart.png'></img><br />"
});

graph.body.child('img').on({
pinchstart : function(e, t) {
this.startScale = this.scale;
},

pinch : function(e, t) {
this.scale = e.scale * this.startScale;
t.style.webkitTransform = 'scale(' + this.scale + ')';
},

scope: {scale: 1}
});

graph.doLayout();

Ext.getCmp('mainNav').setCard(2);
}
catch (err)
{
alert("Error graphing: " + err.description);
}
};

var backToMain = function(){
mainNav.setCard(0);
};

var backToData = function(){
mainNav.setCard(1);
};

var showGraph = function() {
//get the current card number
var temp = Ext.getCmp('carousel').getActiveItem().id.replace("card","");
makeGraph(temp - 2);
};

var mainNav = new Ext.Panel({
id: 'mainNav',
fullscreen: true,
layout: 'card',
items: [{
xtype: 'form',
items: [{
xtype: 'fieldset',
title: 'Select an Item',
items: [{
xtype: 'select',
id: 'wellsList',
name: 'wellsList',
options: [{
text: "test", value: "1"
}]
}]
}, {
xtype: 'button',
text: 'Submit',
maxHeight: '40',
ui: 'round',
handler: getWellData
}]
}, {
xtype: 'carousel',
id: 'carousel'
}, {
xtype: 'panel',
scroll: 'vertical',
dockedItems: [{
title: 'Emit',
dock: 'top',
xtype: 'toolbar',
ui: 'dark',
items: [{
text: 'Back',
ui: 'back',
handler: backToData
}],
}],
items: [{
xtype: 'panel',
id: 'graph',
html: "<div id='picture'></div>"
}]
}]
});
}
});

achapman
12 Aug 2010, 10:29 AM
Any ideas?

evant
12 Aug 2010, 5:28 PM
Well, as the error says, it can't find the image. This is because you try and bind the event ~before~ you call doLayout, but it hasn't been laid out in the panel yet. doLayout forces child items to render, so you just need to change the order.

achapman
13 Aug 2010, 3:51 AM
I tried this code before and after the doLayout() line, but it fails either way.

The following code fails as well. Try to run it and you'll see what I mean.


Ext.setup({
tabletStartupScreen: 'tablet_startup.png',
phoneStartupScreen: 'phone_startup.png',
icon: 'icon.png',
glossOnIcon: false,
onReady: function(){

var getWellData = function(){

if (Ext.getCmp('wellsList').getValue() == 0) {
alert("Please select a well");
}
else {
var carousel = Ext.getCmp('carousel'),
temp = '0~0~1~2~3',
seperatedString = temp.split("~"),
i = 2,
len = seperatedString.length,
myAnswer,
card;

carousel.removeAll();

if (len < 2) { //bad return
carousel.add({
title: 'Card 1',
html: 'Error',
scroll: 'vertical'
});
} else {
for (; i < len; i++) {
myAnswer = seperatedString[i];

if (i > 2) {
carousel.add({
dockedItems: [{
dock: 'top',
xtype: 'toolbar',
ui: 'dark',
items: [{
text: 'Back',
ui: 'back',
maxWidth: '80',
handler: backToMain
},{
xtype: 'spacer'
},{
text: 'Graph',
handler: showGraph
}],
}],
id: 'card' + i,
title: 'Card ' + i,
scroll: 'vertical',
html: "<ul><li>" + myAnswer + "</li></ul>"
});
} else {
carousel.add({
dockedItems: [{
dock: 'top',
xtype: 'toolbar',
ui: 'dark',
items: [{
text: 'Back',
ui: 'back',
handler: backToMain
}],
}],
title: 'Card ' + i,
scroll: 'vertical',
html: "<ul><li>" + myAnswer + "</li></ul>"
});
}
}
}

Ext.getCmp('mainNav').setCard(1);
carousel.doLayout();
carousel.setCard(0);
}
};

var makeGraph = function(tableNum) {
try {
var graph = Ext.getCmp('graph');

graph.removeAll();

graph.add({
title: 'Graph',
html: "<img alt='loading graph' width=100% style='padding:10px 10px 10px 10px;' src='chart.png'></img><br />"
});

graph.doLayout();

graph.body.child('img').on({
pinchstart : function(e, t) {
this.startScale = this.scale;
},

pinch : function(e, t) {
this.scale = e.scale * this.startScale;
t.style.webkitTransform = 'scale(' + this.scale + ')';
},

scope: {scale: 1}
});

Ext.getCmp('mainNav').setCard(2);
}
catch (err)
{
alert("Error graphing: " + err.description);
}
};

var backToMain = function(){
mainNav.setCard(0);
};

var backToData = function(){
mainNav.setCard(1);
};

var showGraph = function() {
//get the current card number
var temp = Ext.getCmp('carousel').getActiveItem().id.replace("card","");
makeGraph(temp - 2);
};

var mainNav = new Ext.Panel({
id: 'mainNav',
fullscreen: true,
layout: 'card',
items: [{
xtype: 'form',
items: [{
xtype: 'fieldset',
title: 'Select an Item',
items: [{
xtype: 'select',
id: 'wellsList',
name: 'wellsList',
options: [{
text: "test", value: "1"
}]
}]
}, {
xtype: 'button',
text: 'Submit',
maxHeight: '40',
ui: 'round',
handler: getWellData
}]
}, {
xtype: 'carousel',
id: 'carousel'
}, {
xtype: 'panel',
scroll: 'vertical',
dockedItems: [{
title: 'Emit',
dock: 'top',
xtype: 'toolbar',
ui: 'dark',
items: [{
text: 'Back',
ui: 'back',
handler: backToData
}],
}],
items: [{
xtype: 'panel',
id: 'graph',
html: "<div id='picture'></div>"
}]
}]
});
}
});

evant
13 Aug 2010, 7:12 AM
Ext.setup({
onReady: function(){
Ext.setup({
tabletStartupScreen: 'tablet_startup.png',
phoneStartupScreen: 'phone_startup.png',
icon: 'icon.png',
glossOnIcon: false,
onReady: function(){

var getWellData = function(){

if (Ext.getCmp('wellsList').getValue() == 0) {
alert("Please select a well");
} else {
var carousel = Ext.getCmp('carousel'), temp = '0~0~1~2~3', seperatedString = temp.split("~"), i = 2, len = seperatedString.length, myAnswer, card;

carousel.removeAll();

if (len < 2) { //bad return
carousel.add({
title: 'Card 1',
html: 'Error',
scroll: 'vertical'
});
} else {
for (; i < len; i++) {
myAnswer = seperatedString[i];

if (i > 2) {
carousel.add({
dockedItems: [{
dock: 'top',
xtype: 'toolbar',
ui: 'dark',
items: [{
text: 'Back',
ui: 'back',
maxWidth: '80',
handler: backToMain
}, {
xtype: 'spacer'
}, {
text: 'Graph',
handler: showGraph
}],
}],
id: 'card' + i,
title: 'Card ' + i,
scroll: 'vertical',
html: "<ul><li>" + myAnswer + "</li></ul>"
});
} else {
carousel.add({
dockedItems: [{
dock: 'top',
xtype: 'toolbar',
ui: 'dark',
items: [{
text: 'Back',
ui: 'back',
handler: backToMain
}],
}],
title: 'Card ' + i,
scroll: 'vertical',
html: "<ul><li>" + myAnswer + "</li></ul>"
});
}
}
}

Ext.getCmp('mainNav').setCard(1);
carousel.doLayout();
carousel.setCard(0);
}
};

var makeGraph = function(tableNum){
Ext.getCmp('mainNav').setCard(2);
var graph = Ext.getCmp('graph');
graph.add({
title: 'Graph',
html: '<img alt="loading graph" width="100%" style="padding:10px 10px 10px 10px;" src="chart.png"></img><br />'
});

graph.doLayout();

graph.body.select('img').on({
pinchstart: function(e, t){
this.startScale = this.scale;
},

pinch: function(e, t){
this.scale = e.scale * this.startScale;
t.style.webkitTransform = 'scale(' + this.scale + ')';
},

scope: {
scale: 1
}
});
};

var backToMain = function(){
mainNav.setCard(0);
};

var backToData = function(){
mainNav.setCard(1);
};

var showGraph = function(){
//get the current card number
var temp = Ext.getCmp('carousel').getActiveItem().id.replace("card", "");
makeGraph(temp - 2);
};

var mainNav = new Ext.Panel({
id: 'mainNav',
fullscreen: true,
layout: 'card',
items: [{
xtype: 'form',
items: [{
xtype: 'fieldset',
title: 'Select an Item',
items: [{
xtype: 'select',
id: 'wellsList',
name: 'wellsList',
options: [{
text: "test",
value: "1"
}]
}]
}, {
xtype: 'button',
text: 'Submit',
maxHeight: '40',
ui: 'round',
handler: getWellData
}]
}, {
xtype: 'carousel',
id: 'carousel'
}, {
xtype: 'panel',
scroll: 'vertical',
dockedItems: [{
title: 'Emit',
dock: 'top',
xtype: 'toolbar',
ui: 'dark',
items: [{
text: 'Back',
ui: 'back',
handler: backToData
}],
}],
items: [{
xtype: 'panel',
id: 'graph'
}]
}]
});
}
});
}
});

achapman
13 Aug 2010, 8:57 AM
So it was graph.body.select('img') instead of graph.body.child('img').

Thanks.

So now that it zooms when it is pinched, how do I keep it from snapping back to center when it scrolls?

I changed the scroll to 'both'.

achapman
16 Aug 2010, 6:05 AM
?

evant
16 Aug 2010, 6:08 AM
Keep what from snapping back to center? When what scrolls? Remember I can't see your app and I don't really know how it works, so you need to be fairly explicit in your descriptions.

You'll likely have much better luck if I can drop something in and run it locally.

achapman
16 Aug 2010, 6:26 AM
Sorry, I was going by the code you posted. Once the image, chart.png, that has the pinch code is zoomed to the point where it no longer fits on the screen, when I scroll it (the panel it is in has the scroll set to both), the image bounces back to the center instead of just scrolling to the position I want to see.


Ext.setup({
onReady: function(){
Ext.setup({
tabletStartupScreen: 'tablet_startup.png',
phoneStartupScreen: 'phone_startup.png',
icon: 'icon.png',
glossOnIcon: false,
onReady: function(){

var getWellData = function(){

if (Ext.getCmp('wellsList').getValue() == 0) {
alert("Please select a well");
} else {
var carousel = Ext.getCmp('carousel'), temp = '0~0~1~2~3', seperatedString = temp.split("~"), i = 2, len = seperatedString.length, myAnswer, card;

carousel.removeAll();

if (len < 2) { //bad return
carousel.add({
title: 'Card 1',
html: 'Error',
scroll: 'vertical'
});
} else {
for (; i < len; i++) {
myAnswer = seperatedString[i];

if (i > 2) {
carousel.add({
dockedItems: [{
dock: 'top',
xtype: 'toolbar',
ui: 'dark',
items: [{
text: 'Back',
ui: 'back',
maxWidth: '80',
handler: backToMain
}, {
xtype: 'spacer'
}, {
text: 'Graph',
handler: showGraph
}],
}],
id: 'card' + i,
title: 'Card ' + i,
scroll: 'vertical',
html: "<ul><li>" + myAnswer + "</li></ul>"
});
} else {
carousel.add({
dockedItems: [{
dock: 'top',
xtype: 'toolbar',
ui: 'dark',
items: [{
text: 'Back',
ui: 'back',
handler: backToMain
}],
}],
title: 'Card ' + i,
scroll: 'vertical',
html: "<ul><li>" + myAnswer + "</li></ul>"
});
}
}
}

Ext.getCmp('mainNav').setCard(1);
carousel.doLayout();
carousel.setCard(0);
}
};

var makeGraph = function(tableNum){
Ext.getCmp('mainNav').setCard(2);
var graph = Ext.getCmp('graph');

graph.removeAll();

graph.add({
title: 'Graph',
html: '<img alt="loading graph" width="100%" style="padding:10px 10px 10px 10px;" src="chart.png"></img><br />'
});

graph.doLayout();

graph.body.select('img').on({
pinchstart: function(e, t){
this.startScale = this.scale;
},

pinch: function(e, t){
this.scale = e.scale * this.startScale;
t.style.webkitTransform = 'scale(' + this.scale + ')';
},

scope: {
scale: 1
}
});
};

var backToMain = function(){
mainNav.setCard(0);
};

var backToData = function(){
mainNav.setCard(1);
};

var showGraph = function(){
//get the current card number
var temp = Ext.getCmp('carousel').getActiveItem().id.replace("card", "");
makeGraph(temp - 2);
};

var mainNav = new Ext.Panel({
id: 'mainNav',
fullscreen: true,
layout: 'card',
items: [{
xtype: 'form',
items: [{
xtype: 'fieldset',
title: 'Select an Item',
items: [{
xtype: 'select',
id: 'wellsList',
name: 'wellsList',
options: [{
text: "test",
value: "1"
}]
}]
}, {
xtype: 'button',
text: 'Submit',
maxHeight: '40',
ui: 'round',
handler: getWellData
}]
}, {
xtype: 'carousel',
id: 'carousel'
}, {
xtype: 'panel',
scroll: 'both',
dockedItems: [{
title: 'Emit',
dock: 'top',
xtype: 'toolbar',
ui: 'dark',
items: [{
text: 'Back',
ui: 'back',
handler: backToData
}],
}],
items: [{
xtype: 'panel',
id: 'graph'
}]
}]
});
}
});
}
});

achapman
17 Aug 2010, 10:15 AM
bump

achapman
24 Aug 2010, 4:06 AM
Still having trouble with this. Any clues?

Kask
25 Aug 2010, 12:26 AM
Having the same problem, and got a partial solution. Still trying to figure out some way to center the image when zooming out, so it doesnt go out of bounds.

Note the change to scope. Just add scale: 1 outside on the object.

Also changed to scale3d for a smoother zoom.



graph.body.select('img').on({
pinchstart: function(e, t){
this.startScale = this.scale;
this.startHeight = t.height;
this.startWidth = t.width;
},

pinch: function(e, t){
this.scale = e.scale * this.startScale;

// scale image
t.style.webkitTransform = 'scale3d(' +this.scale + ', ' +this.scale + ', ' +this.scale + ')';

// scale container
var p = t.parentNode;
p.style.height = (this.startHeight * this.scale) + 'px';
p.style.width = (this.startWidth * this.scale) + 'px';

// re-position image in container
t.style.marginTop = (((this.startHeight * this.scale) - t.height) / 2) + 'px';
t.style.marginLeft = (((this.startWidth * this.scale) - t.width) / 2) + 'px';
},

scope: this
});

mliga
31 Aug 2010, 6:36 AM
I almost there (want to pinch & zoom on an image into a scrollable panel,and applying your code saved my life), but sometimes I have weird image positioning (out of bound, ecc...).
Wouldn't be better to scroll the panel (using scroller.scrollTo), instead of setting marginLeft and marginTop on the image?

Kask
31 Aug 2010, 7:04 AM
You are correct on that note, and im actually trying to implement something like that myself. I've having trouble tho with transforming the positions so that i know where to scroll to.

Tthe code im currently using is below, and i've added a <div /> around the image. Dont really know if I needed it tho :"> This is copy/paste of dev code trying to get around the problem :)



pinchstart: function (e, t) {
this.startScale = this.scale;
this.startHeight = t.clientHeight;
this.startWidth = t.clientWidth;

this._p = t.parentNode;
this._pp = this._p.parentNode;
this._ppp = this._pp.parentNode;
},

pinch: function (e, t) {
this.scale = e.scale * this.startScale;

var containerHeight = (this.startHeight * this.scale), containerWidth = (this.startWidth * this.scale);
var translateX = Math.max((this._ppp.width() - containerWidth) / 2, 0);
var translateY = Math.max((this._ppp.height() - containerHeight) / 2, 0);

// scale container
var p = t.parentNode;
p.style.minHeight = containerHeight + 'px';
p.style.height = containerHeight + 'px';
p.style.minWidth = containerWidth + 'px';
p.style.width = containerWidth + 'px';

// scale image
var transformScale = 'scale3d(' + this.scale + ', ' + this.scale + ', ' + this.scale + ')';
t.style.webkitTransformOrigin = '0 0';
t.style.webkitTransform = transformScale;

transformTranslate = 'translate(' + translateX + 'px, ' + translateY + 'px)';
this._p.style.webkitTransform = transformTranslate;
}

mliga
31 Aug 2010, 8:54 AM
Sorry, did't get this _p, _pp, _ppp thing from your code ... maybe if I can have a look at the full example ...
Here is my attempt with scrollTo.
BTW, I found that css scale transform doesn't change the width/height property of the image, so i'm trying for now to change these directly: so far:


pinch : function(e, t) {
// Save for later
oldScale = this.scale;
// I want scale to range between 1 and 2
this.scale = Math.min(2,Math.max(1,e.scale * this.startScale));

// Changing width/height directly
t.style.width = Math.floor(parseInt(this.startW)*this.scale)+"px";
t.style.height = Math.floor(parseInt(this.startH)*this.scale)+"px";
// This adjust scrollbars
Ext.getCmp("pagina").doLayout();

// Actual scrolling is negative
actualScrolling = Ext.getCmp("pagina").scroller.offset;
gapX = -actualScrolling.x + Math.floor((parseInt(this.startW)*this.scale-this.startW)/2);
gapY = -actualScrolling.y + Math.floor((parseInt(this.startH)*this.scale-this.startH)/2);
Ext.getCmp("pagina").scroller.scrollTo({ x: gapX, y: gapY },0);
}


far from perfect or fluid, but better than before ...

mliga
31 Aug 2010, 12:20 PM
Stupid me!
I just realized that scrollTo gaps should be calculated against viewport size (the scrolling pane) and not the image size...
Now it's late but will post a patch tomorrow.

Kask
31 Aug 2010, 10:23 PM
Looking forward to your patch :D ...

_p, _pp and _ppp is just shorthand for the parent elements of the image.

_p is the div around the image, im using this for transfer/center the image instead of the scroller div, might mess things up.
_pp is the scroller div. auto-generated to handle the scrolling.
_ppp is the "viewport" ... the floating fullscreen panel im using to display the image. Im using width and height from that in case I want to specifiy the window size instead of using the screen size.

mliga
31 Aug 2010, 11:59 PM
Here it is:



pinch : function(e, t) {
// Cache container
cntnr = Ext.getCmp("pagina");

// Save for later (if needed)
oldScale = this.scale;
// Want scale to range between 1 and 2
this.scale = Math.min(2,Math.max(1,e.scale * this.startScale));

// Here css transition would be better ...
//t.style.webkitTransform = 'scale3d(' +this.scale + ', ' +this.scale + ', ' +this.scale + ')';
t.style.width = Math.floor(parseInt(this.startW)*this.scale)+"px";
t.style.height = Math.floor(parseInt(this.startH)*this.scale)+"px";
// Readjust scroll
cntnr.doLayout();

// Actual scrolling (IS NEGATIVE)
actualScrolling = cntnr.scroller.offset;
gapX = -actualScrolling.x + Math.floor((cntnr.getWidth()*(this.scale-1))/2);
gapY = -actualScrolling.y + Math.floor((cntnr.getHeight()*(this.scale-1))/2);
cntnr.scroller.scrollTo({ x: gapX, y: gapY },0);
}


A little better, but I'm still not satisfied ...
First of all, this should be converted to use css transform to be more fluid, but I know nothing about these.
Apart from that, the real problem is that zooming is always centered, so the user cannot really pinch on a detail of the image, and have it zoomed without going out of bound; trascurable on iphone small screens, a real problem on ipad.
I've seen a previous post from kask about getting pinch coordinates. This is the way to go to implement it properly.

The lazy guy in me just hopes we'll have a zoom: true option on panels in a future version, optionally with a zoomRange: { min: 1, max:5 }, along with the scroll property. Am I dreaming?I-|

achapman
1 Sep 2010, 6:06 AM
The lazy guy in me just hopes we'll have a zoom: true option on panels in a future version, optionally with a zoomRange: { min: 1, max:5 }, along with the scroll property. Am I dreaming?I-|

I am dreaming of the same thing. I can't believe it is so hard to get it to do something that mobile safari seems to do with pages by default.