A Pen by Anya Melnyk on CodePen.
Created
April 16, 2020 06:17
-
-
Save rynkis/cefa4d1d83454baecb0d06ae83cfaa1f to your computer and use it in GitHub Desktop.
book flip page turn
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div class="container"> | |
<div class="book-wrapper"> | |
<div class="book-cover"> | |
<img src="https://github.com/slyka85/assets/blob/master/bookcover2.png?raw=true" alt=""> | |
</div> | |
<div class="pages-container"> | |
<div class="pages"> | |
<div class="page-num-1"> | |
<div class="pages-content"> | |
<div class="pages-background"></div> | |
<div class="content-inner"> | |
<h1>Chapter 1</h1> | |
<div class="text"><p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Voluptas ea non vitae a assumenda sint quod, dolores laboriosam velit corrupti nobis cupiditate perspiciatis natus exercitationem, architecto esse ratione blanditiis! Itaque.</p></div> | |
</div> | |
</div> | |
</div> | |
<div class="page-num-2"> | |
<div class="pages-content"> | |
<div class="content-inner"> | |
<h1>Chapter 2</h1> | |
<div class="text"> | |
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Eos, cumque autem! Magni eligendi qui officiis? Fugit iste voluptatum atque voluptatibus totam! Nisi accusantium saepe hic. Aut nobis nesciunt mollitia error.</p> | |
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Quibusdam odio modi, hic ratione fugit quod natus, excepturi quae minus voluptatum cupiditate quia magnam eveniet ex, reiciendis voluptates ipsam iste laudantium!</p> | |
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Obcaecati deserunt magnam, at perspiciatis aut. Voluptatem consequuntur neque quisquam?</p> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div class="page-num-3"> | |
<div class="pages-content"> | |
<div class="content-inner"> | |
<h1>Chapter 3</h1> | |
<div class="text"><p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Voluptate voluptas molestiae tempore amet adipisci dicta incidunt nisi alias distinctio fugit blanditiis dignissimos nobis deserunt eum consequuntur ipsam, perspiciatis numquam repellendus.</p> | |
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Delectus in odio deserunt est hic minima inventore, mollitia, officia aspernatur eaque voluptatibus? Amet, molestias adipisci delectus ea eligendi sit numquam illo.</p> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div class="page-num-4"> | |
<div class="pages-content"> | |
<div class="content-inner"> | |
<h1>The End</h1> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* turn.js 4th release | |
* turnjs.com | |
* turnjs.com/license.txt | |
* | |
* Copyright (C) 2012 Emmanuel Garcia | |
* All rights reserved | |
**/ | |
(function($) { | |
'use strict'; | |
var has3d, | |
hasRot, | |
vendor = '', | |
version = '4.1.0', | |
PI = Math.PI, | |
A90 = PI/2, | |
isTouch = 'ontouchstart' in window, | |
mouseEvents = (isTouch) ? | |
{ | |
down: 'touchstart', | |
move: 'touchmove', | |
up: 'touchend', | |
over: 'touchstart', | |
out: 'touchend' | |
} | |
: | |
{ | |
down: 'mousedown', | |
move: 'mousemove', | |
up: 'mouseup', | |
over: 'mouseover', | |
out: 'mouseout' | |
}, | |
// Contansts used for each corner | |
// | tl * tr | | |
// l | * * | r | |
// | bl * br | | |
corners = { | |
backward: ['bl', 'tl'], | |
forward: ['br', 'tr'], | |
all: ['tl', 'bl', 'tr', 'br', 'l', 'r'] | |
}, | |
// Display values | |
displays = ['single', 'double'], | |
// Direction values | |
directions = ['ltr', 'rtl'], | |
// Default options | |
turnOptions = { | |
// Enables hardware acceleration | |
acceleration: true, | |
// Display | |
display: 'double', | |
// Duration of transition in milliseconds | |
duration: 600, | |
// First page | |
page: 1, | |
// Enables gradients | |
gradients: true, | |
// Corners used when turning the page | |
turnCorners: 'bl,br', | |
// Events | |
when: null | |
}, | |
flipOptions = { | |
// Size of the active zone of each corner | |
cornerSize: 100 | |
}, | |
// Number of pages in the DOM, minimum value: 6 | |
pagesInDOM = 6, | |
turnMethods = { | |
// Singleton constructor | |
// $('#selector').turn([options]); | |
init: function(options) { | |
// Define constants | |
has3d = 'WebKitCSSMatrix' in window || 'MozPerspective' in document.body.style; | |
hasRot = rotationAvailable(); | |
vendor = getPrefix(); | |
var i, that = this, pageNum = 0, data = this.data(), ch = this.children(); | |
// Set initial configuration | |
options = $.extend({ | |
width: this.width(), | |
height: this.height(), | |
direction: this.attr('dir') || this.css('direction') || 'ltr' | |
}, turnOptions, options); | |
data.opts = options; | |
data.pageObjs = {}; | |
data.pages = {}; | |
data.pageWrap = {}; | |
data.pageZoom = {}; | |
data.pagePlace = {}; | |
data.pageMv = []; | |
data.zoom = 1; | |
data.totalPages = options.pages || 0; | |
data.eventHandlers = { | |
touchStart: $.proxy(turnMethods._touchStart, this), | |
touchMove: $.proxy(turnMethods._touchMove, this), | |
touchEnd: $.proxy(turnMethods._touchEnd, this), | |
start: $.proxy(turnMethods._eventStart, this) | |
}; | |
// Add event listeners | |
if (options.when) | |
for (i in options.when) | |
if (has(i, options.when)) | |
this.bind(i, options.when[i]); | |
// Set the css | |
this.css({position: 'relative', width: options.width, height: options.height}); | |
// Set the initial display | |
this.turn('display', options.display); | |
// Set the direction | |
if (options.direction!=='') | |
this.turn('direction', options.direction); | |
// Prevent blue screen problems of switching to hardware acceleration mode | |
// By forcing hardware acceleration for ever | |
if (has3d && !isTouch && options.acceleration) | |
this.transform(translate(0, 0, true)); | |
// Add pages from the DOM | |
for (i = 0; i<ch.length; i++) { | |
if ($(ch[i]).attr('ignore')!='1') { | |
this.turn('addPage', ch[i], ++pageNum); | |
} | |
} | |
// Event listeners | |
$(this).bind(mouseEvents.down, data.eventHandlers.touchStart). | |
bind('end', turnMethods._eventEnd). | |
bind('pressed', turnMethods._eventPressed). | |
bind('released', turnMethods._eventReleased). | |
bind('flip', turnMethods._flip); | |
$(this).parent().bind('start', data.eventHandlers.start); | |
$(document).bind(mouseEvents.move, data.eventHandlers.touchMove). | |
bind(mouseEvents.up, data.eventHandlers.touchEnd); | |
// Set the initial page | |
this.turn('page', options.page); | |
// This flipbook is ready | |
data.done = true; | |
return this; | |
}, | |
// Adds a page from external data | |
addPage: function(element, page) { | |
var currentPage, | |
className, | |
incPages = false, | |
data = this.data(), | |
lastPage = data.totalPages+1; | |
if (data.destroying) | |
return false; | |
// Read the page number from the className of `element` - format: p[0-9]+ | |
if ((currentPage = /\bp([0-9]+)\b/.exec($(element).attr('class')))) | |
page = parseInt(currentPage[1], 10); | |
if (page) { | |
if (page==lastPage) | |
incPages = true; | |
else if (page>lastPage) | |
throw turnError('Page "'+page+'" cannot be inserted'); | |
} else { | |
page = lastPage; | |
incPages = true; | |
} | |
if (page>=1 && page<=lastPage) { | |
if (data.display=='double') | |
className = (page%2) ? ' odd' : ' even'; | |
else | |
className = ''; | |
// Stop animations | |
if (data.done) | |
this.turn('stop'); | |
// Move pages if it's necessary | |
if (page in data.pageObjs) | |
turnMethods._movePages.call(this, page, 1); | |
// Increase the number of pages | |
if (incPages) | |
data.totalPages = lastPage; | |
// Add element | |
data.pageObjs[page] = $(element). | |
css({'float': 'left'}). | |
addClass('page p' + page + className); | |
if (!hasHardPage() && data.pageObjs[page].hasClass('hard')) { | |
data.pageObjs[page].removeClass('hard'); | |
} | |
// Add page | |
turnMethods._addPage.call(this, page); | |
// Remove pages out of range | |
turnMethods._removeFromDOM.call(this); | |
} | |
return this; | |
}, | |
// Adds a page | |
_addPage: function(page) { | |
var data = this.data(), | |
element = data.pageObjs[page]; | |
if (element) | |
if (turnMethods._necessPage.call(this, page)) { | |
if (!data.pageWrap[page]) { | |
// Wrapper | |
data.pageWrap[page] = $('<div/>', | |
{'class': 'page-wrapper', | |
page: page, | |
css: {position: 'absolute', | |
overflow: 'hidden'}}); | |
// Append to this flipbook | |
this.append(data.pageWrap[page]); | |
if (!data.pagePlace[page]) { | |
data.pagePlace[page] = page; | |
// Move `pageObjs[page]` to wrapper | |
data.pageObjs[page].appendTo(data.pageWrap[page]); | |
} | |
// Set the size of the page | |
var prop = turnMethods._pageSize.call(this, page, true); | |
element.css({width: prop.width, height: prop.height}); | |
data.pageWrap[page].css(prop); | |
} | |
if (data.pagePlace[page] == page) { | |
// If the page isn't in another place, create the flip effect | |
turnMethods._makeFlip.call(this, page); | |
} | |
} else { | |
// Place | |
data.pagePlace[page] = 0; | |
// Remove element from the DOM | |
if (data.pageObjs[page]) | |
data.pageObjs[page].remove(); | |
} | |
}, | |
// Checks if a page is in memory | |
hasPage: function(page) { | |
return has(page, this.data().pageObjs); | |
}, | |
// Centers the flipbook | |
center: function(page) { | |
var data = this.data(), | |
size = $(this).turn('size'), | |
left = 0; | |
if (!data.noCenter) { | |
if (data.display=='double') { | |
var view = this.turn('view', page || data.tpage || data.page); | |
if (data.direction=='ltr') { | |
if (!view[0]) | |
left -= size.width/4; | |
else if (!view[1]) | |
left += size.width/4; | |
} else { | |
if (!view[0]) | |
left += size.width/4; | |
else if (!view[1]) | |
left -= size.width/4; | |
} | |
} | |
$(this).css({marginLeft: left}); | |
} | |
return this; | |
}, | |
// Destroys the flipbook | |
destroy: function () { | |
var page, | |
flipbook = this, | |
data = this.data(), | |
events = [ | |
'end', 'first', 'flip', 'last', 'pressed', | |
'released', 'start', 'turning', 'turned', | |
'zooming', 'missing']; | |
if (trigger('destroying', this)=='prevented') | |
return; | |
data.destroying = true; | |
$.each(events, function(index, eventName) { | |
flipbook.unbind(eventName); | |
}); | |
this.parent().unbind('start', data.eventHandlers.start); | |
$(document).unbind(mouseEvents.move, data.eventHandlers.touchMove). | |
unbind(mouseEvents.up, data.eventHandlers.touchEnd); | |
while (data.totalPages!==0) { | |
this.turn('removePage', data.totalPages); | |
} | |
if (data.fparent) | |
data.fparent.remove(); | |
if (data.shadow) | |
data.shadow.remove(); | |
this.removeData(); | |
data = null; | |
return this; | |
}, | |
// Checks if this element is a flipbook | |
is: function() { | |
return typeof(this.data().pages)=='object'; | |
}, | |
// Sets and gets the zoom value | |
zoom: function(newZoom) { | |
var data = this.data(); | |
if (typeof(newZoom)=='number') { | |
if (newZoom<0.001 || newZoom>100) | |
throw turnError(newZoom+ ' is not a value for zoom'); | |
if (trigger('zooming', this, [newZoom, data.zoom])=='prevented') | |
return this; | |
var size = this.turn('size'), | |
currentView = this.turn('view'), | |
iz = 1/data.zoom, | |
newWidth = Math.round(size.width * iz * newZoom), | |
newHeight = Math.round(size.height * iz * newZoom); | |
data.zoom = newZoom; | |
$(this).turn('stop'). | |
turn('size', newWidth, newHeight); | |
/*. | |
css({marginTop: size.height * iz / 2 - newHeight / 2});*/ | |
if (data.opts.autoCenter) | |
this.turn('center'); | |
/*else | |
$(this).css({marginLeft: size.width * iz / 2 - newWidth / 2});*/ | |
turnMethods._updateShadow.call(this); | |
for (var i = 0; i<currentView.length; i++) { | |
if (currentView[i] && data.pageZoom[currentView[i]]!=data.zoom) { | |
this.trigger('zoomed',[ | |
currentView[i], | |
currentView, | |
data.pageZoom[currentView[i]], | |
data.zoom]); | |
data.pageZoom[currentView[i]] = data.zoom; | |
} | |
} | |
return this; | |
} else | |
return data.zoom; | |
}, | |
// Gets the size of a page | |
_pageSize: function(page, position) { | |
var data = this.data(), | |
prop = {}; | |
if (data.display=='single') { | |
prop.width = this.width(); | |
prop.height = this.height(); | |
if (position) { | |
prop.top = 0; | |
prop.left = 0; | |
prop.right = 'auto'; | |
} | |
} else { | |
var pageWidth = this.width()/2, | |
pageHeight = this.height(); | |
if (data.pageObjs[page].hasClass('own-size')) { | |
prop.width = data.pageObjs[page].width(); | |
prop.height = data.pageObjs[page].height(); | |
} else { | |
prop.width = pageWidth; | |
prop.height = pageHeight; | |
} | |
if (position) { | |
var odd = page%2; | |
prop.top = (pageHeight-prop.height)/2; | |
if (data.direction=='ltr') { | |
prop[(odd) ? 'right' : 'left'] = pageWidth-prop.width; | |
prop[(odd) ? 'left' : 'right'] = 'auto'; | |
} else { | |
prop[(odd) ? 'left' : 'right'] = pageWidth-prop.width; | |
prop[(odd) ? 'right' : 'left'] = 'auto'; | |
} | |
} | |
} | |
return prop; | |
}, | |
// Prepares the flip effect for a page | |
_makeFlip: function(page) { | |
var data = this.data(); | |
if (!data.pages[page] && data.pagePlace[page]==page) { | |
var single = data.display=='single', | |
odd = page%2; | |
data.pages[page] = data.pageObjs[page]. | |
css(turnMethods._pageSize.call(this, page)). | |
flip({ | |
page: page, | |
next: (odd || single) ? page+1 : page-1, | |
turn: this | |
}). | |
flip('disable', data.disabled); | |
// Issue about z-index | |
turnMethods._setPageLoc.call(this, page); | |
data.pageZoom[page] = data.zoom; | |
} | |
return data.pages[page]; | |
}, | |
// Makes pages within a range | |
_makeRange: function() { | |
var page, range, | |
data = this.data(); | |
if (data.totalPages<1) | |
return; | |
range = this.turn('range'); | |
for (page = range[0]; page<=range[1]; page++) | |
turnMethods._addPage.call(this, page); | |
}, | |
// Returns a range of pages that should be in the DOM | |
// Example: | |
// - page in the current view, return true | |
// * page is in the range, return true | |
// Otherwise, return false | |
// | |
// 1 2-3 4-5 6-7 8-9 10-11 12-13 | |
// ** ** -- ** ** | |
range: function(page) { | |
var remainingPages, left, right, view, | |
data = this.data(); | |
page = page || data.tpage || data.page || 1; | |
view = turnMethods._view.call(this, page); | |
if (page<1 || page>data.totalPages) | |
throw turnError('"'+page+'" is not a valid page'); | |
view[1] = view[1] || view[0]; | |
if (view[0]>=1 && view[1]<=data.totalPages) { | |
remainingPages = Math.floor((pagesInDOM-2)/2); | |
if (data.totalPages-view[1] > view[0]) { | |
left = Math.min(view[0]-1, remainingPages); | |
right = 2*remainingPages-left; | |
} else { | |
right = Math.min(data.totalPages-view[1], remainingPages); | |
left = 2*remainingPages-right; | |
} | |
} else { | |
left = pagesInDOM-1; | |
right = pagesInDOM-1; | |
} | |
return [Math.max(1, view[0]-left), | |
Math.min(data.totalPages, view[1]+right)]; | |
}, | |
// Detects if a page is within the range of `pagesInDOM` from the current view | |
_necessPage: function(page) { | |
if (page===0) | |
return true; | |
var range = this.turn('range'); | |
return this.data().pageObjs[page].hasClass('fixed') || | |
(page>=range[0] && page<=range[1]); | |
}, | |
// Releases memory by removing pages from the DOM | |
_removeFromDOM: function() { | |
var page, data = this.data(); | |
for (page in data.pageWrap) | |
if (has(page, data.pageWrap) && | |
!turnMethods._necessPage.call(this, page)) | |
turnMethods._removePageFromDOM.call(this, page); | |
}, | |
// Removes a page from DOM and its internal references | |
_removePageFromDOM: function(page) { | |
var data = this.data(); | |
if (data.pages[page]) { | |
var dd = data.pages[page].data(); | |
flipMethods._moveFoldingPage.call(data.pages[page], false); | |
if (dd.f && dd.f.fwrapper) | |
dd.f.fwrapper.remove(); | |
data.pages[page].removeData(); | |
data.pages[page].remove(); | |
delete data.pages[page]; | |
} | |
if (data.pageObjs[page]) | |
data.pageObjs[page].remove(); | |
if (data.pageWrap[page]) { | |
data.pageWrap[page].remove(); | |
delete data.pageWrap[page]; | |
} | |
turnMethods._removeMv.call(this, page); | |
delete data.pagePlace[page]; | |
delete data.pageZoom[page]; | |
}, | |
// Removes a page | |
removePage: function(page) { | |
var data = this.data(); | |
// Delete all the pages | |
if (page=='*') { | |
while (data.totalPages!==0) { | |
this.turn('removePage', data.totalPages); | |
} | |
} else { | |
if (page<1 || page>data.totalPages) | |
throw turnError('The page '+ page + ' doesn\'t exist'); | |
if (data.pageObjs[page]) { | |
// Stop animations | |
this.turn('stop'); | |
// Remove `page` | |
turnMethods._removePageFromDOM.call(this, page); | |
delete data.pageObjs[page]; | |
} | |
// Move the pages | |
turnMethods._movePages.call(this, page, -1); | |
// Resize the size of this flipbook | |
data.totalPages = data.totalPages-1; | |
// Check the current view | |
if (data.page>data.totalPages) { | |
data.page = null; | |
turnMethods._fitPage.call(this, data.totalPages); | |
} else { | |
turnMethods._makeRange.call(this); | |
this.turn('update'); | |
} | |
} | |
return this; | |
}, | |
// Moves pages | |
_movePages: function(from, change) { | |
var page, | |
that = this, | |
data = this.data(), | |
single = data.display=='single', | |
move = function(page) { | |
var next = page + change, | |
odd = next%2, | |
className = (odd) ? ' odd ' : ' even '; | |
if (data.pageObjs[page]) | |
data.pageObjs[next] = data.pageObjs[page]. | |
removeClass('p' + page + ' odd even'). | |
addClass('p' + next + className); | |
if (data.pagePlace[page] && data.pageWrap[page]) { | |
data.pagePlace[next] = next; | |
if (data.pageObjs[next].hasClass('fixed')) | |
data.pageWrap[next] = data.pageWrap[page]. | |
attr('page', next); | |
else | |
data.pageWrap[next] = data.pageWrap[page]. | |
css(turnMethods._pageSize.call(that, next, true)). | |
attr('page', next); | |
if (data.pages[page]) | |
data.pages[next] = data.pages[page]. | |
flip('options', { | |
page: next, | |
next: (single || odd) ? next+1 : next-1 | |
}); | |
if (change) { | |
delete data.pages[page]; | |
delete data.pagePlace[page]; | |
delete data.pageZoom[page]; | |
delete data.pageObjs[page]; | |
delete data.pageWrap[page]; | |
} | |
} | |
}; | |
if (change>0) | |
for (page=data.totalPages; page>=from; page--) | |
move(page); | |
else | |
for (page=from; page<=data.totalPages; page++) | |
move(page); | |
}, | |
// Sets or Gets the display mode | |
display: function(display) { | |
var data = this.data(), | |
currentDisplay = data.display; | |
if (display===undefined) { | |
return currentDisplay; | |
} else { | |
if ($.inArray(display, displays)==-1) | |
throw turnError('"'+display + '" is not a value for display'); | |
switch(display) { | |
case 'single': | |
// Create a temporal page to use as folded page | |
if (!data.pageObjs[0]) { | |
this.turn('stop'). | |
css({'overflow': 'hidden'}); | |
data.pageObjs[0] = $('<div />', | |
{'class': 'page p-temporal'}). | |
css({width: this.width(), height: this.height()}). | |
appendTo(this); | |
} | |
this.addClass('shadow'); | |
break; | |
case 'double': | |
// Remove the temporal page | |
if (data.pageObjs[0]) { | |
this.turn('stop').css({'overflow': ''}); | |
data.pageObjs[0].remove(); | |
delete data.pageObjs[0]; | |
} | |
this.removeClass('shadow'); | |
break; | |
} | |
data.display = display; | |
if (currentDisplay) { | |
var size = this.turn('size'); | |
turnMethods._movePages.call(this, 1, 0); | |
this.turn('size', size.width, size.height). | |
turn('update'); | |
} | |
return this; | |
} | |
}, | |
// Gets and sets the direction of the flipbook | |
direction: function(dir) { | |
var data = this.data(); | |
if (dir===undefined) { | |
return data.direction; | |
} else { | |
dir = dir.toLowerCase(); | |
if ($.inArray(dir, directions)==-1) | |
throw turnError('"' + dir + '" is not a value for direction'); | |
if (dir=='rtl') { | |
$(this).attr('dir', 'ltr'). | |
css({direction: 'ltr'}); | |
} | |
data.direction = dir; | |
if (data.done) | |
this.turn('size', $(this).width(), $(this).height()); | |
return this; | |
} | |
}, | |
// Detects animation | |
animating: function() { | |
return this.data().pageMv.length>0; | |
}, | |
// Gets the current activated corner | |
corner: function() { | |
var corner, | |
page, | |
data = this.data(); | |
for (page in data.pages) { | |
if (has(page, data.pages)) | |
if ((corner = data.pages[page].flip('corner'))) { | |
return corner; | |
} | |
} | |
return false; | |
}, | |
// Gets the data stored in the flipbook | |
data: function() { | |
return this.data(); | |
}, | |
// Disables and enables the effect | |
disable: function(disable) { | |
var page, | |
data = this.data(), | |
view = this.turn('view'); | |
data.disabled = disable===undefined || disable===true; | |
for (page in data.pages) { | |
if (has(page, data.pages)) | |
data.pages[page].flip('disable', | |
(data.disabled) ? true : $.inArray(parseInt(page, 10), view)==-1); | |
} | |
return this; | |
}, | |
// Disables and enables the effect | |
disabled: function(disable) { | |
if (disable===undefined) { | |
return this.data().disabled===true; | |
} else { | |
return this.turn('disable', disable); | |
} | |
}, | |
// Gets and sets the size | |
size: function(width, height) { | |
if (width===undefined || height===undefined) { | |
return {width: this.width(), height: this.height()}; | |
} else { | |
this.turn('stop'); | |
var page, prop, | |
data = this.data(), | |
pageWidth = (data.display=='double') ? width/2 : width; | |
this.css({width: width, height: height}); | |
if (data.pageObjs[0]) | |
data.pageObjs[0].css({width: pageWidth, height: height}); | |
for (page in data.pageWrap) { | |
if (!has(page, data.pageWrap)) continue; | |
prop = turnMethods._pageSize.call(this, page, true); | |
data.pageObjs[page].css({width: prop.width, height: prop.height}); | |
data.pageWrap[page].css(prop); | |
if (data.pages[page]) | |
data.pages[page].css({width: prop.width, height: prop.height}); | |
} | |
this.turn('resize'); | |
return this; | |
} | |
}, | |
// Resizes each page | |
resize: function() { | |
var page, data = this.data(); | |
if (data.pages[0]) { | |
data.pageWrap[0].css({left: -this.width()}); | |
data.pages[0].flip('resize', true); | |
} | |
for (page = 1; page <= data.totalPages; page++) | |
if (data.pages[page]) | |
data.pages[page].flip('resize', true); | |
turnMethods._updateShadow.call(this); | |
if (data.opts.autoCenter) | |
this.turn('center'); | |
}, | |
// Removes an animation from the cache | |
_removeMv: function(page) { | |
var i, data = this.data(); | |
for (i=0; i<data.pageMv.length; i++) | |
if (data.pageMv[i]==page) { | |
data.pageMv.splice(i, 1); | |
return true; | |
} | |
return false; | |
}, | |
// Adds an animation to the cache | |
_addMv: function(page) { | |
var data = this.data(); | |
turnMethods._removeMv.call(this, page); | |
data.pageMv.push(page); | |
}, | |
// Gets indexes for a view | |
_view: function(page) { | |
var data = this.data(); | |
page = page || data.page; | |
if (data.display=='double') | |
return (page%2) ? [page-1, page] : [page, page+1]; | |
else | |
return [page]; | |
}, | |
// Gets a view | |
view: function(page) { | |
var data = this.data(), | |
view = turnMethods._view.call(this, page); | |
if (data.display=='double') | |
return [(view[0]>0) ? view[0] : 0, | |
(view[1]<=data.totalPages) ? view[1] : 0]; | |
else | |
return [(view[0]>0 && view[0]<=data.totalPages) ? view[0] : 0]; | |
}, | |
// Stops animations | |
stop: function(ignore, animate) { | |
if (this.turn('animating')) { | |
var i, opts, page, | |
data = this.data(); | |
if (data.tpage) { | |
data.page = data.tpage; | |
delete data['tpage']; | |
} | |
for (i = 0; i<data.pageMv.length; i++) { | |
if (!data.pageMv[i] || data.pageMv[i]===ignore) | |
continue; | |
page = data.pages[data.pageMv[i]]; | |
opts = page.data().f.opts; | |
page.flip('hideFoldedPage', animate); | |
if (!animate) | |
flipMethods._moveFoldingPage.call(page, false); | |
if (opts.force) { | |
opts.next = (opts.page%2===0) ? opts.page-1 : opts.page+1; | |
delete opts['force']; | |
} | |
} | |
} | |
this.turn('update'); | |
return this; | |
}, | |
// Gets and sets the number of pages | |
pages: function(pages) { | |
var data = this.data(); | |
if (pages) { | |
if (pages<data.totalPages) { | |
for (var page = data.totalPages; page>pages; page--) | |
this.turn('removePage', page); | |
} | |
data.totalPages = pages; | |
turnMethods._fitPage.call(this, data.page); | |
return this; | |
} else | |
return data.totalPages; | |
}, | |
// Checks missing pages | |
_missing : function(page) { | |
var data = this.data(); | |
if (data.totalPages<1) | |
return; | |
var p, | |
range = this.turn('range', page), | |
missing = []; | |
for (p = range[0]; p<=range[1]; p++) { | |
if (!data.pageObjs[p]) | |
missing.push(p); | |
} | |
if (missing.length>0) | |
this.trigger('missing', [missing]); | |
}, | |
// Sets a page without effect | |
_fitPage: function(page) { | |
var data = this.data(), | |
newView = this.turn('view', page); | |
turnMethods._missing.call(this, page); | |
if (!data.pageObjs[page]) | |
return; | |
data.page = page; | |
this.turn('stop'); | |
for (var i = 0; i<newView.length; i++) { | |
if (newView[i] && data.pageZoom[newView[i]]!=data.zoom) { | |
this.trigger('zoomed',[ | |
newView[i], | |
newView, | |
data.pageZoom[newView[i]], | |
data.zoom]); | |
data.pageZoom[newView[i]] = data.zoom; | |
} | |
} | |
turnMethods._removeFromDOM.call(this); | |
turnMethods._makeRange.call(this); | |
turnMethods._updateShadow.call(this); | |
this.trigger('turned', [page, newView]); | |
this.turn('update'); | |
if (data.opts.autoCenter) | |
this.turn('center'); | |
}, | |
// Turns the page | |
_turnPage: function(page) { | |
var current, | |
next, | |
data = this.data(), | |
place = data.pagePlace[page], | |
view = this.turn('view'), | |
newView = this.turn('view', page); | |
if (data.page!=page) { | |
var currentPage = data.page; | |
if (trigger('turning', this, [page, newView])=='prevented') { | |
if (currentPage==data.page && $.inArray(place, data.pageMv)!=-1) | |
data.pages[place].flip('hideFoldedPage', true); | |
return; | |
} | |
if ($.inArray(1, newView)!=-1) | |
this.trigger('first'); | |
if ($.inArray(data.totalPages, newView)!=-1) | |
this.trigger('last'); | |
} | |
if (data.display=='single') { | |
current = view[0]; | |
next = newView[0]; | |
} else if (view[1] && page>view[1]) { | |
current = view[1]; | |
next = newView[0]; | |
} else if (view[0] && page<view[0]) { | |
current = view[0]; | |
next = newView[1]; | |
} | |
var optsCorners = data.opts.turnCorners.split(','), | |
flipData = data.pages[current].data().f, | |
opts = flipData.opts, | |
actualPoint = flipData.point; | |
turnMethods._missing.call(this, page); | |
if (!data.pageObjs[page]) | |
return; | |
this.turn('stop'); | |
data.page = page; | |
turnMethods._makeRange.call(this); | |
data.tpage = next; | |
if (opts.next!=next) { | |
opts.next = next; | |
opts.force = true; | |
} | |
this.turn('update'); | |
flipData.point = actualPoint; | |
if (flipData.effect=='hard') | |
if (data.direction=='ltr') | |
data.pages[current].flip('turnPage', | |
(page>current) ? 'r' : 'l'); | |
else | |
data.pages[current].flip('turnPage', | |
(page>current) ? 'l' : 'r'); | |
else { | |
if (data.direction=='ltr') | |
data.pages[current].flip('turnPage', | |
optsCorners[(page>current) ? 1 : 0]); | |
else | |
data.pages[current].flip('turnPage', | |
optsCorners[(page>current) ? 0 : 1]); | |
} | |
}, | |
// Gets and sets a page | |
page: function(page) { | |
var data = this.data(); | |
if (page===undefined) { | |
return data.page; | |
} else { | |
if (!data.disabled && !data.destroying) { | |
page = parseInt(page, 10); | |
if (page>0 && page<=data.totalPages) { | |
if (page!=data.page) { | |
if (!data.done || $.inArray(page, this.turn('view'))!=-1) | |
turnMethods._fitPage.call(this, page); | |
else | |
turnMethods._turnPage.call(this, page); | |
} | |
return this; | |
} else { | |
throw turnError('The page ' + page + ' does not exist'); | |
} | |
} | |
} | |
}, | |
// Turns to the next view | |
next: function() { | |
return this.turn('page', Math.min(this.data().totalPages, | |
turnMethods._view.call(this, this.data().page).pop() + 1)); | |
}, | |
// Turns to the previous view | |
previous: function() { | |
return this.turn('page', Math.max(1, | |
turnMethods._view.call(this, this.data().page).shift() - 1)); | |
}, | |
// Shows a peeling corner | |
peel: function(corner, animate) { | |
var data = this.data(), | |
view = this.turn('view'); | |
animate = (animate===undefined) ? true : animate===true; | |
if (corner===false) { | |
this.turn('stop', null, animate); | |
} else { | |
if (data.display=='single') { | |
data.pages[data.page].flip('peel', corner, animate); | |
} else { | |
var page; | |
if (data.direction=='ltr') { | |
page = (corner.indexOf('l')!=-1) ? view[0] : view[1]; | |
} else { | |
page = (corner.indexOf('l')!=-1) ? view[1] : view[0]; | |
} | |
if (data.pages[page]) | |
data.pages[page].flip('peel', corner, animate); | |
} | |
} | |
return this; | |
}, | |
// Adds a motion to the internal list | |
// This event is called in context of flip | |
_addMotionPage: function() { | |
var opts = $(this).data().f.opts, | |
turn = opts.turn, | |
dd = turn.data(); | |
turnMethods._addMv.call(turn, opts.page); | |
}, | |
// This event is called in context of flip | |
_eventStart: function(e, opts, corner) { | |
var data = opts.turn.data(), | |
actualZoom = data.pageZoom[opts.page]; | |
if (e.isDefaultPrevented()) { | |
turnMethods._updateShadow.call(opts.turn); | |
return; | |
} | |
if (actualZoom && actualZoom!=data.zoom) { | |
opts.turn.trigger('zoomed',[ | |
opts.page, | |
opts.turn.turn('view', opts.page), | |
actualZoom, | |
data.zoom]); | |
data.pageZoom[opts.page] = data.zoom; | |
} | |
if (data.display=='single' && corner) { | |
if ((corner.charAt(1)=='l' && data.direction=='ltr') || | |
(corner.charAt(1)=='r' && data.direction=='rtl')) | |
{ | |
opts.next = (opts.next<opts.page) ? opts.next : opts.page-1; | |
opts.force = true; | |
} else { | |
opts.next = (opts.next>opts.page) ? opts.next : opts.page+1; | |
} | |
} | |
turnMethods._addMotionPage.call(e.target); | |
turnMethods._updateShadow.call(opts.turn); | |
}, | |
// This event is called in context of flip | |
_eventEnd: function(e, opts, turned) { | |
var that = $(e.target), | |
data = that.data().f, | |
turn = opts.turn, | |
dd = turn.data(); | |
if (turned) { | |
var tpage = dd.tpage || dd.page; | |
if (tpage==opts.next || tpage==opts.page) { | |
delete dd.tpage; | |
turnMethods._fitPage.call(turn, tpage || opts.next, true); | |
} | |
} else { | |
turnMethods._removeMv.call(turn, opts.page); | |
turnMethods._updateShadow.call(turn); | |
turn.turn('update'); | |
} | |
}, | |
// This event is called in context of flip | |
_eventPressed: function(e) { | |
var page, | |
data = $(e.target).data().f, | |
turn = data.opts.turn, | |
turnData = turn.data(), | |
pages = turnData.pages; | |
turnData.mouseAction = true; | |
turn.turn('update'); | |
return data.time = new Date().getTime(); | |
}, | |
// This event is called in context of flip | |
_eventReleased: function(e, point) { | |
var outArea, | |
page = $(e.target), | |
data = page.data().f, | |
turn = data.opts.turn, | |
turnData = turn.data(); | |
if (turnData.display=='single') { | |
outArea = (point.corner=='br' || point.corner=='tr') ? | |
point.x<page.width()/2: | |
point.x>page.width()/2; | |
} else { | |
outArea = point.x<0 || point.x>page.width(); | |
} | |
if ((new Date()).getTime()-data.time<200 || outArea) { | |
e.preventDefault(); | |
turnMethods._turnPage.call(turn, data.opts.next); | |
} | |
turnData.mouseAction = false; | |
}, | |
// This event is called in context of flip | |
_flip: function(e) { | |
e.stopPropagation(); | |
var opts = $(e.target).data().f.opts; | |
opts.turn.trigger('turn', [opts.next]); | |
if (opts.turn.data().opts.autoCenter) { | |
opts.turn.turn('center', opts.next); | |
} | |
}, | |
// | |
_touchStart: function() { | |
var data = this.data(); | |
for (var page in data.pages) { | |
if (has(page, data.pages) && | |
flipMethods._eventStart.apply(data.pages[page], arguments)===false) { | |
return false; | |
} | |
} | |
}, | |
// | |
_touchMove: function() { | |
var data = this.data(); | |
for (var page in data.pages) { | |
if (has(page, data.pages)) { | |
flipMethods._eventMove.apply(data.pages[page], arguments); | |
} | |
} | |
}, | |
// | |
_touchEnd: function() { | |
var data = this.data(); | |
for (var page in data.pages) { | |
if (has(page, data.pages)) { | |
flipMethods._eventEnd.apply(data.pages[page], arguments); | |
} | |
} | |
}, | |
// Calculate the z-index value for pages during the animation | |
calculateZ: function(mv) { | |
var i, page, nextPage, placePage, dpage, | |
that = this, | |
data = this.data(), | |
view = this.turn('view'), | |
currentPage = view[0] || view[1], | |
total = mv.length-1, | |
r = {pageZ: {}, partZ: {}, pageV: {}}, | |
addView = function(page) { | |
var view = that.turn('view', page); | |
if (view[0]) r.pageV[view[0]] = true; | |
if (view[1]) r.pageV[view[1]] = true; | |
}; | |
for (i = 0; i<=total; i++) { | |
page = mv[i]; | |
nextPage = data.pages[page].data().f.opts.next; | |
placePage = data.pagePlace[page]; | |
addView(page); | |
addView(nextPage); | |
dpage = (data.pagePlace[nextPage]==nextPage) ? nextPage : page; | |
r.pageZ[dpage] = data.totalPages - Math.abs(currentPage-dpage); | |
r.partZ[placePage] = data.totalPages*2 - total + i; | |
} | |
return r; | |
}, | |
// Updates the z-index and display property of every page | |
update: function() { | |
var page, | |
data = this.data(); | |
if (this.turn('animating') && data.pageMv[0]!==0) { | |
// Update motion | |
var p, apage, fixed, | |
pos = this.turn('calculateZ', data.pageMv), | |
corner = this.turn('corner'), | |
actualView = this.turn('view'), | |
newView = this.turn('view', data.tpage); | |
for (page in data.pageWrap) { | |
if (!has(page, data.pageWrap)) | |
continue; | |
fixed = data.pageObjs[page].hasClass('fixed'); | |
data.pageWrap[page].css({ | |
display: (pos.pageV[page] || fixed) ? '' : 'none', | |
zIndex: | |
(data.pageObjs[page].hasClass('hard') ? | |
pos.partZ[page] | |
: | |
pos.pageZ[page] | |
) || (fixed ? -1 : 0) | |
}); | |
if ((p = data.pages[page])) { | |
p.flip('z', pos.partZ[page] || null); | |
if (pos.pageV[page]) | |
p.flip('resize'); | |
if (data.tpage) { // Is it turning the page to `tpage`? | |
p.flip('hover', false). | |
flip('disable', | |
$.inArray(parseInt(page, 10), data.pageMv)==-1 && | |
page!=newView[0] && | |
page!=newView[1]); | |
} else { | |
p.flip('hover', corner===false). | |
flip('disable', page!=actualView[0] && page!=actualView[1]); | |
} | |
} | |
} | |
} else { | |
// Update static pages | |
for (page in data.pageWrap) { | |
if (!has(page, data.pageWrap)) | |
continue; | |
var pageLocation = turnMethods._setPageLoc.call(this, page); | |
if (data.pages[page]) { | |
data.pages[page]. | |
flip('disable', data.disabled || pageLocation!=1). | |
flip('hover', true). | |
flip('z', null); | |
} | |
} | |
} | |
return this; | |
}, | |
// Updates the position and size of the flipbook's shadow | |
_updateShadow: function() { | |
var view, view2, shadow, | |
data = this.data(), | |
width = this.width(), | |
height = this.height(), | |
pageWidth = (data.display=='single') ? width : width/2; | |
view = this.turn('view'); | |
if (!data.shadow) { | |
data.shadow = $('<div />', { | |
'class': 'shadow', | |
'css': divAtt(0, 0, 0).css | |
}). | |
appendTo(this); | |
} | |
for (var i = 0; i<data.pageMv.length; i++) { | |
if (!view[0] || !view[1]) | |
break; | |
view = this.turn('view', data.pages[data.pageMv[i]].data().f.opts.next); | |
view2 = this.turn('view', data.pageMv[i]); | |
view[0] = view[0] && view2[0]; | |
view[1] = view[1] && view2[1]; | |
} | |
if (!view[0]) shadow = (data.direction=='ltr') ? 1 : 2; | |
else if (!view[1]) shadow = (data.direction=='ltr') ? 2 : 1; | |
else shadow = 3; | |
switch (shadow) { | |
case 1: | |
data.shadow.css({ | |
width: pageWidth, | |
height: height, | |
top: 0, | |
left: pageWidth | |
}); | |
break; | |
case 2: | |
data.shadow.css({ | |
width: pageWidth, | |
height: height, | |
top: 0, | |
left: 0 | |
}); | |
break; | |
case 3: | |
data.shadow.css({ | |
width: width, | |
height: height, | |
top: 0, | |
left: 0 | |
}); | |
break; | |
} | |
}, | |
// Sets the z-index and display property of a page | |
// It depends on the current view | |
_setPageLoc: function(page) { | |
var data = this.data(), | |
view = this.turn('view'), | |
loc = 0; | |
if (page==view[0] || page==view[1]) | |
loc = 1; | |
else if ( | |
(data.display=='single' && page==view[0]+1) || | |
(data.display=='double' && page==view[0]-2 || page==view[1]+2) | |
) | |
loc = 2; | |
if (!this.turn('animating')) | |
switch (loc) { | |
case 1: | |
data.pageWrap[page].css( | |
{ | |
zIndex: data.totalPages, | |
display: '' | |
}); | |
break; | |
case 2: | |
data.pageWrap[page].css( | |
{ | |
zIndex: data.totalPages-1, | |
display: '' | |
}); | |
break; | |
case 0: | |
data.pageWrap[page].css( | |
{ | |
zIndex: 0, | |
display: (data.pageObjs[page].hasClass('fixed')) ? '' : 'none'} | |
); | |
break; | |
} | |
return loc; | |
}, | |
// Gets and sets the options | |
options: function(options) { | |
if (options===undefined) { | |
return this.data().opts; | |
} else { | |
var data = this.data(); | |
// Set new values | |
$.extend(data.opts, options); | |
// Set pages | |
if (options.pages) | |
this.turn('pages', options.pages); | |
// Set page | |
if (options.page) | |
this.turn('page', options.page); | |
// Set display | |
if (options.display) | |
this.turn('display', options.display); | |
// Set direction | |
if (options.direction) | |
this.turn('direction', options.direction); | |
// Set size | |
if (options.width && options.height) | |
this.turn('size', options.width, options.height); | |
// Add event listeners | |
if (options.when) | |
for (var eventName in options.when) | |
if (has(eventName, options.when)) { | |
this.unbind(eventName). | |
bind(eventName, options.when[eventName]); | |
} | |
return this; | |
} | |
}, | |
// Gets the current version | |
version: function() { | |
return version; | |
} | |
}, | |
// Methods and properties for the flip page effect | |
flipMethods = { | |
// Constructor | |
init: function(opts) { | |
this.data({f: { | |
disabled: false, | |
hover: false, | |
effect: (this.hasClass('hard')) ? 'hard' : 'sheet' | |
}}); | |
this.flip('options', opts); | |
flipMethods._addPageWrapper.call(this); | |
return this; | |
}, | |
setData: function(d) { | |
var data = this.data(); | |
data.f = $.extend(data.f, d); | |
return this; | |
}, | |
options: function(opts) { | |
var data = this.data().f; | |
if (opts) { | |
flipMethods.setData.call(this, | |
{opts: $.extend({}, data.opts || flipOptions, opts)}); | |
return this; | |
} else | |
return data.opts; | |
}, | |
z: function(z) { | |
var data = this.data().f; | |
data.opts['z-index'] = z; | |
if (data.fwrapper) | |
data.fwrapper.css({ | |
zIndex: z || parseInt(data.parent.css('z-index'), 10) || 0 | |
}); | |
return this; | |
}, | |
_cAllowed: function() { | |
var data = this.data().f, | |
page = data.opts.page, | |
turnData = data.opts.turn.data(), | |
odd = page%2; | |
if (data.effect=='hard') { | |
return (turnData.direction=='ltr') ? | |
[(odd) ? 'r' : 'l'] : | |
[(odd) ? 'l' : 'r']; | |
} else { | |
if (turnData.display=='single') { | |
if (page==1) | |
return (turnData.direction=='ltr') ? | |
corners['forward'] : corners['backward']; | |
else if (page==turnData.totalPages) | |
return (turnData.direction=='ltr') ? | |
corners['backward'] : corners['forward']; | |
else | |
return corners['all']; | |
} else { | |
return (turnData.direction=='ltr') ? | |
corners[(odd) ? 'forward' : 'backward'] | |
: | |
corners[(odd) ? 'backward' : 'forward']; | |
} | |
} | |
}, | |
_cornerActivated: function(p) { | |
var data = this.data().f, | |
width = this.width(), | |
height = this.height(), | |
point = {x: p.x, y: p.y, corner: ''}, | |
csz = data.opts.cornerSize; | |
if (point.x<=0 || point.y<=0 || point.x>=width || point.y>=height) | |
return false; | |
var allowedCorners = flipMethods._cAllowed.call(this); | |
switch (data.effect) { | |
case 'hard': | |
if (point.x>width-csz) | |
point.corner = 'r'; | |
else if (point.x<csz) | |
point.corner = 'l'; | |
else | |
return false; | |
break; | |
case 'sheet': | |
if (point.y<csz) | |
point.corner+= 't'; | |
else if (point.y>=height-csz) | |
point.corner+= 'b'; | |
else | |
return false; | |
if (point.x<=csz) | |
point.corner+= 'l'; | |
else if (point.x>=width-csz) | |
point.corner+= 'r'; | |
else | |
return false; | |
break; | |
} | |
return (!point.corner || $.inArray(point.corner, allowedCorners)==-1) ? | |
false : point; | |
}, | |
_isIArea: function(e) { | |
var pos = this.data().f.parent.offset(); | |
e = (isTouch && e.originalEvent) ? e.originalEvent.touches[0] : e; | |
return flipMethods._cornerActivated.call(this, | |
{ | |
x: e.pageX-pos.left, | |
y: e.pageY-pos.top | |
}); | |
}, | |
_c: function(corner, opts) { | |
opts = opts || 0; | |
switch (corner) { | |
case 'tl': | |
return point2D(opts, opts); | |
case 'tr': | |
return point2D(this.width()-opts, opts); | |
case 'bl': | |
return point2D(opts, this.height()-opts); | |
case 'br': | |
return point2D(this.width()-opts, this.height()-opts); | |
case 'l': | |
return point2D(opts, 0); | |
case 'r': | |
return point2D(this.width()-opts, 0); | |
} | |
}, | |
_c2: function(corner) { | |
switch (corner) { | |
case 'tl': | |
return point2D(this.width()*2, 0); | |
case 'tr': | |
return point2D(-this.width(), 0); | |
case 'bl': | |
return point2D(this.width()*2, this.height()); | |
case 'br': | |
return point2D(-this.width(), this.height()); | |
case 'l': | |
return point2D(this.width()*2, 0); | |
case 'r': | |
return point2D(-this.width(), 0); | |
} | |
}, | |
_foldingPage: function() { | |
var data = this.data().f; | |
if (!data) | |
return; | |
var opts = data.opts; | |
if (opts.turn) { | |
data = opts.turn.data(); | |
if (data.display == 'single') | |
return (opts.next>1 || opts.page>1) ? data.pageObjs[0] : null; | |
else | |
return data.pageObjs[opts.next]; | |
} | |
}, | |
_backGradient: function() { | |
var data = this.data().f, | |
turnData = data.opts.turn.data(), | |
gradient = turnData.opts.gradients && (turnData.display=='single' || | |
(data.opts.page!=2 && data.opts.page!=turnData.totalPages-1)); | |
if (gradient && !data.bshadow) | |
data.bshadow = $('<div/>', divAtt(0, 0, 1)). | |
css({'position': '', width: this.width(), height: this.height()}). | |
appendTo(data.parent); | |
return gradient; | |
}, | |
type: function () { | |
return this.data().f.effect; | |
}, | |
resize: function(full) { | |
var data = this.data().f, | |
turnData = data.opts.turn.data(), | |
width = this.width(), | |
height = this.height(); | |
switch (data.effect) { | |
case 'hard': | |
if (full) { | |
data.wrapper.css({width: width, height: height}); | |
data.fpage.css({width: width, height: height}); | |
if (turnData.opts.gradients) { | |
data.ashadow.css({width: width, height: height}); | |
data.bshadow.css({width: width, height: height}); | |
} | |
} | |
break; | |
case 'sheet': | |
if (full) { | |
var size = Math.round(Math.sqrt(Math.pow(width, 2)+Math.pow(height, 2))); | |
data.wrapper.css({width: size, height: size}); | |
data.fwrapper.css({width: size, height: size}). | |
children(':first-child'). | |
css({width: width, height: height}); | |
data.fpage.css({width: width, height: height}); | |
if (turnData.opts.gradients) | |
data.ashadow.css({width: width, height: height}); | |
if (flipMethods._backGradient.call(this)) | |
data.bshadow.css({width: width, height: height}); | |
} | |
if (data.parent.is(':visible')) { | |
var offset = findPos(data.parent[0]); | |
data.fwrapper.css({top: offset.top, | |
left: offset.left}); | |
//if (data.opts.turn) { | |
offset = findPos(data.opts.turn[0]); | |
data.fparent.css({top: -offset.top, left: -offset.left}); | |
//} | |
} | |
this.flip('z', data.opts['z-index']); | |
break; | |
} | |
}, | |
// Prepares the page by adding a general wrapper and another objects | |
_addPageWrapper: function() { | |
var att, | |
data = this.data().f, | |
turnData = data.opts.turn.data(), | |
parent = this.parent(); | |
data.parent = parent; | |
if (!data.wrapper) | |
switch (data.effect) { | |
case 'hard': | |
var cssProperties = {}; | |
cssProperties[vendor + 'transform-style'] = 'preserve-3d'; | |
cssProperties[vendor + 'backface-visibility'] = 'hidden'; | |
data.wrapper = $('<div/>', divAtt(0, 0, 2)). | |
css(cssProperties). | |
appendTo(parent). | |
prepend(this); | |
data.fpage = $('<div/>', divAtt(0, 0, 1)). | |
css(cssProperties). | |
appendTo(parent); | |
if (turnData.opts.gradients) { | |
data.ashadow = $('<div/>', divAtt(0, 0, 0)). | |
hide(). | |
appendTo(parent); | |
data.bshadow = $('<div/>', divAtt(0, 0, 0)); | |
} | |
break; | |
case 'sheet': | |
var width = this.width(), | |
height = this.height(), | |
size = Math.round(Math.sqrt(Math.pow(width, 2)+Math.pow(height, 2))); | |
data.fparent = data.opts.turn.data().fparent; | |
if (!data.fparent) { | |
var fparent = $('<div/>', {css: {'pointer-events': 'none'}}).hide(); | |
fparent.data().flips = 0; | |
fparent.css(divAtt(0, 0, 'auto', 'visible').css). | |
appendTo(data.opts.turn); | |
data.opts.turn.data().fparent = fparent; | |
data.fparent = fparent; | |
} | |
this.css({position: 'absolute', top: 0, left: 0, bottom: 'auto', right: 'auto'}); | |
data.wrapper = $('<div/>', divAtt(0, 0, this.css('z-index'))). | |
appendTo(parent). | |
prepend(this); | |
data.fwrapper = $('<div/>', divAtt(parent.offset().top, parent.offset().left)). | |
hide(). | |
appendTo(data.fparent); | |
data.fpage = $('<div/>', divAtt(0, 0, 0, 'visible')). | |
css({cursor: 'default'}). | |
appendTo(data.fwrapper); | |
if (turnData.opts.gradients) | |
data.ashadow = $('<div/>', divAtt(0, 0, 1)). | |
appendTo(data.fpage); | |
flipMethods.setData.call(this, data); | |
break; | |
} | |
// Set size | |
flipMethods.resize.call(this, true); | |
}, | |
// Takes a 2P point from the screen and applies the transformation | |
_fold: function(point) { | |
var data = this.data().f, | |
turnData = data.opts.turn.data(), | |
o = flipMethods._c.call(this, point.corner), | |
width = this.width(), | |
height = this.height(); | |
switch (data.effect) { | |
case 'hard': | |
if (point.corner=='l') | |
point.x = Math.min(Math.max(point.x, 0), width*2); | |
else | |
point.x = Math.max(Math.min(point.x, width), -width); | |
var leftPos, | |
shadow, | |
gradientX, | |
fpageOrigin, | |
parentOrigin, | |
totalPages = turnData.totalPages, | |
zIndex = data.opts['z-index'] || totalPages, | |
parentCss = {'overflow': 'visible'}, | |
relX = (o.x) ? (o.x - point.x)/width : point.x/width, | |
angle = relX * 90, | |
half = angle<90; | |
switch (point.corner) { | |
case 'l': | |
fpageOrigin = '0% 50%'; | |
parentOrigin = '100% 50%'; | |
if (half) { | |
leftPos = 0; | |
shadow = data.opts.next-1>0; | |
gradientX = 1; | |
} else { | |
leftPos = '100%'; | |
shadow = data.opts.page+1<totalPages; | |
gradientX = 0; | |
} | |
break; | |
case 'r': | |
fpageOrigin = '100% 50%'; | |
parentOrigin = '0% 50%'; | |
angle = -angle; | |
width = -width; | |
if (half) { | |
leftPos = 0; | |
shadow = data.opts.next+1<totalPages; | |
gradientX = 0; | |
} else { | |
leftPos = '-100%'; | |
shadow = data.opts.page!=1; | |
gradientX = 1; | |
} | |
break; | |
} | |
parentCss[vendor+'perspective-origin'] = parentOrigin; | |
data.wrapper.transform('rotateY('+angle+'deg)' + | |
'translate3d(0px, 0px, '+(this.attr('depth')||0)+'px)', parentOrigin); | |
data.fpage.transform('translateX('+width+'px) rotateY('+(180+angle)+'deg)', fpageOrigin); | |
data.parent.css(parentCss); | |
if (half) { | |
relX = -relX+1; | |
data.wrapper.css({zIndex: zIndex+1}); | |
data.fpage.css({zIndex: zIndex}); | |
} else { | |
relX = relX-1; | |
data.wrapper.css({zIndex: zIndex}); | |
data.fpage.css({zIndex: zIndex+1}); | |
} | |
if (turnData.opts.gradients) { | |
if (shadow) | |
data.ashadow.css({ | |
display: '', | |
left: leftPos, | |
backgroundColor: 'rgba(0,0,0,'+(0.5*relX)+')' | |
}). | |
transform('rotateY(0deg)'); | |
else | |
data.ashadow.hide(); | |
data.bshadow.css({opacity:-relX + 1}); | |
if (half) { | |
if (data.bshadow.parent()[0]!=data.wrapper[0]) { | |
data.bshadow.appendTo(data.wrapper); | |
} | |
} else { | |
if (data.bshadow.parent()[0]!=data.fpage[0]) { | |
data.bshadow.appendTo(data.fpage); | |
} | |
} | |
/*data.bshadow.css({ | |
backgroundColor: 'rgba(0,0,0,'+(0.1)+')' | |
})*/ | |
gradient(data.bshadow, point2D(gradientX * 100, 0), point2D((-gradientX + 1)*100, 0), | |
[[0, 'rgba(0,0,0,0.3)'],[1, 'rgba(0,0,0,0)']],2); | |
} | |
break; | |
case 'sheet': | |
var that = this, | |
a = 0, | |
alpha = 0, | |
beta, | |
px, | |
gradientEndPointA, | |
gradientEndPointB, | |
gradientStartVal, | |
gradientSize, | |
gradientOpacity, | |
shadowVal, | |
mv = point2D(0, 0), | |
df = point2D(0, 0), | |
tr = point2D(0, 0), | |
folding = flipMethods._foldingPage.call(this), | |
tan = Math.tan(alpha), | |
ac = turnData.opts.acceleration, | |
h = data.wrapper.height(), | |
top = point.corner.substr(0, 1) == 't', | |
left = point.corner.substr(1, 1) == 'l', | |
compute = function() { | |
var rel = point2D(0, 0); | |
var middle = point2D(0, 0); | |
rel.x = (o.x) ? o.x - point.x : point.x; | |
if (!hasRot) { | |
rel.y = 0; | |
} else { | |
rel.y = (o.y) ? o.y - point.y : point.y; | |
} | |
middle.x = (left)? width - rel.x/2 : point.x + rel.x/2; | |
middle.y = rel.y/2; | |
var alpha = A90-Math.atan2(rel.y, rel.x), | |
gamma = alpha - Math.atan2(middle.y, middle.x), | |
distance = Math.max(0, Math.sin(gamma) * Math.sqrt(Math.pow(middle.x, 2) + Math.pow(middle.y, 2))); | |
a = deg(alpha); | |
tr = point2D(distance * Math.sin(alpha), distance * Math.cos(alpha)); | |
if (alpha > A90) { | |
tr.x = tr.x + Math.abs(tr.y * rel.y/rel.x); | |
tr.y = 0; | |
if (Math.round(tr.x*Math.tan(PI-alpha)) < height) { | |
point.y = Math.sqrt(Math.pow(height, 2)+2 * middle.x * rel.x); | |
if (top) point.y = height - point.y; | |
return compute(); | |
} | |
} | |
if (alpha>A90) { | |
var beta = PI-alpha, dd = h - height/Math.sin(beta); | |
mv = point2D(Math.round(dd*Math.cos(beta)), Math.round(dd*Math.sin(beta))); | |
if (left) mv.x = - mv.x; | |
if (top) mv.y = - mv.y; | |
} | |
px = Math.round(tr.y/Math.tan(alpha) + tr.x); | |
var side = width - px, | |
sideX = side*Math.cos(alpha*2), | |
sideY = side*Math.sin(alpha*2); | |
df = point2D( | |
Math.round((left ? side -sideX : px+sideX)), | |
Math.round((top) ? sideY : height - sideY)); | |
// Gradients | |
if (turnData.opts.gradients) { | |
gradientSize = side*Math.sin(alpha); | |
var endingPoint = flipMethods._c2.call(that, point.corner), | |
far = Math.sqrt(Math.pow(endingPoint.x-point.x, 2)+Math.pow(endingPoint.y-point.y, 2))/width; | |
shadowVal = Math.sin(A90*((far>1) ? 2 - far : far)); | |
gradientOpacity = Math.min(far, 1); | |
gradientStartVal = gradientSize>100 ? (gradientSize-100)/gradientSize : 0; | |
gradientEndPointA = point2D( | |
gradientSize*Math.sin(alpha)/width*100, | |
gradientSize*Math.cos(alpha)/height*100); | |
if (flipMethods._backGradient.call(that)) { | |
gradientEndPointB = point2D( | |
gradientSize*1.2*Math.sin(alpha)/width*100, | |
gradientSize*1.2*Math.cos(alpha)/height*100); | |
if (!left) gradientEndPointB.x = 100-gradientEndPointB.x; | |
if (!top) gradientEndPointB.y = 100-gradientEndPointB.y; | |
} | |
} | |
tr.x = Math.round(tr.x); | |
tr.y = Math.round(tr.y); | |
return true; | |
}, | |
transform = function(tr, c, x, a) { | |
var f = ['0', 'auto'], mvW = (width-h)*x[0]/100, mvH = (height-h)*x[1]/100, | |
cssA = {left: f[c[0]], top: f[c[1]], right: f[c[2]], bottom: f[c[3]]}, | |
cssB = {}, | |
aliasingFk = (a!=90 && a!=-90) ? (left ? -1 : 1) : 0, | |
origin = x[0] + '% ' + x[1] + '%'; | |
that.css(cssA). | |
transform(rotate(a) + translate(tr.x + aliasingFk, tr.y, ac), origin); | |
data.fpage.css(cssA).transform( | |
rotate(a) + | |
translate(tr.x + df.x - mv.x - width*x[0]/100, tr.y + df.y - mv.y - height*x[1]/100, ac) + | |
rotate((180/a - 2)*a), | |
origin); | |
data.wrapper.transform(translate(-tr.x + mvW-aliasingFk, -tr.y + mvH, ac) + rotate(-a), origin); | |
data.fwrapper.transform(translate(-tr.x + mv.x + mvW, -tr.y + mv.y + mvH, ac) + rotate(-a), origin); | |
if (turnData.opts.gradients) { | |
if (x[0]) | |
gradientEndPointA.x = 100-gradientEndPointA.x; | |
if (x[1]) | |
gradientEndPointA.y = (100-gradientEndPointA.y); | |
cssB['box-shadow'] = '0 0 20px rgba(0,0,0,'+(0.5*shadowVal)+')'; | |
folding.css(cssB); | |
gradient(data.ashadow, | |
point2D(left?100:0, top?0:100), | |
point2D(gradientEndPointA.x, gradientEndPointA.y), | |
[[gradientStartVal, 'rgba(0,0,0,0)'], | |
[((1-gradientStartVal)*0.8)+gradientStartVal, 'rgba(0,0,0,'+(0.2*gradientOpacity)+')'], | |
[1, 'rgba(255,255,255,'+(0.2*gradientOpacity)+')']], | |
3, | |
alpha); | |
if (flipMethods._backGradient.call(that)) | |
gradient(data.bshadow, | |
point2D(left?0:100, top?0:100), | |
point2D(gradientEndPointB.x, gradientEndPointB.y), | |
[[0.6, 'rgba(0,0,0,0)'], | |
[0.8, 'rgba(0,0,0,'+(0.3*gradientOpacity)+')'], | |
[1, 'rgba(0,0,0,0)'] | |
], | |
3); | |
} | |
}; | |
switch (point.corner) { | |
case 'l' : | |
break; | |
case 'r' : | |
break; | |
case 'tl' : | |
point.x = Math.max(point.x, 1); | |
compute(); | |
transform(tr, [1,0,0,1], [100, 0], a); | |
break; | |
case 'tr' : | |
point.x = Math.min(point.x, width-1); | |
compute(); | |
transform(point2D(-tr.x, tr.y), [0,0,0,1], [0, 0], -a); | |
break; | |
case 'bl' : | |
point.x = Math.max(point.x, 1); | |
compute(); | |
transform(point2D(tr.x, -tr.y), [1,1,0,0], [100, 100], -a); | |
break; | |
case 'br' : | |
point.x = Math.min(point.x, width-1); | |
compute(); | |
transform(point2D(-tr.x, -tr.y), [0,1,1,0], [0, 100], a); | |
break; | |
} | |
break; | |
} | |
data.point = point; | |
}, | |
_moveFoldingPage: function(move) { | |
var data = this.data().f; | |
if (!data) | |
return; | |
var turn = data.opts.turn, | |
turnData = turn.data(), | |
place = turnData.pagePlace; | |
if (move) { | |
var nextPage = data.opts.next; | |
if (place[nextPage]!=data.opts.page) { | |
if (data.folding) | |
flipMethods._moveFoldingPage.call(this, false); | |
var folding = flipMethods._foldingPage.call(this); | |
folding.appendTo(data.fpage); | |
place[nextPage] = data.opts.page; | |
data.folding = nextPage; | |
} | |
turn.turn('update'); | |
} else { | |
if (data.folding) { | |
if (turnData.pages[data.folding]) { | |
// If we have flip available | |
var flipData = turnData.pages[data.folding].data().f; | |
turnData.pageObjs[data.folding]. | |
appendTo(flipData.wrapper); | |
} else if (turnData.pageWrap[data.folding]) { | |
// If we have the pageWrapper | |
turnData.pageObjs[data.folding]. | |
appendTo(turnData.pageWrap[data.folding]); | |
} | |
if (data.folding in place) { | |
place[data.folding] = data.folding; | |
} | |
delete data.folding; | |
} | |
} | |
}, | |
_showFoldedPage: function(c, animate) { | |
var folding = flipMethods._foldingPage.call(this), | |
dd = this.data(), | |
data = dd.f, | |
visible = data.visible; | |
if (folding) { | |
if (!visible || !data.point || data.point.corner!=c.corner) { | |
var corner = ( | |
data.status=='hover' || | |
data.status=='peel' || | |
data.opts.turn.data().mouseAction) ? | |
c.corner : null; | |
visible = false; | |
if (trigger('start', this, [data.opts, corner])=='prevented') | |
return false; | |
} | |
if (animate) { | |
var that = this, | |
point = (data.point && data.point.corner==c.corner) ? | |
data.point : flipMethods._c.call(this, c.corner, 1); | |
this.animatef({ | |
from: [point.x, point.y], | |
to: [c.x, c.y], | |
duration: 500, | |
frame: function(v) { | |
c.x = Math.round(v[0]); | |
c.y = Math.round(v[1]); | |
flipMethods._fold.call(that, c); | |
} | |
}); | |
} else { | |
flipMethods._fold.call(this, c); | |
if (dd.effect && !dd.effect.turning) | |
this.animatef(false); | |
} | |
if (!visible) { | |
switch(data.effect) { | |
case 'hard': | |
data.visible = true; | |
flipMethods._moveFoldingPage.call(this, true); | |
data.fpage.show(); | |
if (data.opts.shadows) | |
data.bshadow.show(); | |
break; | |
case 'sheet': | |
data.visible = true; | |
data.fparent.show().data().flips++; | |
flipMethods._moveFoldingPage.call(this, true); | |
data.fwrapper.show(); | |
if (data.bshadow) | |
data.bshadow.show(); | |
break; | |
} | |
} | |
return true; | |
} | |
return false; | |
}, | |
hide: function() { | |
var data = this.data().f, | |
turnData = data.opts.turn.data(), | |
folding = flipMethods._foldingPage.call(this); | |
switch (data.effect) { | |
case 'hard': | |
if (turnData.opts.gradients) { | |
data.bshadowLoc = 0; | |
data.bshadow.remove(); | |
data.ashadow.hide(); | |
} | |
data.wrapper.transform(''); | |
data.fpage.hide(); | |
break; | |
case 'sheet': | |
if ((--data.fparent.data().flips)===0) | |
data.fparent.hide(); | |
this.css({left: 0, top: 0, right: 'auto', bottom: 'auto'}). | |
transform(''); | |
data.wrapper.transform(''); | |
data.fwrapper.hide(); | |
if (data.bshadow) | |
data.bshadow.hide(); | |
folding.transform(''); | |
break; | |
} | |
data.visible = false; | |
return this; | |
}, | |
hideFoldedPage: function(animate) { | |
var data = this.data().f; | |
if (!data.point) return; | |
var that = this, | |
p1 = data.point, | |
hide = function() { | |
data.point = null; | |
data.status = ''; | |
that.flip('hide'); | |
that.trigger('end', [data.opts, false]); | |
}; | |
if (animate) { | |
var p4 = flipMethods._c.call(this, p1.corner), | |
top = (p1.corner.substr(0,1)=='t'), | |
delta = (top) ? Math.min(0, p1.y-p4.y)/2 : Math.max(0, p1.y-p4.y)/2, | |
p2 = point2D(p1.x, p1.y+delta), | |
p3 = point2D(p4.x, p4.y-delta); | |
this.animatef({ | |
from: 0, | |
to: 1, | |
frame: function(v) { | |
var np = bezier(p1, p2, p3, p4, v); | |
p1.x = np.x; | |
p1.y = np.y; | |
flipMethods._fold.call(that, p1); | |
}, | |
complete: hide, | |
duration: 800, | |
hiding: true | |
}); | |
} else { | |
this.animatef(false); | |
hide(); | |
} | |
}, | |
turnPage: function(corner) { | |
var that = this, | |
data = this.data().f, | |
turnData = data.opts.turn.data(); | |
corner = {corner: (data.corner) ? | |
data.corner.corner : | |
corner || flipMethods._cAllowed.call(this)[0]}; | |
var p1 = data.point || | |
flipMethods._c.call(this, | |
corner.corner, | |
(data.opts.turn) ? turnData.opts.elevation : 0), | |
p4 = flipMethods._c2.call(this, corner.corner); | |
this.trigger('flip'). | |
animatef({ | |
from: 0, | |
to: 1, | |
frame: function(v) { | |
var np = bezier(p1, p1, p4, p4, v); | |
corner.x = np.x; | |
corner.y = np.y; | |
flipMethods._showFoldedPage.call(that, corner); | |
}, | |
complete: function() { | |
that.trigger('end', [data.opts, true]); | |
}, | |
duration: turnData.opts.duration, | |
turning: true | |
}); | |
data.corner = null; | |
}, | |
moving: function() { | |
return 'effect' in this.data(); | |
}, | |
isTurning: function() { | |
return this.flip('moving') && this.data().effect.turning; | |
}, | |
corner: function() { | |
return this.data().f.corner; | |
}, | |
_eventStart: function(e) { | |
var data = this.data().f, | |
turn = data.opts.turn; | |
if (!data.corner && !data.disabled && !this.flip('isTurning') && | |
data.opts.page==turn.data().pagePlace[data.opts.page]) | |
{ | |
data.corner = flipMethods._isIArea.call(this, e); | |
if (data.corner && flipMethods._foldingPage.call(this)) { | |
this.trigger('pressed', [data.point]); | |
flipMethods._showFoldedPage.call(this, data.corner); | |
return false; | |
} else | |
data.corner = null; | |
} | |
}, | |
_eventMove: function(e) { | |
var data = this.data().f; | |
if (!data.disabled) { | |
e = (isTouch) ? e.originalEvent.touches : [e]; | |
if (data.corner) { | |
var pos = data.parent.offset(); | |
data.corner.x = e[0].pageX-pos.left; | |
data.corner.y = e[0].pageY-pos.top; | |
flipMethods._showFoldedPage.call(this, data.corner); | |
} else if (data.hover && !this.data().effect && this.is(':visible')) { | |
var point = flipMethods._isIArea.call(this, e[0]); | |
if (point) { | |
if ((data.effect=='sheet' && point.corner.length==2) || data.effect=='hard') { | |
data.status = 'hover'; | |
var origin = flipMethods._c.call(this, point.corner, data.opts.cornerSize/2); | |
point.x = origin.x; | |
point.y = origin.y; | |
flipMethods._showFoldedPage.call(this, point, true); | |
} | |
} else { | |
if (data.status=='hover') { | |
data.status = ''; | |
flipMethods.hideFoldedPage.call(this, true); | |
} | |
} | |
} | |
} | |
}, | |
_eventEnd: function() { | |
var data = this.data().f, | |
corner = data.corner; | |
if (!data.disabled && corner) { | |
if (trigger('released', this, [data.point || corner])!='prevented') { | |
flipMethods.hideFoldedPage.call(this, true); | |
} | |
} | |
data.corner = null; | |
}, | |
disable: function(disable) { | |
flipMethods.setData.call(this, {'disabled': disable}); | |
return this; | |
}, | |
hover: function(hover) { | |
flipMethods.setData.call(this, {'hover': hover}); | |
return this; | |
}, | |
peel: function (corner, animate) { | |
var data = this.data().f; | |
if (corner) { | |
if ($.inArray(corner, corners.all)==-1) | |
throw turnError('Corner '+corner+' is not permitted'); | |
if ($.inArray(corner, flipMethods._cAllowed.call(this))!=-1) { | |
var point = flipMethods._c.call(this, corner, data.opts.cornerSize/2); | |
data.status = 'peel'; | |
flipMethods._showFoldedPage.call(this, | |
{ | |
corner: corner, | |
x: point.x, | |
y: point.y | |
}, animate); | |
} | |
} else { | |
data.status = ''; | |
flipMethods.hideFoldedPage.call(this, animate); | |
} | |
return this; | |
} | |
}; | |
// Processes classes | |
function dec(that, methods, args) { | |
if (!args[0] || typeof(args[0])=='object') | |
return methods.init.apply(that, args); | |
else if (methods[args[0]]) | |
return methods[args[0]].apply(that, Array.prototype.slice.call(args, 1)); | |
else | |
throw turnError(args[0] + ' is not a method or property'); | |
} | |
// Attributes for a layer | |
function divAtt(top, left, zIndex, overf) { | |
return {'css': { | |
position: 'absolute', | |
top: top, | |
left: left, | |
'overflow': overf || 'hidden', | |
zIndex: zIndex || 'auto' | |
} | |
}; | |
} | |
// Gets a 2D point from a bezier curve of four points | |
function bezier(p1, p2, p3, p4, t) { | |
var a = 1 - t, | |
b = a * a * a, | |
c = t * t * t; | |
return point2D(Math.round(b*p1.x + 3*t*a*a*p2.x + 3*t*t*a*p3.x + c*p4.x), | |
Math.round(b*p1.y + 3*t*a*a*p2.y + 3*t*t*a*p3.y + c*p4.y)); | |
} | |
// Converts an angle from degrees to radians | |
function rad(degrees) { | |
return degrees/180*PI; | |
} | |
// Converts an angle from radians to degrees | |
function deg(radians) { | |
return radians/PI*180; | |
} | |
// Gets a 2D point | |
function point2D(x, y) { | |
return {x: x, y: y}; | |
} | |
// Webkit 534.3 on Android wrongly repaints elements that use overflow:hidden + rotation | |
function rotationAvailable() { | |
var parts; | |
if ((parts = /AppleWebkit\/([0-9\.]+)/i.exec(navigator.userAgent))) { | |
var webkitVersion = parseFloat(parts[1]); | |
return (webkitVersion>534.3); | |
} else { | |
return true; | |
} | |
} | |
// Returns the traslate value | |
function translate(x, y, use3d) { | |
return (has3d && use3d) ? ' translate3d(' + x + 'px,' + y + 'px, 0px) ' | |
: ' translate(' + x + 'px, ' + y + 'px) '; | |
} | |
// Returns the rotation value | |
function rotate(degrees) { | |
return ' rotate(' + degrees + 'deg) '; | |
} | |
// Checks if a property belongs to an object | |
function has(property, object) { | |
return Object.prototype.hasOwnProperty.call(object, property); | |
} | |
// Gets the CSS3 vendor prefix | |
function getPrefix() { | |
var vendorPrefixes = ['Moz','Webkit','Khtml','O','ms'], | |
len = vendorPrefixes.length, | |
vendor = ''; | |
while (len--) | |
if ((vendorPrefixes[len] + 'Transform') in document.body.style) | |
vendor='-'+vendorPrefixes[len].toLowerCase()+'-'; | |
return vendor; | |
} | |
// Detects the transitionEnd Event | |
function getTransitionEnd() { | |
var t, | |
el = document.createElement('fakeelement'), | |
transitions = { | |
'transition':'transitionend', | |
'OTransition':'oTransitionEnd', | |
'MSTransition':'transitionend', | |
'MozTransition':'transitionend', | |
'WebkitTransition':'webkitTransitionEnd' | |
}; | |
for (t in transitions) { | |
if (el.style[t] !== undefined) { | |
return transitions[t]; | |
} | |
} | |
} | |
// Gradients | |
function gradient(obj, p0, p1, colors, numColors) { | |
var j, cols = []; | |
if (vendor=='-webkit-') { | |
for (j = 0; j<numColors; j++) | |
cols.push('color-stop('+colors[j][0]+', '+colors[j][1]+')'); | |
obj.css({'background-image': | |
'-webkit-gradient(linear, '+ | |
p0.x+'% '+ | |
p0.y+'%,'+ | |
p1.x+'% '+ | |
p1.y+'%, '+ | |
cols.join(',') + ' )'}); | |
} else { | |
p0 = {x:p0.x/100 * obj.width(), y:p0.y/100 * obj.height()}; | |
p1 = {x:p1.x/100 * obj.width(), y:p1.y/100 * obj.height()}; | |
var dx = p1.x-p0.x, | |
dy = p1.y-p0.y, | |
angle = Math.atan2(dy, dx), | |
angle2 = angle - Math.PI/2, | |
diagonal = Math.abs(obj.width()*Math.sin(angle2))+Math.abs(obj.height()*Math.cos(angle2)), | |
gradientDiagonal = Math.sqrt(dy*dy + dx*dx), | |
corner = point2D((p1.x<p0.x) ? obj.width() : 0, (p1.y<p0.y) ? obj.height() : 0), | |
slope = Math.tan(angle), | |
inverse = -1/slope, | |
x = (inverse*corner.x - corner.y - slope*p0.x + p0.y)/(inverse-slope), | |
c = {x: x, y: inverse*x - inverse*corner.x + corner.y}, | |
segA = (Math.sqrt( Math.pow(c.x-p0.x,2) + Math.pow(c.y-p0.y,2))); | |
for (j = 0; j<numColors; j++) | |
cols.push(' '+colors[j][1]+' '+((segA + gradientDiagonal*colors[j][0])*100/diagonal)+'%'); | |
obj.css({'background-image': vendor+'linear-gradient(' + (-angle) + 'rad,' + cols.join(',') + ')'}); | |
} | |
} | |
// Triggers an event | |
function trigger(eventName, context, args) { | |
var event = $.Event(eventName); | |
context.trigger(event, args); | |
if (event.isDefaultPrevented()) | |
return 'prevented'; | |
else if (event.isPropagationStopped()) | |
return 'stopped'; | |
else | |
return ''; | |
} | |
// JS Errors | |
function turnError(message) { | |
function TurnJsError(message) { | |
this.name = "TurnJsError"; | |
this.message = message; | |
} | |
TurnJsError.prototype = new Error(); | |
TurnJsError.prototype.constructor = TurnJsError; | |
return new TurnJsError(message); | |
} | |
// Find the offset of an element ignoring its transformation | |
function findPos(obj) { | |
var offset = {top: 0, left: 0}; | |
do{ | |
offset.left += obj.offsetLeft; | |
offset.top += obj.offsetTop; | |
} while ((obj = obj.offsetParent)); | |
return offset; | |
} | |
// Checks if there's hard page compatibility | |
// IE9 is the only browser that does not support hard pages | |
function hasHardPage() { | |
return (navigator.userAgent.indexOf('MSIE 9.0')==-1); | |
} | |
// Request an animation | |
window.requestAnim = (function() { | |
return window.requestAnimationFrame || | |
window.webkitRequestAnimationFrame || | |
window.mozRequestAnimationFrame || | |
window.oRequestAnimationFrame || | |
window.msRequestAnimationFrame || | |
function(callback) { | |
window.setTimeout(callback, 1000 / 60); | |
}; | |
})(); | |
// Extend $.fn | |
$.extend($.fn, { | |
flip: function() { | |
return dec($(this[0]), flipMethods, arguments); | |
}, | |
turn: function() { | |
return dec($(this[0]), turnMethods, arguments); | |
}, | |
transform: function(transform, origin) { | |
var properties = {}; | |
if (origin) | |
properties[vendor+'transform-origin'] = origin; | |
properties[vendor+'transform'] = transform; | |
return this.css(properties); | |
}, | |
animatef: function(point) { | |
var data = this.data(); | |
if (data.effect) | |
data.effect.stop(); | |
if (point) { | |
if (!point.to.length) point.to = [point.to]; | |
if (!point.from.length) point.from = [point.from]; | |
var diff = [], | |
len = point.to.length, | |
animating = true, | |
that = this, | |
time = (new Date()).getTime(), | |
frame = function() { | |
if (!data.effect || !animating) | |
return; | |
var v = [], | |
timeDiff = Math.min(point.duration, (new Date()).getTime() - time); | |
for (var i = 0; i < len; i++) | |
v.push(data.effect.easing(1, timeDiff, point.from[i], diff[i], point.duration)); | |
point.frame((len==1) ? v[0] : v); | |
if (timeDiff==point.duration) { | |
delete data['effect']; | |
that.data(data); | |
if (point.complete) | |
point.complete(); | |
} else { | |
window.requestAnim(frame); | |
} | |
}; | |
for (var i = 0; i < len; i++) | |
diff.push(point.to[i] - point.from[i]); | |
data.effect = $.extend({ | |
stop: function() { | |
animating = false; | |
}, | |
easing: function (x, t, b, c, data) { | |
return c * Math.sqrt(1 - (t=t/data-1)*t) + b; | |
} | |
}, point); | |
this.data(data); | |
frame(); | |
} else { | |
delete data['effect']; | |
} | |
} | |
}); | |
// Export some globals | |
$.isTouch = isTouch; | |
$.mouseEvents = mouseEvents; | |
$.cssPrefix = getPrefix; | |
$.cssTransitionEnd = getTransitionEnd; | |
$.findPos = findPos; | |
})(jQuery); | |
$(window).ready(function() { | |
$('.pages').turn({ | |
duration: 1500, | |
width: 620, | |
height: 445, | |
// acceleration: true, | |
// display: 'single', | |
// autoCenter: true, | |
turnCorners: "bl,br", | |
elevation: 300, | |
when: { | |
turned: function(e, page) { | |
console.log('Current view: ', $(this).turn('view')); | |
} | |
} | |
}); | |
}); | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/TweenMax.min.js"></script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@import url('https://fonts.googleapis.com/css?family=Stalemate'); | |
*, *:before, *:after { | |
box-sizing: border-box; | |
} | |
img { | |
max-width: 100%; | |
} | |
body { | |
background: linear-gradient( | |
rgba(255, 255, 255, 0.9), | |
rgba(255, 255, 255, 0.9)), url(https://s-media-cache-ak0.pinimg.com/originals/30/b3/a9/30b3a9b27032a6b9c1d7a69d2400fb9e.jpg); | |
font-family: 'Stalemate', cursive; | |
} | |
.container { | |
overflow: hidden; | |
// margin-top: 3%; | |
} | |
.book-wrapper { | |
position: relative; | |
margin: 2% auto; | |
width: 670px; | |
} | |
.book-wrapper img { | |
filter: drop-shadow(15px 5px 5px #494338); | |
} | |
.pages-container { | |
position: absolute; | |
top: 4%; | |
left: 3%; | |
right: 0; | |
bottom: 0; | |
width: 91.5%; | |
height: 91%; | |
} | |
.pages { | |
} | |
.pages [class^="page-num-"] { | |
background-image: url("https://img00.deviantart.net/cbb9/i/2005/258/c/4/paper_texture_v5_by_bashcorpo.jpg"); | |
background-size: auto 100%; | |
position: relative; | |
} | |
.pages-content { | |
text-align: center; | |
position: absolute; | |
width: 100%; | |
height: 100%; | |
display: table; | |
top: 0; | |
bottom: 0; | |
left: 0; | |
right: 0; | |
padding: 10%; | |
} | |
.content-inner { | |
text-align: center; | |
display: table-cell; | |
vertical-align: middle; | |
} | |
.book-wrapper { | |
user-select: none; | |
} | |
.page-num-1 .pages-background { | |
background: linear-gradient( rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.1)), url(https://openclipart.org/image/2400px/svg_to_png/242093/Vintage-Floral-Texture-Background.png); | |
background-size: cover; | |
opacity: 0.2; | |
width: 100%; | |
height: 100%; | |
position: absolute; | |
left: 0; | |
top: 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment