Mar 26, 2012, 10:09 PM
Carousel Hack
I'd say this qualifies as a "hack," but it's a start. I started with equiman's idea of using the CardLayout + TaskManager using the CardLayout example, but ended up switching to a ColumnLayout to get the left/right sliding effect instead of up/down. I've tested this in 1.2, and will post again as I try it out in the newer versions.
<%@ Page Language="C#" %>
<%@ Register Assembly="Ext.Net" Namespace="Ext.Net" TagPrefix="ext" %>
<script runat="server">
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
this.ResourceMgr.RegisterIcon(Icon.PlayGreen);
}
</script>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Carousel Hack</title>
<link href="../../../../resources/css/examples.css" rel="stylesheet" type="text/css" />
</head>
<body>
<form runat="server">
<ext:ResourceManager ID="ResourceMgr" runat="server" />
<ext:XScript ID="xscript" runat="server">
<script type="text/javascript" >
//Perform the expand of next/shrink of current actions
var carouselNav = function(change){
//Repeated clicks don't look very nice, so use a rudimentary semaphore
// to ensure that it will only "spin" after stopping.
if(#{CarouselPanel}.spinning !== true){
#{CarouselPanel}.spinning = true;
var current = #{CarouselPanel}.items.itemAt(#{CarouselPanel}.activeItem),
idx = #{CarouselPanel}.items.indexOf(current),
width = #{CarouselPanel}.getEl().getWidth() -1, //Full width didn't work
// (and possibly needs to
// consider padding?)
nextIdx = idx + change,
next;
//Get the next or previous item, wrapping around
// the total items as needed
nextIdx = nextIdx < 0
? #{CarouselPanel}.items.length - 1
: nextIdx % #{CarouselPanel}.items.length;
next = #{CarouselPanel}.items.itemAt(nextIdx);
moveElementPosition(current, change > 0);
//Set current's width, and the slide container's width,
// to match up with the full width of the column panel
current.getEl().parent().setWidth(width);
current.getEl().setWidth(width);
//Set next's width to 0 so it will expand nicely
next.getEl().setWidth(0);
next.show();
//Shrink the current item to 0
current.getEl().animate(
{ width: { to: 0 } },
1.5,
null,
'easeBoth',
'run');
//Expand the next item to the full width
next.getEl().animate(
{ width: { to: width } },
1.5,
function(){
#{CarouselPanel}.activeItem = nextIdx;
#{CarouselPanel}.spinning = false;
},
'easeBoth',
'run');
}
};
//Rearrange the carousel items in the dom to allow for the "infinite spin" effect
var moveElementPosition = function(aPanel, moveForward){
var parentNode = aPanel.getEl().parent(),
currentNode = aPanel.getEl().dom,
childNode = moveForward ? parentNode.dom.firstChild : parentNode.dom.lastChild;
//Only perform the movement actions if the current dom node is not
// the node that is to be moved
if(currentNode !== childNode){
Ext.removeNode(childNode);
if(moveForward){
//Moving forward, so move the first item to the end
parentNode.appendChild(childNode);
}
else{
//Moving backward, so move the last item to the front
parentNode.insertFirst(childNode);
}
}
};
//Action when the forward or backward buttons are clicked
var navClick = function(change){
navPause(false);
carouselNav(change);
};
//Pause the carousel (occurs when clicking play, or forward and backward)
//allowPlay - will only restart the task if true
var navPause = function(allowPlay){
if(#{CarouselTaskManager}.tasks && #{CarouselTaskManager}.getTask('CarouselTask').executing){
#{CarouselTaskManager}.stopTask('CarouselTask');
#{btnPlayPause}.setIconClass('icon-playgreen');
}
else if(allowPlay){
if(#{CarouselTaskManager}.tasks){
#{CarouselTaskManager}.startTask('CarouselTask');
}
else{
#{CarouselTaskManager}.initManager();
}
#{btnPlayPause}.setIconClass('icon-pausegreen');
}
};
//Action to take when the carousel task fires
var carouselTaskUpdate = function(task){
//The first time the task is called, don't advance the carousel item
// (since the event fires before the timer, the first item will immediately
// be carouselled away. Can also work around by delaying the task manager,
// but it complicates other matters)
if(#{CarouselTaskManager}.getTask('CarouselTask').first){
#{CarouselTaskManager}.getTask('CarouselTask').first = false;
}
else{
carouselNav(1);
}
}
//Column Layout adds an extra div to the end of the item contents, which throws
// off moveElementPosition. It doesn't appear to have any effect in this use case,
// so just throw it away when the carousel panel renders
var domKludge = function(){
Ext.removeNode(#{CarouselPanel}.items.itemAt(0).getEl().parent().dom.lastChild);
#{CarouselPanel}.spinning = false;
};
</script>
</ext:XScript>
<ext:Panel ID="CarouselPanel" runat="server" Layout="ColumnLayout" ActiveIndex="0"
HideMode="Offsets" AnchorHorizontal="100%" StyleSpec="white-space:nowrap;">
<Listeners>
<AfterRender Fn="domKludge" />
</Listeners>
<Items>
<ext:Panel ID="Panel1" runat="server" Html="<h1>Welcome to the Wizard!</h1><p>Step 1 of 3</p>"
Border="false" Header="false" AnchorHorizontal="100%" />
<ext:Panel ID="Panel2" runat="server" Html="<h1>Card 2</h1><p>Step 2 of 3</p>" Border="false"
Header="false" Hidden="true" />
<ext:Panel ID="Panel3" runat="server" Html="<h1>Congratulations!</h1><p>Step 3 of 3 - Complete</p>"
Border="false" Header="false" Hidden="true" />
</Items>
<Buttons>
<ext:Button ID="btnPrev" runat="server" Text="Prev" Icon="PreviousGreen">
<Listeners>
<Click Handler="navClick(-1);" />
</Listeners>
</ext:Button>
<ext:Button ID="btnPlayPause" runat="server" Icon="PauseGreen">
<Listeners>
<Click Handler="navPause(true);" />
</Listeners>
</ext:Button>
<ext:Button ID="btnNext" runat="server" Text="Next" Icon="NextGreen">
<Listeners>
<Click Handler="navClick(1);" />
</Listeners>
</ext:Button>
</Buttons>
</ext:Panel>
<ext:TaskManager ID="CarouselTaskManager" runat="server">
<Tasks>
<ext:Task TaskID="CarouselTask" Interval="5000" OnStart="this.executing = true; this.first = true;"
OnStop="this.executing = false; this.first = false;">
<Listeners>
<Update Fn="carouselTaskUpdate" />
</Listeners>
</ext:Task>
</Tasks>
</ext:TaskManager>
</form>
</body>
</html>
Like I said, definitely a hack! I've also tried it with images in the panels with success as well. The movement isn't exactly carousel-like (it's more like shuffling through a deck of cards), but it gets the idea across. I'll can play around with turning it into an actual control as the opportunity presents itself.