PDA

View Full Version : Reengineered Image Carousel for Ext2.0



jerrybrown5
4 Mar 2008, 11:49 PM
I sorely needed an image carousel for my 2.x app so I started with the 1.x Carousel component from Matthew Hazlett. As Jack would say--two skipped meals and moderate amounts of caffeine later, it is working. The general look and feel remains the same from the 1.x app-- however even that can be customized through css.

Things it doesn't have (a. because I didn't need it for my projects -- b. I got lazy)
*Doesn't scroll vertically even though that would be pretty easy to do
*The image from the right comes in and leaves choppy, especially pronounced on larger images
*It doesn't have a scroll automatically mode
*It doesn't patch the beginning onto the end when you scroll to the end
*It doesn't handle re-positioning after render.
*The image wrap is not an ext container. I would have liked to allow the scrolling of non-images and then set up an xtype for viewing images but... my dog needed a walk. ;)

Things it does:
*It is written more like an Ext component with much less code and much more customizable. This means easier upgrades.
*It only display whole pictures like the YUI image carousel. Each arrow click scrolls to the next page of images.

New CSS Options


.x-carousel-inner {
border: 1px solid black;
margin-bottom:5px;
}
.x-carousel-handle-right {
background:gray url(handle-right.png) repeat-y;
float:right;
cursor:pointer;
width:10px
}
.x-carousel-handle-left {
background:gray url(handle-left.png) repeat-y;
float:left;
cursor:pointer;
width:10px
}


Here is a sample implementation.



