You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
936 lines
27 KiB
936 lines
27 KiB
14 years ago
|
/**
|
||
|
* @author elmasse (Maximiliano Fierro)
|
||
|
* @version 0.1 beta
|
||
|
*
|
||
|
* @usage:
|
||
|
* <code>
|
||
|
* var images = [
|
||
|
* {src: 'images/ardillitaMac.jpg'},
|
||
|
* {src: 'http://farm2.static.flickr.com/1380/1426855399_b4b8eccbdb.jpg?v=0'},
|
||
|
* {src: 'http://farm1.static.flickr.com/69/213130158_0d1aa23576_d.jpg'}
|
||
|
* ];
|
||
|
* var myCoverFlow = new YAHOO.ext.CoverFlow('coverFlowTest', {height: 200, width: 800, images: images, bgColor: '#C0C0C0'});
|
||
|
* </code>
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
|
||
|
YAHOO.namespace("ext");
|
||
|
|
||
|
//(function(){
|
||
|
|
||
|
/**
|
||
|
* @class CoverFlow
|
||
|
* @namespace YAHOO.ext
|
||
|
* @constructor
|
||
|
* @param el {String|HTMLElement} Reference to element where CoverFlow will be rendered.
|
||
|
* @param config {Object} configuration object
|
||
|
* config.height {Number} Element height. Optional. Default: CoverFlow.DEFAULT_HEIGHT.
|
||
|
* config.width {Number} Element width. Optional. Default: CoverFlow.DEFAULT_WIDTH.
|
||
|
* config.images {Array} Array of Images. [{src:}]
|
||
|
* config.bgColor {String} Background color. Could be in the form #00000 or black. Optional. Default: CoverFlow.DEFAULT_BG_COLOR.
|
||
|
*
|
||
|
*/
|
||
|
YAHOO.ext.CoverFlow = function(el, userConfig){
|
||
|
if(el)
|
||
|
this.init(el, userConfig || {});
|
||
|
};
|
||
|
|
||
|
//shortcuts
|
||
|
var CoverFlow = YAHOO.ext.CoverFlow;
|
||
|
var Dom = YAHOO.util.Dom;
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Defaults
|
||
|
*/
|
||
|
CoverFlow.DEFAULT_HEIGHT = 300;
|
||
|
CoverFlow.DEFAULT_WIDTH = 800;
|
||
|
CoverFlow.DEFAULT_BG_COLOR = '#000000';
|
||
|
CoverFlow.IMAGE_SEPARATION = 50;
|
||
|
CoverFlow.RIGHT = 'right';
|
||
|
CoverFlow.LEFT = 'left';
|
||
|
CoverFlow.LABEL_CLASS = 'coverFlowLabel';
|
||
|
|
||
|
CoverFlow.prototype = {
|
||
|
//Images array (it's a sort of transient var)
|
||
|
images: [],
|
||
|
//Items array {CoverFlowItem[]}
|
||
|
coverFlowItems: [],
|
||
|
|
||
|
remainingImages: 9999,
|
||
|
|
||
|
element: null,
|
||
|
labelElement: null,
|
||
|
containerHeight: 0,
|
||
|
containerWidth: 0,
|
||
|
|
||
|
imageHeightRatio: 0.6,
|
||
|
imageWidthRatio: 0.2,
|
||
|
reflectionRatio: 0.6, // this causes: imageTotalHeightRatio = imageHeightRatio + imageHeightRatio*reflectionRatio
|
||
|
topRatio: 0.1,
|
||
|
sideRatio: 0.4,
|
||
|
|
||
|
perspectiveAngle: 20,
|
||
|
imageZIndex: 1000,
|
||
|
selectedImageZIndex: 9999,
|
||
|
selectedItem: 0,
|
||
|
|
||
|
moveQueue: [],
|
||
|
animationWorking: false,
|
||
|
|
||
|
init: function(el, userConfig){
|
||
|
|
||
|
this.element = Dom.get(el);
|
||
|
this.applyConfig(userConfig);
|
||
|
|
||
|
if(userConfig.images)
|
||
|
this.addImages(userConfig.images);
|
||
|
|
||
|
this.attachEventListeners();
|
||
|
this.createLabelElement();
|
||
|
},
|
||
|
|
||
|
applyConfig: function(config){
|
||
|
this.containerHeight = config.height || CoverFlow.DEFAULT_HEIGHT;
|
||
|
this.containerWidth = config.width || CoverFlow.DEFAULT_WIDTH;
|
||
|
this.backgroundColor = config.bgColor || CoverFlow.DEFAULT_BG_COLOR;
|
||
|
|
||
|
this.element.style.position = 'relative';
|
||
|
this.element.style.height = this.containerHeight + 'px';
|
||
|
this.element.style.width = this.containerWidth + 'px';
|
||
|
this.element.style.background = this.backgroundColor;
|
||
|
this.element.style.overflow = 'hidden';
|
||
|
},
|
||
|
|
||
|
addImages: function(images){
|
||
|
this.images = [];
|
||
|
this.remainingImages = images.length;
|
||
|
|
||
|
for(var i=0; i < images.length; i++){
|
||
|
var img = images[i];
|
||
|
var image = new Image();
|
||
|
image.id = Dom.generateId();
|
||
|
image.index = i;
|
||
|
image.onclick = img.onclick;
|
||
|
image.label = img.label;
|
||
|
|
||
|
//hide images
|
||
|
image.style.visibility = 'hidden';
|
||
|
image.style.display = 'none';
|
||
|
//this is to maintain image order since image.onload will be called randomly
|
||
|
this.element.appendChild(image);
|
||
|
//a shortcut to not create another context to call onload
|
||
|
var me = this;
|
||
|
// image.onload = function(){
|
||
|
// CoverFlow.preloadImage(me, this); // this = image
|
||
|
// };
|
||
|
YAHOO.util.Event.on(image, 'load', this.preloadImage, image, this);
|
||
|
image.src = img.src;
|
||
|
|
||
|
};
|
||
|
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* @function preloadImage
|
||
|
* @param event
|
||
|
* @param image
|
||
|
* @return void
|
||
|
*/
|
||
|
preloadImage : function(e, image){
|
||
|
this.images.push(image);
|
||
|
this.checkAllImagesLoaded();
|
||
|
},
|
||
|
|
||
|
checkAllImagesLoaded: function(){
|
||
|
this.remainingImages--;
|
||
|
if(!this.remainingImages){
|
||
|
this.setup();
|
||
|
}
|
||
|
},
|
||
|
|
||
|
setup: function(){
|
||
|
this.createCoverFlowItems();
|
||
|
this.sortCoverFlowItems();
|
||
|
this.initCoverFlow();
|
||
|
},
|
||
|
|
||
|
initCoverFlow: function(){
|
||
|
|
||
|
for(var i=0; i < this.coverFlowItems.length; i++){
|
||
|
var coverFlowItem = this.coverFlowItems[i];
|
||
|
|
||
|
var angle = 0;
|
||
|
var direction;
|
||
|
|
||
|
if(i==0){
|
||
|
coverFlowItem.setZIndex(this.selectedImageZIndex);
|
||
|
coverFlowItem.setLeft(this.getCenter() - coverFlowItem.element.width/2);
|
||
|
coverFlowItem.isSelected(true);
|
||
|
this.selectedItem = 0;
|
||
|
this.showLabel(coverFlowItem.getLabel());
|
||
|
}else{
|
||
|
angle = this.perspectiveAngle;
|
||
|
direction = CoverFlow.LEFT;
|
||
|
coverFlowItem.setZIndex(this.imageZIndex - i);
|
||
|
coverFlowItem.setLeft( this.getRightStart()+ (i - 1)* CoverFlow.IMAGE_SEPARATION);
|
||
|
coverFlowItem.isSelected(false);
|
||
|
}
|
||
|
coverFlowItem.setAngle(angle);
|
||
|
coverFlowItem.drawInPerspective(direction);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
createLabelElement: function(){
|
||
|
var label = document.createElement('div');
|
||
|
label.id = Dom.generateId();
|
||
|
label.style.position = 'absolute';
|
||
|
label.style.top = this.getFooterOffset() + 'px';
|
||
|
label.innerHTML = ' ';
|
||
|
label.style.textAlign = 'center';
|
||
|
label.style.width = this.containerWidth + 'px';
|
||
|
label.style.zIndex = this.selectedImageZIndex + 10;
|
||
|
label.className = CoverFlow.LABEL_CLASS;
|
||
|
this.labelElement = this.element.appendChild(label);
|
||
|
},
|
||
|
|
||
|
showLabel: function(text){
|
||
|
if(text)
|
||
|
this.labelElement.innerHTML = text;
|
||
|
else
|
||
|
this.labelElement.innerHTML = '';
|
||
|
},
|
||
|
|
||
|
attachEventListeners: function(){
|
||
|
new YAHOO.util.KeyListener(this.element, { keys:39 },
|
||
|
{ fn:this.selectNext,
|
||
|
scope:this,
|
||
|
correctScope:true } ).enable();
|
||
|
|
||
|
new YAHOO.util.KeyListener(this.element, { keys:37 },
|
||
|
{ fn:this.selectPrevious,
|
||
|
scope:this,
|
||
|
correctScope:true } ).enable();
|
||
|
|
||
|
|
||
|
},
|
||
|
|
||
|
select: function(e,coverFlowItem){
|
||
|
var distance = this.selectedItem - coverFlowItem.index;
|
||
|
if(distance < 0){
|
||
|
for(var i=0; i < -distance; i++)
|
||
|
this.selectNext();
|
||
|
}else{
|
||
|
for(var i=0; i < distance; i++)
|
||
|
this.selectPrevious();
|
||
|
}
|
||
|
},
|
||
|
|
||
|
|
||
|
selectNext: function(){
|
||
|
if(this.animationWorking){
|
||
|
this.moveQueue.push('moveLeft');
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var animateItems = [];
|
||
|
|
||
|
for(var i=0; i < this.coverFlowItems.length; i++){
|
||
|
var coverFlowItem = this.coverFlowItems[i];
|
||
|
var isLast = (this.selectedItem == this.coverFlowItems.length -1);
|
||
|
if(!isLast){
|
||
|
var distance = i-this.selectedItem;
|
||
|
|
||
|
if(distance == 0){// selected
|
||
|
coverFlowItem.setZIndex(this.imageZIndex);
|
||
|
coverFlowItem.isSelected(false);
|
||
|
animateItems.push({item: coverFlowItem, attribute:{angle: {start: 0, end: this.perspectiveAngle} } });
|
||
|
|
||
|
coverFlowItem = this.coverFlowItems[++i];
|
||
|
coverFlowItem.isSelected(true);
|
||
|
this.showLabel(coverFlowItem.getLabel());
|
||
|
animateItems.push({item: coverFlowItem, attribute:{angle: {start: this.perspectiveAngle, end: 0} } });
|
||
|
|
||
|
}else{
|
||
|
animateItems.push({item: coverFlowItem, attribute: {left: {start:coverFlowItem.getLeft(), end: coverFlowItem.getLeft() - CoverFlow.IMAGE_SEPARATION} }});
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var animation = new CoverFlowAnimation({
|
||
|
direction: CoverFlow.LEFT,
|
||
|
center: this.getCenter(),
|
||
|
startLeftPos: this.getLeftStart(),
|
||
|
startRightPos: this.getRightStart()
|
||
|
},
|
||
|
animateItems, 0.5);
|
||
|
|
||
|
animation.onStart.subscribe(this.handleAnimationWorking, this);
|
||
|
animation.onComplete.subscribe(this.handleQueuedMove, this);
|
||
|
|
||
|
animation.animate();
|
||
|
|
||
|
if(this.selectedItem + 1 < this.coverFlowItems.length)
|
||
|
this.selectedItem++;
|
||
|
},
|
||
|
|
||
|
selectPrevious: function(){
|
||
|
if(this.animationWorking){
|
||
|
this.moveQueue.push('moveRight');
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var animateItems = [];
|
||
|
|
||
|
for(var i=0; i < this.coverFlowItems.length; i++){
|
||
|
var coverFlowItem = this.coverFlowItems[i];
|
||
|
var isFirst = (this.selectedItem == 0);
|
||
|
var distance = i-this.selectedItem;
|
||
|
if(!isFirst){
|
||
|
if(distance == - 1){
|
||
|
coverFlowItem.setZIndex(this.selectedImageZIndex);
|
||
|
coverFlowItem.isSelected(true);
|
||
|
this.showLabel(coverFlowItem.getLabel());
|
||
|
animateItems.push({item: coverFlowItem, attribute: {angle: {start: this.perspectiveAngle, end: 0}}});
|
||
|
|
||
|
coverFlowItem = this.coverFlowItems[++i];
|
||
|
coverFlowItem.isSelected(false);
|
||
|
coverFlowItem.setZIndex(this.imageZIndex);
|
||
|
animateItems.push({item: coverFlowItem, attribute: {angle: {start: 0, end: this.perspectiveAngle}}});
|
||
|
}else{
|
||
|
coverFlowItem.setZIndex(coverFlowItem.getZIndex() - 1);
|
||
|
animateItems.push({item: coverFlowItem, attribute: {left: {start:coverFlowItem.getLeft(), end: coverFlowItem.getLeft() + CoverFlow.IMAGE_SEPARATION} }});
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
var animation = new CoverFlowAnimation({
|
||
|
direction: CoverFlow.RIGHT,
|
||
|
center: this.getCenter(),
|
||
|
startLeftPos: this.getLeftStart(),
|
||
|
startRightPos: this.getRightStart()
|
||
|
},
|
||
|
animateItems, 0.5);
|
||
|
|
||
|
animation.onStart.subscribe(this.handleAnimationWorking, this);
|
||
|
animation.onComplete.subscribe(this.handleQueuedMove, this);
|
||
|
|
||
|
animation.animate();
|
||
|
|
||
|
if(this.selectedItem > 0)
|
||
|
this.selectedItem--;
|
||
|
},
|
||
|
|
||
|
handleAnimationWorking: function(a, b, cf){
|
||
|
cf.animationWorking = true;
|
||
|
},
|
||
|
|
||
|
handleQueuedMove: function(msg, data, cf){
|
||
|
cf.animationWorking = false;
|
||
|
|
||
|
var next = cf.moveQueue.pop();
|
||
|
if(next == 'moveLeft')
|
||
|
cf.selectNext();
|
||
|
if(next == 'moveRight')
|
||
|
cf.selectPrevious();
|
||
|
},
|
||
|
|
||
|
getCenter: function(){
|
||
|
return this.containerWidth / 2;
|
||
|
},
|
||
|
|
||
|
getRightStart: function() {
|
||
|
return this.containerWidth - this.sideRatio * this.containerWidth;
|
||
|
},
|
||
|
|
||
|
getLeftStart: function() {
|
||
|
return this.sideRatio * this.containerWidth;
|
||
|
},
|
||
|
|
||
|
sortCoverFlowItems: function(){
|
||
|
function sortFunction(aCoverFlowItem, bCoverFlowItem){
|
||
|
return aCoverFlowItem.index - bCoverFlowItem.index;
|
||
|
}
|
||
|
|
||
|
this.coverFlowItems.sort(sortFunction);
|
||
|
},
|
||
|
|
||
|
createCoverFlowItems: function(){
|
||
|
this.coverFlowItems = [];
|
||
|
for(var i=0; i<this.images.length; i++){
|
||
|
var image = this.images[i];
|
||
|
var coverFlowItem = new CoverFlowItem(image, {
|
||
|
scaledWidth: this.scaleWidth(image),
|
||
|
scaledHeight: this.scaleHeight(image),
|
||
|
reflectionRatio: this.reflectionRatio,
|
||
|
bgColor: this.backgroundColor,
|
||
|
onclick: {fn: this.select, scope: this}
|
||
|
});
|
||
|
this.alignCenterHeight(coverFlowItem);
|
||
|
this.coverFlowItems.push(coverFlowItem);
|
||
|
};
|
||
|
delete this.images;
|
||
|
},
|
||
|
|
||
|
alignCenterHeight: function(coverFlowItem){//review!!!!!
|
||
|
coverFlowItem.element.style.position = 'absolute';
|
||
|
|
||
|
var imageHeight = coverFlowItem.canvas.height / (1 + this.reflectionRatio);
|
||
|
var top = this.getMaxImageHeight() - imageHeight;
|
||
|
top += this.topRatio * this.containerHeight;
|
||
|
|
||
|
coverFlowItem.setTop(top);
|
||
|
|
||
|
},
|
||
|
|
||
|
scaleHeight: function(image){
|
||
|
var height = 0;
|
||
|
if(image.height <= this.getMaxImageHeight() && image.width <= this.getMaxImageWidth()){
|
||
|
height = image.height;
|
||
|
}
|
||
|
if(image.height > this.getMaxImageHeight() && image.width <= this.getMaxImageWidth()){
|
||
|
height = ((image.height / this.getMaxImageHeight())) * image.height;
|
||
|
}
|
||
|
if(image.height <= this.getMaxImageHeight() && image.width > this.getMaxImageWidth()){
|
||
|
height = ((image.width / this.getMaxImageWidth())) * image.height;
|
||
|
}
|
||
|
if(image.height > this.getMaxImageHeight() && image.width > this.getMaxImageWidth()){
|
||
|
if(image.height > image.width)
|
||
|
height = ((this.getMaxImageHeight() / image.height)) * image.height;
|
||
|
else
|
||
|
height = ((this.getMaxImageWidth() / image.width)) * image.height;
|
||
|
}
|
||
|
return height;
|
||
|
},
|
||
|
|
||
|
scaleWidth: function(image){
|
||
|
var width = 0;
|
||
|
if(image.height <= this.getMaxImageHeight() && image.width <= this.getMaxImageWidth()){
|
||
|
width = image.width;
|
||
|
}
|
||
|
if(image.height > this.getMaxImageHeight() && image.width <= this.getMaxImageWidth()){
|
||
|
width = ((image.height / this.getMaxImageHeight())) * image.width;
|
||
|
}
|
||
|
if(image.height <= this.getMaxImageHeight() && image.width > this.getMaxImageWidth()){
|
||
|
width = ((image.width / this.getMaxImageWidth())) * image.width;
|
||
|
}
|
||
|
if(image.height > this.getMaxImageHeight() && image.width > this.getMaxImageWidth()){
|
||
|
if(image.height > image.width)
|
||
|
width = ((this.getMaxImageHeight() / image.height)) * image.width;
|
||
|
else
|
||
|
width = ((this.getMaxImageWidth() / image.width)) * image.width;
|
||
|
}
|
||
|
return width;
|
||
|
},
|
||
|
|
||
|
|
||
|
getMaxImageHeight: function(){
|
||
|
return (this.containerHeight * this.imageHeightRatio);
|
||
|
},
|
||
|
|
||
|
getMaxImageWidth: function(){
|
||
|
return (this.containerWidth * this.imageWidthRatio);
|
||
|
},
|
||
|
|
||
|
getTopOffset: function(){
|
||
|
return this.containerHeight * this.topRatio;
|
||
|
},
|
||
|
|
||
|
getFooterOffset: function(){
|
||
|
return this.containerHeight * (this.topRatio + this.imageHeightRatio);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @class CoverFlowItem
|
||
|
*
|
||
|
*/
|
||
|
CoverFlowItem = function(image, config){
|
||
|
if(image)
|
||
|
this.init(image, config);
|
||
|
};
|
||
|
|
||
|
CoverFlowItem.prototype = {
|
||
|
canvas: null,
|
||
|
element: null,
|
||
|
index: null,
|
||
|
id: null,
|
||
|
angle: 0,
|
||
|
selected: false,
|
||
|
onclickFn: null,
|
||
|
selectedOnclickFn: null,
|
||
|
label: null,
|
||
|
|
||
|
onSelected: null,
|
||
|
|
||
|
init: function(image, config){
|
||
|
var scaledWidth = config.scaledWidth;
|
||
|
var scaledHeight = config.scaledHeight;
|
||
|
var reflectionRatio = config.reflectionRatio;
|
||
|
var bgColor = config.bgColor;
|
||
|
|
||
|
this.id = image.id;
|
||
|
this.index = image.index;
|
||
|
this.onclickFn = config.onclick;
|
||
|
this.selectedOnclickFn = image.onclick;
|
||
|
this.label = image.label;
|
||
|
var parent = image.parentNode;
|
||
|
this.canvas = this.createImageCanvas(image,scaledWidth,scaledHeight,reflectionRatio, bgColor);
|
||
|
this.element = this.canvas.cloneNode(false);
|
||
|
this.element.id = this.id;
|
||
|
parent.replaceChild(this.element, image);
|
||
|
|
||
|
this.onSelected = new YAHOO.util.CustomEvent('onSelected', this);
|
||
|
this.onSelected.subscribe(this.handleOnclick);
|
||
|
|
||
|
},
|
||
|
|
||
|
getLabel: function(){
|
||
|
return this.label;
|
||
|
},
|
||
|
|
||
|
handleOnclick: function(){
|
||
|
YAHOO.util.Event.removeListener(this.element, 'click');
|
||
|
if(!this.selected){
|
||
|
YAHOO.util.Event.addListener(this.element, 'click', this.onclickFn.fn, this, this.onclickFn.scope);
|
||
|
}else{
|
||
|
if(this.selectedOnclickFn && this.selectedOnclickFn.fn)
|
||
|
YAHOO.util.Event.addListener(this.element, 'click', this.selectedOnclickFn.fn, this, this.selectedOnclickFn.scope);
|
||
|
else
|
||
|
YAHOO.util.Event.addListener(this.element, 'click', this.selectedOnclickFn);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
isSelected: function(selected){
|
||
|
this.selected = selected;
|
||
|
this.onSelected.fire();
|
||
|
},
|
||
|
|
||
|
setAngle: function(angle){
|
||
|
this.angle = angle;
|
||
|
},
|
||
|
|
||
|
getAngle: function(){
|
||
|
return this.angle;
|
||
|
},
|
||
|
|
||
|
setTop: function(top){
|
||
|
this.element.style.top = top + 'px';
|
||
|
},
|
||
|
|
||
|
setLeft: function(left){
|
||
|
this.element.style.left = left + 'px';
|
||
|
},
|
||
|
|
||
|
getLeft: function(){
|
||
|
var ret = this.element.style.left;
|
||
|
return new Number(ret.replace("px", ""));
|
||
|
},
|
||
|
|
||
|
getZIndex: function(){
|
||
|
return this.element.style.zIndex;
|
||
|
},
|
||
|
|
||
|
setZIndex: function(zIndex){
|
||
|
this.element.style.zIndex = zIndex;
|
||
|
},
|
||
|
|
||
|
createImageCanvas: function(image, sWidth, sHeight, reflectionRatio, bgColor){
|
||
|
|
||
|
var imageCanvas = document.createElement('canvas');
|
||
|
|
||
|
if(imageCanvas.getContext){
|
||
|
|
||
|
var scaledWidth = sWidth;
|
||
|
var scaledHeight = sHeight;
|
||
|
var reflectionHeight = scaledHeight * reflectionRatio;
|
||
|
|
||
|
imageCanvas.height = scaledHeight + reflectionHeight;
|
||
|
imageCanvas.width = scaledWidth;
|
||
|
|
||
|
var ctx = imageCanvas.getContext('2d');
|
||
|
|
||
|
ctx.clearRect(0, 0, imageCanvas.width, imageCanvas.height);
|
||
|
ctx.globalCompositeOperation = 'source-over';
|
||
|
ctx.fillStyle = 'rgba(0, 0, 0, 1)';
|
||
|
ctx.fillRect(0, 0, imageCanvas.width, imageCanvas.height);
|
||
|
|
||
|
//draw the reflection image
|
||
|
ctx.save();
|
||
|
ctx.translate(0, (2*scaledHeight));
|
||
|
ctx.scale(1, -1);
|
||
|
ctx.drawImage(image, 0, 0, scaledWidth, scaledHeight);
|
||
|
ctx.restore();
|
||
|
//create the gradient effect
|
||
|
ctx.save();
|
||
|
ctx.translate(0, scaledHeight);
|
||
|
ctx.globalCompositeOperation = 'destination-out';
|
||
|
var grad = ctx.createLinearGradient( 0, 0, 0, scaledHeight);
|
||
|
grad.addColorStop(1, 'rgba(0, 0, 0, 1)');
|
||
|
grad.addColorStop(0, 'rgba(0, 0, 0, 0.75)');
|
||
|
ctx.fillStyle = grad;
|
||
|
ctx.fillRect(0, 0, scaledWidth, scaledHeight);
|
||
|
//apply the background color to the gradient
|
||
|
ctx.globalCompositeOperation = 'destination-over';
|
||
|
ctx.fillStyle = bgColor; '#000';
|
||
|
ctx.globalAlpha = 0.8;
|
||
|
ctx.fillRect(0, 0 , scaledWidth, scaledHeight);
|
||
|
ctx.restore();
|
||
|
//draw the image
|
||
|
ctx.save();
|
||
|
ctx.translate(0, 0);
|
||
|
ctx.globalCompositeOperation = 'source-over';
|
||
|
ctx.drawImage(image, 0, 0, scaledWidth, scaledHeight);
|
||
|
ctx.restore();
|
||
|
|
||
|
return imageCanvas;
|
||
|
}
|
||
|
},
|
||
|
|
||
|
drawInPerspective: function(direction, frameSize){
|
||
|
var canvas = this.element;
|
||
|
var image = this.canvas;
|
||
|
var angle = Math.ceil(this.angle);
|
||
|
var ctx;
|
||
|
var originalWidth = image.width;
|
||
|
var originalHeight = image.height;
|
||
|
var destinationWidth = destinationWidth || originalWidth; // for future use
|
||
|
var destinationHeight = destinationHeight || originalHeight; // for future use
|
||
|
|
||
|
var perspectiveCanvas = document.createElement('canvas');
|
||
|
perspectiveCanvas.height = destinationHeight;
|
||
|
perspectiveCanvas.width = destinationWidth;
|
||
|
var perspectiveCtx = perspectiveCanvas.getContext('2d');
|
||
|
|
||
|
var alpha = angle * Math.PI/180; // Math uses radian
|
||
|
|
||
|
if(alpha > 0){ // if we have an angle greater than 0 then apply the perspective
|
||
|
var right = (direction == CoverFlow.RIGHT);
|
||
|
|
||
|
var initialX=0, finalX=0, initialY=0, finalY=0;
|
||
|
|
||
|
frameSize = frameSize || 1;
|
||
|
var xDes, yDes;
|
||
|
var heightDes, widthDes;
|
||
|
var perspectiveWidht = destinationWidth;
|
||
|
|
||
|
var frameFactor = frameSize / originalWidth;
|
||
|
var frames = Math.floor(originalWidth / frameSize);
|
||
|
|
||
|
var widthSrc = frameSize ;
|
||
|
var heightSrc = originalHeight;
|
||
|
|
||
|
for(var i=0; i < frames; i++){
|
||
|
var xSrc = (i) * frameSize;
|
||
|
var ySrc = 0;
|
||
|
var betaTan = 0;
|
||
|
width = destinationWidth * (i) * frameFactor;
|
||
|
horizon = destinationHeight / 2;
|
||
|
|
||
|
if(right){
|
||
|
betaTan = horizon/((Math.tan(alpha)*horizon) + width);
|
||
|
xDes = (betaTan*width)/(Math.tan(alpha) + betaTan);
|
||
|
yDes = Math.tan(alpha) * xDes;
|
||
|
|
||
|
if(i == frames -1){
|
||
|
finalX=xDes;
|
||
|
finalY=yDes;
|
||
|
}
|
||
|
}else{
|
||
|
betaTan = horizon/((Math.tan(alpha)*horizon) +(destinationWidth-width));
|
||
|
xDes = (Math.tan(alpha)*(destinationWidth) + (betaTan * width))/(Math.tan(alpha) + betaTan);
|
||
|
yDes = -Math.tan(alpha)*xDes + (Math.tan(alpha)*(destinationWidth));
|
||
|
|
||
|
if(i == 0){
|
||
|
initialX = xDes;
|
||
|
initialY = yDes;
|
||
|
finalX = destinationWidth;
|
||
|
finalY = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
heightDes = destinationHeight - (2*yDes);
|
||
|
widthDes = heightDes / destinationHeight * destinationWidth;
|
||
|
|
||
|
perspectiveCtx.drawImage(image, xSrc, ySrc, widthSrc, heightSrc, xDes, yDes, widthDes, heightDes);
|
||
|
|
||
|
}
|
||
|
|
||
|
perspectiveWidth = finalX - initialX;
|
||
|
originalCanvasWidth = destinationWidth;
|
||
|
canvas.width = perspectiveWidth;
|
||
|
|
||
|
ctx = canvas.getContext('2d');
|
||
|
|
||
|
//remove exceeded pixels
|
||
|
ctx.beginPath();
|
||
|
if(right){
|
||
|
ctx.moveTo(0, 0);
|
||
|
ctx.lineTo(finalX, finalY);
|
||
|
ctx.lineTo(finalX, finalY + (destinationHeight - 2*finalY));
|
||
|
ctx.lineTo(0, destinationHeight);
|
||
|
ctx.lineTo(0,0);
|
||
|
}else{
|
||
|
var initialX1 = initialX - (originalCanvasWidth - perspectiveWidth);
|
||
|
var finalX1 = finalX - (originalCanvasWidth - perspectiveWidth);
|
||
|
ctx.moveTo(0, initialY);
|
||
|
ctx.lineTo(finalX1, finalY);
|
||
|
ctx.lineTo(finalX1, destinationHeight);
|
||
|
ctx.lineTo(initialX1, initialY + (destinationHeight - 2*initialY));
|
||
|
ctx.lineTo(0, initialY);
|
||
|
}
|
||
|
ctx.closePath();
|
||
|
ctx.clip();
|
||
|
|
||
|
ctx.drawImage(perspectiveCanvas, initialX, 0, perspectiveWidth, destinationHeight, 0, 0, perspectiveWidth, destinationHeight);
|
||
|
|
||
|
}else{
|
||
|
|
||
|
canvas.width = perspectiveCanvas.width;
|
||
|
canvas.height = perspectiveCanvas.height;
|
||
|
perspectiveCtx.drawImage(image, 0, 0, originalWidth, originalHeight, 0, 0, destinationWidth, destinationHeight);
|
||
|
ctx = canvas.getContext('2d');
|
||
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||
|
ctx.drawImage(perspectiveCanvas, 0, 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* @class CoverFlowAnimation
|
||
|
* @requires YAHOO.util.AnimMgr
|
||
|
*/
|
||
|
CoverFlowAnimation = function(config, animationItems, duration){
|
||
|
this.init(config, animationItems, duration);
|
||
|
};
|
||
|
|
||
|
CoverFlowAnimation.prototype = {
|
||
|
direction: null,
|
||
|
|
||
|
center: null,
|
||
|
|
||
|
startLeftPos: null,
|
||
|
|
||
|
startRightPos: null,
|
||
|
|
||
|
animationItems: null,
|
||
|
|
||
|
method : YAHOO.util.Easing.easeNone,
|
||
|
|
||
|
animated: false,
|
||
|
|
||
|
startTime: null,
|
||
|
|
||
|
actualFrames : 0,
|
||
|
|
||
|
useSeconds : true, // default to seconds
|
||
|
|
||
|
currentFrame : 0,
|
||
|
|
||
|
totalFrames : YAHOO.util.AnimMgr.fps,
|
||
|
|
||
|
init: function(config, animationItems, duration){
|
||
|
this.direction = config.direction;
|
||
|
this.center = config.center;
|
||
|
this.startLeftPos = config.startLeftPos;
|
||
|
this.startRightPos = config.startRightPos;
|
||
|
this.animationItems = animationItems;
|
||
|
this.duration = duration || 1;
|
||
|
this.registerEvents();
|
||
|
},
|
||
|
|
||
|
registerEvents: function(){
|
||
|
/**
|
||
|
* Custom event that fires after onStart, useful in subclassing
|
||
|
* @private
|
||
|
*/
|
||
|
this._onStart = new YAHOO.util.CustomEvent('_start', this, true);
|
||
|
|
||
|
/**
|
||
|
* Custom event that fires when animation begins
|
||
|
* Listen via subscribe method (e.g. myAnim.onStart.subscribe(someFunction)
|
||
|
* @event onStart
|
||
|
*/
|
||
|
this.onStart = new YAHOO.util.CustomEvent('start', this);
|
||
|
|
||
|
/**
|
||
|
* Custom event that fires between each frame
|
||
|
* Listen via subscribe method (e.g. myAnim.onTween.subscribe(someFunction)
|
||
|
* @event onTween
|
||
|
*/
|
||
|
this.onTween = new YAHOO.util.CustomEvent('tween', this);
|
||
|
|
||
|
/**
|
||
|
* Custom event that fires after onTween
|
||
|
* @private
|
||
|
*/
|
||
|
this._onTween = new YAHOO.util.CustomEvent('_tween', this, true);
|
||
|
|
||
|
/**
|
||
|
* Custom event that fires when animation ends
|
||
|
* Listen via subscribe method (e.g. myAnim.onComplete.subscribe(someFunction)
|
||
|
* @event onComplete
|
||
|
*/
|
||
|
this.onComplete = new YAHOO.util.CustomEvent('complete', this);
|
||
|
/**
|
||
|
* Custom event that fires after onComplete
|
||
|
* @private
|
||
|
*/
|
||
|
this._onComplete = new YAHOO.util.CustomEvent('_complete', this, true);
|
||
|
|
||
|
this._onStart.subscribe(this.doOnStart);
|
||
|
this._onTween.subscribe(this.doOnTween);
|
||
|
this._onComplete.subscribe(this.doOnComplete);
|
||
|
|
||
|
},
|
||
|
|
||
|
isAnimated : function() {
|
||
|
return this.animated;
|
||
|
},
|
||
|
|
||
|
getStartTime : function() {
|
||
|
return this.startTime;
|
||
|
},
|
||
|
|
||
|
doMethod: function(start, end) {
|
||
|
return this.method(this.currentFrame, start, end - start, this.totalFrames);
|
||
|
},
|
||
|
|
||
|
animate : function() {
|
||
|
if ( this.isAnimated() ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
this.currentFrame = 0;
|
||
|
|
||
|
this.totalFrames = ( this.useSeconds ) ? Math.ceil(YAHOO.util.AnimMgr.fps * this.duration) : this.duration;
|
||
|
|
||
|
if (this.duration === 0 && this.useSeconds) { // jump to last frame if zero second duration
|
||
|
this.totalFrames = 1;
|
||
|
}
|
||
|
YAHOO.util.AnimMgr.registerElement(this);
|
||
|
return true;
|
||
|
},
|
||
|
|
||
|
stop : function(finish) {
|
||
|
if (!this.isAnimated()) { // nothing to stop
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (finish) {
|
||
|
this.currentFrame = this.totalFrames;
|
||
|
this._onTween.fire();
|
||
|
}
|
||
|
YAHOO.util.AnimMgr.stop(this);
|
||
|
},
|
||
|
|
||
|
doOnStart : function() {
|
||
|
this.onStart.fire();
|
||
|
|
||
|
this.runtimeItems = [];
|
||
|
for (var i=0; i<this.animationItems.length; i++) {
|
||
|
this.setRuntimeItem(this.animationItems[i]);
|
||
|
}
|
||
|
|
||
|
this.animated = true;
|
||
|
this.actualFrames = 0;
|
||
|
this.startTime = new Date();
|
||
|
},
|
||
|
|
||
|
doOnTween : function() {
|
||
|
var data = {
|
||
|
duration: new Date() - this.getStartTime(),
|
||
|
currentFrame: this.currentFrame
|
||
|
};
|
||
|
|
||
|
data.toString = function() {
|
||
|
return (
|
||
|
'duration: ' + data.duration +
|
||
|
', currentFrame: ' + data.currentFrame
|
||
|
);
|
||
|
};
|
||
|
|
||
|
this.onTween.fire(data);
|
||
|
|
||
|
this.actualFrames += 1;
|
||
|
|
||
|
var runtimeItems = this.runtimeItems;
|
||
|
|
||
|
for (var i=0; i < runtimeItems.length; i++) {
|
||
|
this.setItemAttributes(runtimeItems[i]);
|
||
|
}
|
||
|
|
||
|
},
|
||
|
|
||
|
doOnComplete : function() {
|
||
|
var actual_duration = (new Date() - this.getStartTime()) / 1000 ;
|
||
|
|
||
|
var data = {
|
||
|
duration: actual_duration,
|
||
|
frames: this.actualFrames,
|
||
|
fps: this.actualFrames / actual_duration
|
||
|
};
|
||
|
|
||
|
data.toString = function() {
|
||
|
return (
|
||
|
'duration: ' + data.duration +
|
||
|
', frames: ' + data.frames +
|
||
|
', fps: ' + data.fps
|
||
|
);
|
||
|
};
|
||
|
|
||
|
this.animated = false;
|
||
|
this.actualFrames = 0;
|
||
|
this.onComplete.fire(data);
|
||
|
},
|
||
|
|
||
|
setRuntimeItem: function(item){
|
||
|
var runtimeItem = {};
|
||
|
runtimeItem.item = item.item;
|
||
|
runtimeItem.attribute = {};
|
||
|
for(var attr in item.attribute){
|
||
|
runtimeItem.attribute[attr] = item.attribute[attr];
|
||
|
if(attr == 'angle'){
|
||
|
if(item.attribute[attr].start - item.attribute[attr].end > 0){
|
||
|
runtimeItem.attribute[attr].perspectiveDirection = this.direction;
|
||
|
runtimeItem.attribute[attr].center = true;
|
||
|
}else{
|
||
|
runtimeItem.attribute[attr].perspectiveDirection = this.direction == CoverFlow.RIGHT ? CoverFlow.LEFT : CoverFlow.RIGHT;
|
||
|
runtimeItem.attribute[attr].center = false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
this.runtimeItems.push(runtimeItem);
|
||
|
},
|
||
|
|
||
|
setItemAttributes: function(item){
|
||
|
|
||
|
for(var attr in item.attribute){
|
||
|
|
||
|
var value = Math.ceil(this.doMethod(item.attribute[attr].start, item.attribute[attr].end));
|
||
|
|
||
|
if(attr == 'angle'){
|
||
|
item.item.setAngle(value);
|
||
|
var frameSize = Math.ceil(this.doMethod(3, 1));
|
||
|
item.item.drawInPerspective(item.attribute[attr].perspectiveDirection, frameSize);
|
||
|
var left;
|
||
|
if(item.attribute[attr].center){
|
||
|
left = this.doMethod(item.item.getLeft(), this.center - item.item.element.width/2);
|
||
|
}else{
|
||
|
if(this.direction == CoverFlow.LEFT)
|
||
|
left = this.doMethod(item.item.getLeft(), this.startLeftPos - item.item.element.width);
|
||
|
else
|
||
|
left = this.doMethod(item.item.getLeft(), this.startRightPos);
|
||
|
}
|
||
|
item.item.setLeft(Math.ceil(left));
|
||
|
|
||
|
}else{
|
||
|
item.item.setLeft(value);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
//});
|