438 lines
11 KiB
JavaScript
438 lines
11 KiB
JavaScript
Element.addMethods({
|
|
|
|
patchwork: function( element, options ){
|
|
|
|
options = Object.extend({
|
|
minSize: 180,
|
|
optimumSize: 200,
|
|
gutterSize: 10,
|
|
minRowCount: 2,
|
|
maxRowCount: 5,
|
|
switchImages: true,
|
|
switchInterval: 8,
|
|
showRandomImage: true
|
|
}, options);
|
|
|
|
var boxDims = {},
|
|
curColumns = null,
|
|
curRows = null,
|
|
columnCount,
|
|
itemWidth,
|
|
grid = [],
|
|
maxBigImages,
|
|
images = [],
|
|
totalImages,
|
|
doneInit = false,
|
|
origImageData,
|
|
rowCount;
|
|
|
|
//------------------------------------
|
|
// Our kinda-constructor
|
|
//------------------------------------
|
|
var _init = function(){
|
|
|
|
// copy image data to a variable that we won't
|
|
// change
|
|
origImageData = options.imageData.clone();
|
|
totalImages = options.imageData.length;
|
|
|
|
getBoxDims();
|
|
calculateColumnsAndRows();
|
|
_createBlocks();
|
|
resizeWrapper();
|
|
doneInit = true;
|
|
},
|
|
|
|
//------------------------------------
|
|
// Creates the grid our images sit on
|
|
//------------------------------------
|
|
_createGrid = function(){
|
|
grid = [];
|
|
for( var i = 0; i < rowCount; i++ ){
|
|
grid[i] = new Array( columnCount );
|
|
}
|
|
},
|
|
|
|
//------------------------------------
|
|
// Randomly positions the larger images
|
|
//------------------------------------
|
|
_allocateLargeImages = function(){
|
|
|
|
// Position the large images
|
|
var checkGrid = function(p){
|
|
return grid[ p[0] ][ p[1] ] == null;
|
|
}
|
|
|
|
var startX = Math.floor( Math.random() * ( columnCount - 1 ) );
|
|
var startY = Math.floor( Math.random() * ( rowCount - 1 ) );
|
|
var curX = startX;
|
|
var curY = startY;
|
|
|
|
// First loop to build each large image
|
|
// Second loop for each row
|
|
// Third loop for each column
|
|
ImageLoop:
|
|
for( var i = 0; i < maxBigImages; i++ ){
|
|
for( var n = 0; n < ( rowCount - 1 ); n++ ){
|
|
for( var m = 0; m < ( grid[ n ].length - 1 ); m++ ){
|
|
|
|
// The coords this image would use if it fits
|
|
var possibleCoords = [
|
|
[ curY, curX ],
|
|
[ curY + 1, curX ],
|
|
[ curY + 1, curX + 1 ],
|
|
[ curY, curX + 1 ]
|
|
];
|
|
|
|
// Check the coords are available, insert is possible
|
|
if( possibleCoords.all( checkGrid ) ){
|
|
grid[ curY ][ curX ] = 3;
|
|
grid[ curY+1 ][ curX+1 ] = 2;
|
|
grid[ curY ][ curX+1 ] = 2;
|
|
grid[ curY+1 ][ curX ] = 2;
|
|
continue ImageLoop;
|
|
}
|
|
|
|
// subtract 2... 1 so it's zero index
|
|
// another because our image is 2 wide so we'll not bother
|
|
// checking the last column - image would never fit anyway.
|
|
if( curX++ == ( columnCount - 2 ) ){
|
|
curX = 0;
|
|
}
|
|
}
|
|
|
|
// Same as curX loop - subtract 2
|
|
if( curY++ == ( rowCount - 2 ) ){
|
|
curY = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
},
|
|
|
|
//------------------------------------
|
|
// Fills small images and inits all
|
|
//------------------------------------
|
|
_fillAndInitImages = function(){
|
|
|
|
images = [];
|
|
|
|
for( var n = 0; n < rowCount; n++ ){
|
|
for( var m = 0; m < grid[ n ].length; m++ ){
|
|
|
|
if( grid[ n ][ m ] == null ){
|
|
images.push(
|
|
new patchworkImage( {
|
|
pos: [ n, m ],
|
|
size: 'small',
|
|
image: getNextImage(),
|
|
fade: !doneInit,
|
|
variance: getRandomVariance()
|
|
} )
|
|
);
|
|
} else if( grid[ n ][ m ] == 3 ){
|
|
images.push(
|
|
new patchworkImage( {
|
|
pos: [ n, m ],
|
|
size: 'large',
|
|
image: getNextImage(),
|
|
fade: !doneInit,
|
|
variance: getRandomVariance()
|
|
} )
|
|
);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
},
|
|
|
|
//------------------------------------
|
|
// Destroys existing images
|
|
//------------------------------------
|
|
destroyImages = function(){
|
|
images.each( function(item){
|
|
item.destroy();
|
|
delete item;
|
|
});
|
|
|
|
images = null;
|
|
},
|
|
|
|
//------------------------------------
|
|
// Calls methods in sequence that will
|
|
// build our grid and blocks
|
|
//------------------------------------
|
|
_createBlocks = function(){
|
|
|
|
maxBigImages = Math.round( Math.sqrt( rowCount * columnCount ) ) - 2;
|
|
if( maxBigImages < 1 ){ maxBigImages = 1; }
|
|
if( maxBigImages > 5 ){ maxBigImages = 5; }
|
|
|
|
Debug.write("Total images shown: " + ( rowCount * columnCount ) );
|
|
Debug.write("Max big images to show: " + maxBigImages );
|
|
|
|
_createGrid();
|
|
_allocateLargeImages();
|
|
_fillAndInitImages();
|
|
},
|
|
|
|
//------------------------------------
|
|
// Sets our wrapper dimensions variable
|
|
//------------------------------------
|
|
getBoxDims = function(){
|
|
boxDims = $( element ).getDimensions();
|
|
},
|
|
|
|
//------------------------------------
|
|
// Resizes the main wrapper to fit vertically
|
|
//------------------------------------
|
|
resizeWrapper = function(){
|
|
var height = calculateHeight( itemWidth );
|
|
|
|
$(element).setStyle({
|
|
'height': ( ( height * rowCount ) + ( options.gutterSize * ( rowCount - 1 ) ) ) + 'px'
|
|
});
|
|
},
|
|
|
|
//------------------------------------
|
|
// Calculate number and size of columns
|
|
//------------------------------------
|
|
calculateColumnsAndRows = function(){
|
|
var columns = parseInt( boxDims.width / options.optimumSize );
|
|
var leftover = boxDims.width % options.optimumSize;
|
|
|
|
// If what's left over is bigger than the minimum size,
|
|
// add an extra column. We'll resize the images.
|
|
if( leftover > options.minSize ){
|
|
columns++;
|
|
}
|
|
|
|
columnCount = columns;
|
|
|
|
// So our total item width is the box size divided by columns
|
|
itemWidth = Math.floor( ( boxDims.width - ( options.gutterSize * ( columnCount - 1 ) ) ) / columnCount );
|
|
|
|
// How many rows?
|
|
// Check whether rows * cols is more than
|
|
// the total number of images we have,
|
|
// and decrement as necessary.
|
|
var done = false;
|
|
var cur = options.maxRowCount;
|
|
|
|
do {
|
|
var c = ( cur * columnCount );
|
|
|
|
if( c < totalImages || cur == options.minRowCount ){
|
|
rowCount = cur;
|
|
done = true;
|
|
} else {
|
|
cur--;
|
|
}
|
|
} while( !done );
|
|
},
|
|
|
|
//------------------------------------
|
|
// Returns height value based on width value
|
|
//------------------------------------
|
|
calculateHeight = function( width ){
|
|
return ( width / 4 ) * 3;
|
|
},
|
|
|
|
//------------------------------------
|
|
// Returns a random image and then removes it from the array
|
|
// If we're out of elements, we repopulate and start again
|
|
//------------------------------------
|
|
getNextImage = function(){
|
|
if( options.imageData.length === 0 ){
|
|
_resetImageArray();
|
|
}
|
|
|
|
if( options.showRandomImage ){
|
|
var toreturn = options.imageData.splice( Math.floor( Math.random() * options.imageData.length ), 1 )[0];
|
|
|
|
while( typeof( toreturn ) == 'undefined' )
|
|
{
|
|
toreturn = options.imageData.splice( Math.floor( Math.random() * options.imageData.length ), 1 )[0];
|
|
}
|
|
} else {
|
|
var toreturn = options.imageData.splice( 0, 1 )[0];
|
|
}
|
|
|
|
return toreturn;
|
|
},
|
|
|
|
getRandomVariance = function(){
|
|
return ( Math.random() * options.switchInterval ).toFixed(3);
|
|
},
|
|
|
|
//------------------------------------
|
|
// resets options.imageData to its original value
|
|
//------------------------------------
|
|
_resetImageArray = function(){
|
|
options.imageData = origImageData.clone();
|
|
},
|
|
|
|
// Debug function
|
|
outputGrid = function( grid ){
|
|
for( var i = 0; i < grid.length; i++ ){
|
|
var rowOutput = '';
|
|
for( var j = 0; j < grid[i].length; j++ ){
|
|
if( grid[ i ][ j ] == 3 || grid[ i ][ j ] == 2 ){
|
|
rowOutput += "[x]";
|
|
} else if( grid[ i ][ j ] === 1 ) {
|
|
rowOutput += "[o]";
|
|
} else {
|
|
rowOutput += "[ ]";
|
|
}
|
|
}
|
|
Debug.write( rowOutput );
|
|
}
|
|
};
|
|
|
|
//------------------------------------
|
|
// Class for each block
|
|
//------------------------------------
|
|
var patchworkImage = Class.create({
|
|
|
|
// Constructor
|
|
initialize: function( data ){
|
|
var self = this;
|
|
this.data = data;
|
|
this.parentElement = element;
|
|
|
|
// Build inner element (which holds the image)
|
|
this.innerElement = new Element('div');
|
|
|
|
// Build outer element (which has a shadow)
|
|
this.outerElement = new Element('div')
|
|
.setStyle({
|
|
'position': 'absolute'
|
|
})
|
|
.addClassName('featured_image')
|
|
.addClassName('clickable')
|
|
.hide()
|
|
.insert( this.innerElement );
|
|
|
|
// Set the size and position of this block
|
|
this.sizeAndPosition();
|
|
|
|
// Set the background image on the inner element
|
|
this.setBackground( this.innerElement );
|
|
|
|
// Insert into our grid
|
|
$( this.parentElement ).insert( this.outerElement );
|
|
|
|
// Decide whether to fade in or not
|
|
if( this.data.fade ){
|
|
this.outerElement.appear({ delay: Math.random().toFixed(3), afterFinish: function(){
|
|
if( options.switchImages ){
|
|
self.switchImage.bind(self).delay( parseInt(options.switchInterval) + parseInt(self.data.variance) );
|
|
}
|
|
}});
|
|
} else {
|
|
this.outerElement.show();
|
|
}
|
|
|
|
this.outerElement.observe( 'click', function(e) {
|
|
window.location = this.data.image.image_url.replace( /&/, '&' );
|
|
}.bindAsEventListener(this) );
|
|
|
|
// Listen for resize event
|
|
$( this.parentElement ).on( 'patchwork:resize', this.resizeEvent.bindAsEventListener(this) );
|
|
},
|
|
|
|
switchImage: function(){
|
|
|
|
// Get new image
|
|
this.data.image = getNextImage();
|
|
|
|
var newDiv = this.setBackground( new Element('div').hide() );
|
|
|
|
this.outerElement.insert( newDiv );
|
|
|
|
new Effect.Parallel([
|
|
new Effect.Fade( this.innerElement, { sync: true } ),
|
|
new Effect.Appear( newDiv, { sync: true } )
|
|
], {
|
|
duration: 1.5,
|
|
afterFinish: function(){
|
|
this.innerElement.remove();
|
|
this.innerElement = newDiv;
|
|
}.bind(this)
|
|
});
|
|
|
|
if( options.switchImages ){
|
|
this.switchImage.bind(this).delay( parseInt(options.switchInterval) + parseInt(this.data.variance) );
|
|
}
|
|
},
|
|
|
|
// Sets the size and position of this block
|
|
sizeAndPosition: function(){
|
|
var itemHeight = calculateHeight( itemWidth );
|
|
|
|
this.outerElement.setStyle({
|
|
'left': ( this.data['pos'][1] * itemWidth ) + ( this.data['pos'][1] * options.gutterSize ) + 'px',
|
|
'top': ( this.data['pos'][0] * itemHeight ) + ( this.data['pos'][0] * options.gutterSize ) + 'px',
|
|
'width': ( ( this.data.size == 'large' ) ? ( itemWidth * 2 ) + options.gutterSize : itemWidth ) + 'px',
|
|
'height': ( ( this.data.size == 'large' ) ? ( itemHeight * 2 + options.gutterSize ) : itemHeight ) + 'px'
|
|
});
|
|
|
|
return this.outerElement;
|
|
},
|
|
|
|
// Sets the background image on an element
|
|
setBackground: function( elem ){
|
|
|
|
$( elem ).setStyle({
|
|
backgroundImage: 'url(' + this.data.image.med_img + ')',
|
|
backgroundPosition: 'center center',
|
|
backgroundRepeat: 'no-repeat'
|
|
});
|
|
|
|
if( this.data.image.thumb_size.w >= itemWidth ){
|
|
$( elem ).setStyle({
|
|
'background-size': 'cover'
|
|
});
|
|
}
|
|
|
|
return elem;
|
|
},
|
|
|
|
// Prepare to destroy ourselves
|
|
destroy: function(){
|
|
this.outerElement.remove();
|
|
},
|
|
|
|
// Catches the resize event fired by outer script
|
|
resizeEvent: function(e){
|
|
this.sizeAndPosition();
|
|
}
|
|
});
|
|
|
|
//------------------------------------
|
|
// Window resize event
|
|
//------------------------------------
|
|
Event.observe( window, 'resize', function(){
|
|
curColumns = columnCount;
|
|
curRows = rowCount;
|
|
getBoxDims();
|
|
calculateColumnsAndRows();
|
|
|
|
// Number of columns changed?
|
|
if( curColumns != columnCount || curRows != rowCount ){
|
|
destroyImages();
|
|
_resetImageArray();
|
|
_createBlocks();
|
|
}
|
|
|
|
resizeWrapper();
|
|
|
|
Event.fire( element, 'patchwork:resize' );
|
|
});
|
|
|
|
_init();
|
|
return element;
|
|
}
|
|
|
|
}); |