2020-10-20 00:58:15 +02:00

374 lines
9.8 KiB
ActionScript
Executable File

/*
Copyright (c) 2009 Yahoo! Inc. All rights reserved.
The copyrights embodied in the content of this file are licensed under the BSD (revised) open source license
*/
package com.yahoo.astra.fl.controls.carouselClasses
{
import com.yahoo.astra.animation.Animation;
import com.yahoo.astra.animation.AnimationEvent;
import fl.controls.listClasses.ICellRenderer;
import fl.core.UIComponent;
import fl.transitions.easing.Regular;
import flash.display.DisplayObject;
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.geom.Rectangle;
/**
* A Carousel renderer that displays items in a horizontal row or a vertical
* column. When the Carousel's selectedIndex changes, the selected cell
* renderer slides into view.
*
* @see com.yahoo.astra.fl.controls.Carousel
* @author Josh Tynjala
*/
public class SlidingCarouselRenderer extends BoxCarouselRenderer implements ICarouselLayoutRenderer
{
//--------------------------------------
// Constructor
//--------------------------------------
/**
* Constructor.
*/
public function SlidingCarouselRenderer()
{
super();
this.scrollRect = new Rectangle();
}
//--------------------------------------
// Properties
//--------------------------------------
/**
* @private
* Storage for the animationDuration property.
*/
private var _animationDuration:int = 500;
/**
* The duration of the fading animation.
*/
public function get animationDuration():int
{
return this._animationDuration;
}
/**
* @private
*/
public function set animationDuration(value:int):void
{
this._animationDuration = value;
}
/**
* @private
* Storage for the animationEasingFunction property.
*/
private var _animationEasingFunction:Function = Regular.easeOut;
/**
* The function used to ease the fading animation used by this layout
* renderer type.
*/
public function get animationEasingFunction():Function
{
return this._animationEasingFunction;
}
/**
* @private
*/
public function set animationEasingFunction(value:Function):void
{
this._animationEasingFunction = value;
}
/**
* @private
* A reference to the animation used to slide items in and out.
*/
protected var slide:Animation;
/**
* @private
* The last value of the selectedItem;
*/
protected var previouslySelectedItem:Object;
//--------------------------------------
// Protected Methods
//--------------------------------------
/**
* @private
*/
override protected function draw():void
{
//in all cases, we want to stop the previous animation
if(this.slide)
{
this.slide.pause();
this.slide.removeEventListener(AnimationEvent.UPDATE, slideUpdateHandler);
this.slide.removeEventListener(AnimationEvent.COMPLETE, slideCompleteHandler);
this.slide = null;
}
super.draw();
if(this.carousel.length == 0 || this.carousel.selectedIndex < 0)
{
//nothing to draw
return;
}
var selectedRenderer:ICellRenderer = this.carousel.itemToCellRenderer(this.carousel.selectedItem);
this.moveToRenderer(selectedRenderer);
this.previouslySelectedItem = this.carousel.selectedItem;
}
/**
* @private
* Creates the required renderers.
*/
override protected function createRenderers():Array
{
this.carousel.astra_carousel_internal::invalidateCellRenderers();
var startIndex:int = 0;
var rendererCount:int = this.carousel.length;
if(!this.drawAllRenderers)
{
var selectedRenderer:DisplayObject = this.carousel.astra_carousel_internal::createCellRenderer(this.carousel.selectedItem);
var pageItemCount:int = this.displayedItemCount;
if(this.displayedItemCount == 0)
{
if(this.direction == "horizontal")
{
pageItemCount = Math.ceil(this.width / (selectedRenderer.width + this.horizontalGap));
}
else
{
pageItemCount = Math.ceil(this.height / (selectedRenderer.height + this.verticalGap));
}
}
if((this.direction == "horizontal" && this.horizontalAlign == "center") ||
(this.direction == "vertical" && this.verticalAlign == "middle"))
{
pageItemCount++;
}
var oldIndex:int = this.carousel.dataProvider.getItemIndex(this.previouslySelectedItem);
var newIndex:int = this.carousel.selectedIndex;
if(oldIndex < 0)
{
oldIndex = newIndex;
}
startIndex = Math.min(oldIndex, newIndex);
var endIndex:int = Math.max(oldIndex, newIndex);
rendererCount = Math.abs(oldIndex - newIndex) + pageItemCount;
if(this.direction == "horizontal")
{
switch(this.horizontalAlign)
{
case "center":
{
startIndex -= ((pageItemCount - 1) / 2);
startIndex = Math.max(startIndex, 0);
if(oldIndex == 0 || oldIndex == this.carousel.length - 1)
{
rendererCount--;
}
if((newIndex == 0 || newIndex == this.carousel.length - 1) && newIndex != oldIndex)
{
rendererCount--;
}
break;
}
case "right":
{
startIndex -= (pageItemCount - 1);
startIndex = Math.max(startIndex, 0);
if(newIndex == 0 || oldIndex == 0)
{
rendererCount--;
}
break;
}
default: //left
{
if(oldIndex == this.carousel.length - 1 || newIndex == this.carousel.length - 1)
{
rendererCount--;
}
break;
}
}
}
}
var renderers:Array = [];
for(var i:int = 0; i < rendererCount; i++)
{
var index:int = startIndex + i;
if(index >= this.carousel.length)
{
break;
}
var item:Object = this.carousel.dataProvider.getItemAt(index);
var renderer:ICellRenderer = this.carousel.astra_carousel_internal::createCellRenderer(item);
Sprite(renderer).addEventListener(MouseEvent.CLICK, rendererClickHandler, false, 0, true);
if(renderer is UIComponent)
{
UIComponent(renderer).drawNow();
}
renderers.push(renderer);
}
this.carousel.astra_carousel_internal::validateCellRenderers();
return renderers;
}
/**
* @private
* Animates the scrollRect to display the specified renderer. Generally,
* this is the selected item.
*/
protected function moveToRenderer(renderer:ICellRenderer):void
{
var displayedRenderer:DisplayObject = DisplayObject(renderer);
var rendererX:Number = displayedRenderer.x;
var rendererY:Number = displayedRenderer.y;
if(this.direction == "vertical")
{
switch(this.verticalAlign)
{
case "middle":
{
rendererY -= (this.height - displayedRenderer.height) / 2;
break;
}
case "right":
{
rendererY -= (this.height - displayedRenderer.height);
break;
}
}
}
else //horizontal
{
switch(this.horizontalAlign)
{
case "center":
{
rendererX -= (this.width - displayedRenderer.width) / 2;
break;
}
case "right":
{
rendererX -= (this.width - displayedRenderer.width);
break;
}
}
}
var prevRendererX:Number = rendererX;
var prevRendererY:Number = rendererY;
if(this.previouslySelectedItem)
{
var prevRenderer:DisplayObject = this.carousel.itemToCellRenderer(this.previouslySelectedItem) as DisplayObject;
if(prevRenderer)
{
prevRendererX = prevRenderer.x;
prevRendererY = prevRenderer.y;
if(this.direction == "vertical")
{
switch(this.verticalAlign)
{
case "middle":
{
prevRendererY -= (this.height - prevRenderer.height) / 2;
break;
}
case "right":
{
prevRendererY -= (this.height - prevRenderer.height);
break;
}
}
}
else //horizontal
{
switch(this.horizontalAlign)
{
case "center":
{
prevRendererX -= (this.width - prevRenderer.width) / 2;
break;
}
case "right":
{
prevRendererX -= (this.width - prevRenderer.width);
break;
}
}
}
}
}
//we miss the first update from animation, so let's initialize the scroll rect
var scrollRect:Rectangle = this.scrollRect;
scrollRect.x = prevRendererX;
scrollRect.y = prevRendererY;
this.scrollRect = scrollRect;
this.slide = new Animation(this.animationDuration,
{x: prevRendererX, y: prevRendererY},
{x: rendererX, y: rendererY});
this.slide.easingFunction = this.animationEasingFunction;
this.slide.addEventListener(AnimationEvent.UPDATE, slideUpdateHandler);
this.slide.addEventListener(AnimationEvent.COMPLETE, slideCompleteHandler);
}
//--------------------------------------
// Protected Event Handlers
//--------------------------------------
/**
* @private
* Updates the scroll rect of this layout renderer to show the currently
* selected item.
*/
protected function slideUpdateHandler(event:AnimationEvent):void
{
var scrollRect:Rectangle = this.scrollRect;
scrollRect.x = event.parameters.x;
scrollRect.y = event.parameters.y;
this.scrollRect = scrollRect;
}
/**
* @private
* When the animation completes, clean it up for garbage collection.
*/
protected function slideCompleteHandler(event:AnimationEvent):void
{
this.slideUpdateHandler(event);
this.slide.removeEventListener(AnimationEvent.UPDATE, slideUpdateHandler);
this.slide.removeEventListener(AnimationEvent.COMPLETE, slideCompleteHandler);
this.slide = null;
}
}
}