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);
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);