{
xtype:'carousel',
wrapMarginY:3,
wrapMarginX:15,
imageHeight:48,
imageWidth:48,
width:300, /*auto width would be nice but not supported*/
listeners:{
selected: function(carousel, image, e, ele){
Ext.get("LargeImage").dom.innerHTML = '<img src="' + (image.fullSrc||image.src) + '" width=300 height=300>';
}
},
images:[ /*notice how you can load images different ways..and even attach an event to a particular image*/
{fullSrc:"images/DSC00213.jpg", src:"images/DSC00213_tn.jpg", onSelected: function(){alert('stopped the display'); return false} },
{fullSrc:"images/DSC00220.jpg", src:"images/DSC00220_tn.jpg"},
"images/DSC00215.jpg",
"images/DSC00217_tn.jpg",
...
]
},{
xtype:'panel',
id: 'LargeImage',
width:300,
height:300



And here is the code if you want to peruse it before downloading the zip file.



Ext.namespace('Ext.ux');
Ext.ux.Carousel = function(config){
Ext.ux.Carousel.superclass.constructor.call(this, config);
Ext.apply(this, config);
};
Ext.extend(Ext.ux.Carousel, Ext.Component, {
baseCls : 'x-carousel',
setSize : Ext.emptyFn,
setWidth : Ext.emptyFn,
setHeight : Ext.emptyFn,
setPosition : Ext.emptyFn,
setPagePosition : Ext.emptyFn,
initComponent : function(){
Ext.ux.Carousel.superclass.initComponent.call(this);
this.addEvents({'selected' : true});
if (typeof(this.imageGap)=='undefined') { this.imageGap = 5 }
if (!this.noPreload) {
this.preload.defer(1,this);
}

},

preload: function(){
var preload = Ext.get(document.body).createChild({tag:"div", style:"display:none"});

Ext.each(this.images, function(image){
if (typeof(image)=='string'){
image={src:image}
}
if (image.src){
preload.createChild({tag:"img", src:image.src});
}
if (image.fullSrc){
preload.createChild({tag:"img", src:image.fullSrc});
}
}, this);
},
onRender : function(ct, position){

var tpl = new Ext.Template(
'<div class="{cls}-wrap">',
'<div class="{cls}-inner">',
'<div class="{cls}-images-wrap">',
'<div class="{cls}-images"></div>',
'</div>',
'</div>',
'</div>'
);
if(position){
this.el = tpl.insertBefore(position, {cls: this.baseCls}, true);
}else{
this.el = tpl.append(ct, {cls: this.baseCls}, true);
}
if(this.id){
this.el.dom.id = this.id;
}

var inner= Ext.get(this.el.dom.firstChild);
var imagesWrap = Ext.get(inner.dom.firstChild);
this.divImages = Ext.get(imagesWrap.dom.firstChild);
inner.setStyle({
height:(this.imageHeight + (2*this.wrapMarginY)) + 'px',
width:this.width+'px'
});

var totalImageWidth=this.imageWidth+this.imageGap;
var usableWidth=this.width-(this.wrapMarginX*2);
var maxPicsOnce=Math.floor(usableWidth/totalImageWidth);
var usedWidth=maxPicsOnce*totalImageWidth-this.imageGap;
var offsetLeft=Math.floor((usableWidth-usedWidth)/2);
this.pageSize=usedWidth+this.imageGap;
this.maxPages=Math.round(this.images.length/maxPicsOnce+.4999);
this.curPage=0;
if (!Ext.isIE){
offsetLeft+=this.wrapMarginX;
}

imagesWrap.setStyle({
position: 'absolute',
clip:'rect(0,'+(usedWidth*1)+','+(this.imageHeight)+',0)',
'margin-top':this.wrapMarginY+'px',
width:this.width+'px',
height:this.imageHeight+'px',
'margin-left':offsetLeft+'px'
});
this.divImages.setStyle({
position: 'absolute'
});
Ext.each(this.images, function(image){
if (typeof(image)=='string'){
image={src:image}
}
thisImage = this.divImages.createChild({tag:"img", src:image.src, style:{
'margin-right': this.imageGap+'px',
width: this.imageWidth+'px',
height: this.imageHeight+'px'
}});

thisImage.on("click", function(e, ele){
if (!image.onSelected || !(image.onSelected.call(this, image, e, ele )===false)){
this.fireEvent('selected', this, image, e, ele);
}
},this);
},this);

var styleArrows={};
if (!this.dontResizeArrows){
styleArrows.height=(this.imageHeight+(2*this.wrapMarginY)) + 'px';
}

var divLeftButton = inner.createChild({tag:"div", cls:this.baseCls + '-handle-right', style:styleArrows});
divLeftButton.on("mousedown", function(){
this.shiftImages('right');
},this);
var divRightButton = inner.createChild({tag:"div", cls:this.baseCls + '-handle-left', style:styleArrows});
divRightButton.on("mousedown", function(){
this.shiftImages('left');
},this);


},
shiftImages: function(direction){
if (!this.curPage){
this.offsetLeft=this.divImages.getLeft();
}
var newPage=(direction=='right' ? this.curPage+1 : this.curPage-1 );
if (newPage<0 || newPage >= this.maxPages){
return;
}
this.curPage=newPage;
var newLocation=(this.pageSize*this.curPage)*-1+this.offsetLeft;
this.divImages.shift({ x:newLocation, duration: this.duration || .7 });
}
});
Ext.reg('carousel', Ext.ux.Carousel);

jsakalos
13 Mar 2008, 6:35 PM
Is there any chance to see it in action? Live Demo?

galdaka
14 Mar 2008, 12:44 AM
Live example please!!

Thanks in advance,

Animal
14 Mar 2008, 2:08 AM
There is an example of a Layout class which implements left/right scrolling of any added Components hre: http://extjs.com/forum/showthread.php?p=73802#post73802

Drop the code from there into examples/layout

You can add any Component in (And usually, it would be just a Panel containing an image as in the demo), and it lays them out side by side and lets you scroll them.

jerrybrown5
14 Mar 2008, 4:44 PM
Animal,
I must admit that I like your carousel layout more than my carousel component. Although mine is simpler to implement for pure image scrolling, it doesn't hold up as well when you need something more complicated. I think you chose the better way to program this feature set. Nice work!

PS, I wish I saw this before :)

Best regards,
Jerry