diff options
author | JinweiClarkChao <[email protected]> | 2014-02-20 12:50:15 +0800 |
---|---|---|
committer | JinweiClarkChao <[email protected]> | 2014-02-20 12:52:51 +0800 |
commit | 31fb10f393fbfd4d7adf528ec70624d2b8d247a8 (patch) | |
tree | 1009bb2a4f5fe89b8bc822b68104018ea8df5261 /Blocks/Flipboard/js/jquery.flips.js | |
parent | be404e3e01ca839e730c884309c25abef10863c9 (diff) | |
download | jinwei.me-31fb10f393fbfd4d7adf528ec70624d2b8d247a8.tar.gz |
Six Blocks Version
Diffstat (limited to 'Blocks/Flipboard/js/jquery.flips.js')
-rw-r--r-- | Blocks/Flipboard/js/jquery.flips.js | 965 |
1 files changed, 965 insertions, 0 deletions
diff --git a/Blocks/Flipboard/js/jquery.flips.js b/Blocks/Flipboard/js/jquery.flips.js new file mode 100644 index 0000000..5c25d87 --- /dev/null +++ b/Blocks/Flipboard/js/jquery.flips.js | |||
@@ -0,0 +1,965 @@ | |||
1 | /** | ||
2 | * jquery.flips.js | ||
3 | * | ||
4 | * Copyright 2011, Pedro Botelho / Codrops | ||
5 | * Free to use under the MIT license. | ||
6 | * | ||
7 | * Date: Fri May 4 2012 | ||
8 | */ | ||
9 | |||
10 | /** | ||
11 | * Note: This is highly experimental and just a proof-of-concept! | ||
12 | * There are some few "hacks", probably some bugs, and some functionality | ||
13 | * is incomplete... definitely not ready for a production environment. | ||
14 | * | ||
15 | * | ||
16 | * Tested and working on: | ||
17 | * - Google Chrome 18.0.1025.168 | ||
18 | * - Apple Safari 5.1.5 | ||
19 | * - Apple Safari 5.1 Mobile | ||
20 | * | ||
21 | */ | ||
22 | |||
23 | (function( window, undefined ) { | ||
24 | |||
25 | $.Flips = function( options, element ) { | ||
26 | |||
27 | this.$el = $( element ); | ||
28 | this._init( options ); | ||
29 | |||
30 | }; | ||
31 | |||
32 | $.Flips.defaults = { | ||
33 | flipspeed : 900, | ||
34 | fliptimingfunction : 'linear', | ||
35 | current : 0 | ||
36 | }; | ||
37 | |||
38 | $.Flips.prototype = { | ||
39 | _init : function( options ) { | ||
40 | |||
41 | this.options = $.extend( true, {}, $.Flips.defaults, options ); | ||
42 | this.$pages = this.$el.children( 'div.f-page' ); | ||
43 | this.pagesCount = this.$pages.length; | ||
44 | this.History = window.History; | ||
45 | this.currentPage = this.options.current; | ||
46 | this._validateOpts(); | ||
47 | this._getWinSize(); | ||
48 | this._getState(); | ||
49 | this._layout(); | ||
50 | this._initTouchSwipe(); | ||
51 | this._loadEvents(); | ||
52 | this._goto(); | ||
53 | |||
54 | }, | ||
55 | _validateOpts : function() { | ||
56 | |||
57 | if( this.currentPage < 0 || this.currentPage > this.pagesCount ) { | ||
58 | |||
59 | this.currentPage = 0; | ||
60 | |||
61 | } | ||
62 | |||
63 | }, | ||
64 | _getWinSize : function() { | ||
65 | |||
66 | var $win = $( window ); | ||
67 | |||
68 | this.windowProp = { | ||
69 | width : $win.width(), | ||
70 | height : $win.height() | ||
71 | }; | ||
72 | |||
73 | }, | ||
74 | _goto : function() { | ||
75 | |||
76 | var page = ( this.state === undefined ) ? this.currentPage : this.state; | ||
77 | |||
78 | if( !this._isNumber( page ) || page < 0 || page > this.flipPagesCount ) { | ||
79 | |||
80 | page = 0; | ||
81 | |||
82 | } | ||
83 | |||
84 | this.currentPage = page; | ||
85 | |||
86 | }, | ||
87 | _getState : function() { | ||
88 | |||
89 | this.state = this.History.getState().url.queryStringToJSON().page; | ||
90 | |||
91 | }, | ||
92 | _isNumber : function( n ) { | ||
93 | |||
94 | return parseFloat( n ) == parseInt( n ) && !isNaN( n ) && isFinite( n ); | ||
95 | |||
96 | }, | ||
97 | _adjustLayout : function( page ) { | ||
98 | |||
99 | var _self = this; | ||
100 | |||
101 | this.$flipPages.each( function( i ) { | ||
102 | |||
103 | var $page = $(this); | ||
104 | |||
105 | if( i === page - 1 ) { | ||
106 | |||
107 | $page.css({ | ||
108 | '-webkit-transform' : 'rotateY( -180deg )', | ||
109 | '-moz-transform' : 'rotateY( -180deg )', | ||
110 | 'z-index' : _self.flipPagesCount - 1 + i | ||
111 | }); | ||
112 | |||
113 | } | ||
114 | else if( i < page ) { | ||
115 | |||
116 | $page.css({ | ||
117 | '-webkit-transform' : 'rotateY( -181deg )', // todo: fix this (should be -180deg) | ||
118 | '-moz-transform' : 'rotateY( -181deg )', // todo: fix this (should be -180deg) | ||
119 | 'z-index' : _self.flipPagesCount - 1 + i | ||
120 | }); | ||
121 | |||
122 | } | ||
123 | else { | ||
124 | |||
125 | $page.css({ | ||
126 | '-webkit-transform' : 'rotateY( 0deg )', | ||
127 | '-moz-transform' : 'rotateY( 0deg )', | ||
128 | 'z-index' : _self.flipPagesCount - 1 - i | ||
129 | }); | ||
130 | |||
131 | } | ||
132 | |||
133 | } ); | ||
134 | |||
135 | }, | ||
136 | _saveState : function() { | ||
137 | |||
138 | // adds a new state to the history object and triggers the statechange event on the window | ||
139 | var page = this.currentPage; | ||
140 | |||
141 | if( this.History.getState().url.queryStringToJSON().page !== page ) { | ||
142 | |||
143 | this.History.pushState( null, null, '?page=' + page ); | ||
144 | |||
145 | } | ||
146 | |||
147 | }, | ||
148 | _layout : function() { | ||
149 | |||
150 | this._setLayoutSize(); | ||
151 | |||
152 | for( var i = 0; i <= this.pagesCount - 2; ++i ) { | ||
153 | |||
154 | var $page = this.$pages.eq( i ), | ||
155 | pageData = { | ||
156 | theClass : 'page', | ||
157 | theContentFront : $page.html(), | ||
158 | theContentBack : ( i !== this.pagesCount ) ? this.$pages.eq( i + 1 ).html() : '', | ||
159 | theStyle : 'z-index: ' + ( this.pagesCount - i ) + ';left: ' + ( this.windowProp.width / 2 ) + 'px;', | ||
160 | theContentStyleFront : 'width:' + this.windowProp.width + 'px;', | ||
161 | theContentStyleBack : 'width:' + this.windowProp.width + 'px;' | ||
162 | }; | ||
163 | |||
164 | if( i === 0 ) { | ||
165 | |||
166 | pageData.theClass += ' cover'; | ||
167 | |||
168 | } | ||
169 | else { | ||
170 | |||
171 | pageData.theContentStyleFront += 'left:-' + ( this.windowProp.width / 2 ) + 'px'; | ||
172 | |||
173 | if( i === this.pagesCount - 2 ) { | ||
174 | |||
175 | pageData.theClass += ' cover-back'; | ||
176 | |||
177 | } | ||
178 | |||
179 | } | ||
180 | |||
181 | $( '#pageTmpl' ).tmpl( pageData ).appendTo( this.$el ); | ||
182 | |||
183 | } | ||
184 | |||
185 | this.$pages.remove(); | ||
186 | this.$flipPages = this.$el.children( 'div.page' ); | ||
187 | this.flipPagesCount = this.$flipPages.length; | ||
188 | |||
189 | this._adjustLayout( ( this.state === undefined ) ? this.currentPage : this.state ); | ||
190 | |||
191 | }, | ||
192 | _setLayoutSize : function() { | ||
193 | |||
194 | this.$el.css( { | ||
195 | width : this.windowProp.width, | ||
196 | height : this.windowProp.height | ||
197 | } ); | ||
198 | |||
199 | }, | ||
200 | _initTouchSwipe : function() { | ||
201 | |||
202 | var _self = this; | ||
203 | |||
204 | this.$el.swipe( { | ||
205 | threshold : 0, | ||
206 | swipeStatus : function( event, phase, start, end, direction, distance ) { | ||
207 | |||
208 | var startX = start.x, | ||
209 | endX = end.x, | ||
210 | sym, angle, | ||
211 | oob = false, | ||
212 | noflip = false; | ||
213 | |||
214 | // check the "page direction" to flip: | ||
215 | // if the page flips from the right to the left (right side page) | ||
216 | // or from the left to the right (left side page). | ||
217 | // check only if not animating | ||
218 | if( !_self._isAnimating() ) { | ||
219 | |||
220 | ( startX < _self.windowProp.width / 2 ) ? _self.flipSide = 'l2r' : _self.flipSide = 'r2l'; | ||
221 | |||
222 | } | ||
223 | |||
224 | if( direction === 'up' || direction === 'down' ) { | ||
225 | |||
226 | if( _self.angle === undefined || _self.angle === 0 ) { | ||
227 | |||
228 | _self._removeOverlays(); | ||
229 | return false; | ||
230 | |||
231 | } | ||
232 | else { | ||
233 | |||
234 | ( _self.angle < 90 ) ? direction = 'right' : direction = 'left'; | ||
235 | |||
236 | } | ||
237 | |||
238 | }; | ||
239 | |||
240 | _self.flipDirection = direction; | ||
241 | |||
242 | // on the first & last page neighbors we don't flip | ||
243 | if( _self.currentPage === 0 && _self.flipSide === 'l2r' || _self.currentPage === _self.flipPagesCount && _self.flipSide === 'r2l' ) { | ||
244 | |||
245 | return false; | ||
246 | |||
247 | } | ||
248 | |||
249 | // save ending point (symetric point): | ||
250 | // if we touch / start dragging on, say [x=10], then | ||
251 | // we need to drag until [window's width - 10] in order to flip the page 100%. | ||
252 | // if the symetric point is too close we are giving some margin: | ||
253 | // if we would start dragging right next to [window's width / 2] then | ||
254 | // the symmetric point would be very close to the starting point. A very short swipe | ||
255 | // would be enough to flip the page.. | ||
256 | sym = _self.windowProp.width - startX; | ||
257 | |||
258 | var symMargin = 0.9 * ( _self.windowProp.width / 2 ); | ||
259 | if( Math.abs( startX - sym ) < symMargin ) { | ||
260 | |||
261 | ( _self.flipSide === 'r2l' ) ? sym -= symMargin / 2 : sym += symMargin / 2; | ||
262 | |||
263 | } | ||
264 | |||
265 | // some special cases: | ||
266 | // Page is on the right side, | ||
267 | // and we drag/swipe to the same direction | ||
268 | // ending on a point > than the starting point | ||
269 | // ----------------------- | ||
270 | // | | | | ||
271 | // | | | | ||
272 | // | sym | s | | ||
273 | // | | e | | ||
274 | // | | | | ||
275 | // ----------------------- | ||
276 | if( endX > startX && _self.flipSide === 'r2l' ) { | ||
277 | |||
278 | angle = 0; | ||
279 | oob = true; | ||
280 | noflip = true; | ||
281 | |||
282 | } | ||
283 | // Page is on the right side, | ||
284 | // and we drag/swipe to the opposite direction | ||
285 | // ending on a point < than the symmetric point | ||
286 | // ----------------------- | ||
287 | // | | | | ||
288 | // | | | | ||
289 | // | sym | s | | ||
290 | // | e | | | ||
291 | // | | | | ||
292 | // ----------------------- | ||
293 | else if( endX < sym && _self.flipSide === 'r2l' ) { | ||
294 | |||
295 | angle = 180; | ||
296 | oob = true; | ||
297 | |||
298 | } | ||
299 | // Page is on the left side, | ||
300 | // and we drag/swipe to the opposite direction | ||
301 | // ending on a point > than the symmetric point | ||
302 | // ----------------------- | ||
303 | // | | | | ||
304 | // | | | | ||
305 | // | s | sym | | ||
306 | // | | e | | ||
307 | // | | | | ||
308 | // ----------------------- | ||
309 | else if( endX > sym && _self.flipSide === 'l2r' ) { | ||
310 | |||
311 | angle = 0; | ||
312 | oob = true; | ||
313 | |||
314 | } | ||
315 | // Page is on the left side, | ||
316 | // and we drag/swipe to the same direction | ||
317 | // ending on a point < than the starting point | ||
318 | // ----------------------- | ||
319 | // | | | | ||
320 | // | | | | ||
321 | // | s | sym | | ||
322 | // | e | | | ||
323 | // | | | | ||
324 | // ----------------------- | ||
325 | else if( endX < startX && _self.flipSide === 'l2r' ) { | ||
326 | |||
327 | angle = 180; | ||
328 | oob = true; | ||
329 | noflip = true; | ||
330 | |||
331 | } | ||
332 | // we drag/swipe to a point between | ||
333 | // the starting point and symetric point | ||
334 | // ----------------------- | ||
335 | // | | | | ||
336 | // | s | sym | | ||
337 | // | sym | s | | ||
338 | // | e| | | ||
339 | // | | | | ||
340 | // ----------------------- | ||
341 | else { | ||
342 | |||
343 | var s, e, val; | ||
344 | |||
345 | ( _self.flipSide === 'r2l' ) ? | ||
346 | ( s = startX, e = sym, val = startX - distance ) : | ||
347 | ( s = sym, e = startX , val = startX + distance ); | ||
348 | |||
349 | angle = _self._calcAngle( val, s, e ); | ||
350 | |||
351 | if( ( direction === 'left' && _self.flipSide === 'l2r' ) || ( direction === 'right' && _self.flipSide === 'r2l' ) ) { | ||
352 | |||
353 | noflip = true; | ||
354 | |||
355 | } | ||
356 | |||
357 | } | ||
358 | |||
359 | switch( phase ) { | ||
360 | |||
361 | case 'start' : | ||
362 | |||
363 | if( _self._isAnimating() ) { | ||
364 | |||
365 | // the user can still grab a page while one is flipping (in this case not being able to move) | ||
366 | // and once the page is flipped the move/touchmove events are triggered.. | ||
367 | _self.start = true; | ||
368 | return false; | ||
369 | |||
370 | } | ||
371 | else { | ||
372 | |||
373 | _self.start = false; | ||
374 | |||
375 | } | ||
376 | |||
377 | // check which page is clicked/touched | ||
378 | _self._setFlippingPage(); | ||
379 | |||
380 | // check which page comes before & after the one we are clicking | ||
381 | _self.$beforePage = _self.$flippingPage.prev(); | ||
382 | _self.$afterPage = _self.$flippingPage.next(); | ||
383 | |||
384 | break; | ||
385 | |||
386 | case 'move' : | ||
387 | |||
388 | if( distance > 0 ) { | ||
389 | |||
390 | if( _self._isAnimating() || _self.start ) { | ||
391 | |||
392 | return false; | ||
393 | |||
394 | } | ||
395 | |||
396 | // adds overlays: shows shadows while flipping | ||
397 | if( !_self.hasOverlays ) { | ||
398 | |||
399 | _self._addOverlays(); | ||
400 | |||
401 | } | ||
402 | |||
403 | // save last angle | ||
404 | _self.angle = angle; | ||
405 | // we will update the rotation value of the page while we move it | ||
406 | _self._turnPage( angle , true ); | ||
407 | |||
408 | } | ||
409 | break; | ||
410 | |||
411 | case 'end' : | ||
412 | |||
413 | if( distance > 0 ) { | ||
414 | |||
415 | if( _self._isAnimating() || _self.start ) return false; | ||
416 | |||
417 | _self.isAnimating = true; | ||
418 | |||
419 | // keep track if the page was actually flipped or not | ||
420 | // the data flip will be used later on the transitionend event | ||
421 | ( noflip ) ? _self.$flippingPage.data( 'flip', false ) : _self.$flippingPage.data( 'flip', true ); | ||
422 | |||
423 | // if out of bounds we will "manually" flip the page, | ||
424 | // meaning there will be no transition set | ||
425 | if( oob ) { | ||
426 | |||
427 | if( !noflip ) { | ||
428 | |||
429 | // the page gets flipped (user dragged from the starting point until the symmetric point) | ||
430 | // update current page | ||
431 | _self._updatePage(); | ||
432 | |||
433 | } | ||
434 | |||
435 | _self._onEndFlip( _self.$flippingPage ); | ||
436 | |||
437 | } | ||
438 | else { | ||
439 | |||
440 | // save last angle | ||
441 | _self.angle = angle; | ||
442 | // calculate the speed to flip the page: | ||
443 | // the speed will depend on the current angle. | ||
444 | _self._calculateSpeed(); | ||
445 | |||
446 | switch( direction ) { | ||
447 | |||
448 | case 'left' : | ||
449 | |||
450 | _self._turnPage( 180 ); | ||
451 | |||
452 | if( _self.flipSide === 'r2l' ) { | ||
453 | |||
454 | _self._updatePage(); | ||
455 | |||
456 | } | ||
457 | |||
458 | break; | ||
459 | |||
460 | case 'right' : | ||
461 | |||
462 | _self._turnPage( 0 ); | ||
463 | |||
464 | if( _self.flipSide === 'l2r' ) { | ||
465 | |||
466 | _self._updatePage(); | ||
467 | |||
468 | } | ||
469 | |||
470 | break; | ||
471 | |||
472 | }; | ||
473 | |||
474 | } | ||
475 | |||
476 | } | ||
477 | |||
478 | break; | ||
479 | |||
480 | }; | ||
481 | |||
482 | } | ||
483 | |||
484 | } ); | ||
485 | |||
486 | }, | ||
487 | _setFlippingPage : function() { | ||
488 | |||
489 | var _self = this; | ||
490 | |||
491 | ( this.flipSide === 'l2r' ) ? | ||
492 | this.$flippingPage = this.$flipPages.eq( this.currentPage - 1 ) : | ||
493 | this.$flippingPage = this.$flipPages.eq( this.currentPage ); | ||
494 | |||
495 | this.$flippingPage.on( 'webkitTransitionEnd.flips transitionend.flips OTransitionEnd.flips', function( event ) { | ||
496 | |||
497 | if( $( event.target ).hasClass( 'page' ) ) { | ||
498 | |||
499 | _self._onEndFlip( $(this) ); | ||
500 | |||
501 | } | ||
502 | |||
503 | }); | ||
504 | |||
505 | }, | ||
506 | _updatePage : function() { | ||
507 | |||
508 | if( this.flipSide === 'r2l' ) { | ||
509 | |||
510 | ++this.currentPage; | ||
511 | |||
512 | } | ||
513 | else if( this.flipSide === 'l2r' ) { | ||
514 | |||
515 | --this.currentPage; | ||
516 | |||
517 | } | ||
518 | |||
519 | }, | ||
520 | _isAnimating : function() { | ||
521 | |||
522 | if( this.isAnimating ) { | ||
523 | |||
524 | return true; | ||
525 | |||
526 | } | ||
527 | |||
528 | return false; | ||
529 | |||
530 | }, | ||
531 | _loadEvents : function() { | ||
532 | |||
533 | var _self = this; | ||
534 | |||
535 | $( window ).on( 'resize.flips', function( event ) { | ||
536 | |||
537 | _self._getWinSize(); | ||
538 | _self._setLayoutSize(); | ||
539 | |||
540 | var $contentFront = _self.$flipPages.children( 'div.front' ).find( 'div.content' ), | ||
541 | $contentBack = _self.$flipPages.children( 'div.back' ).find( 'div.content' ) | ||
542 | |||
543 | _self.$flipPages.css( 'left', _self.windowProp.width / 2 ); | ||
544 | |||
545 | $contentFront.filter( function( i ) { | ||
546 | return i > 0; | ||
547 | }).css( { | ||
548 | width : _self.windowProp.width, | ||
549 | left : -_self.windowProp.width / 2 | ||
550 | } ); | ||
551 | $contentFront.eq( 0 ).css( 'width', _self.windowProp.width ); | ||
552 | |||
553 | $contentBack.css( 'width', _self.windowProp.width ); | ||
554 | |||
555 | } ); | ||
556 | |||
557 | $( window ).on( 'statechange.flips', function( event ) { | ||
558 | |||
559 | _self._getState(); | ||
560 | _self._goto(); | ||
561 | if( !_self.isAnimating ) { | ||
562 | |||
563 | _self._adjustLayout( _self.currentPage ); | ||
564 | |||
565 | } | ||
566 | |||
567 | } ); | ||
568 | |||
569 | this.$flipPages.find( '.box' ).on( 'click.flips', function( event ) { | ||
570 | |||
571 | var $box = $(this), | ||
572 | $boxClose = $( '<span class="box-close">close</span>' ), | ||
573 | transitionProp = { | ||
574 | speed : 450, | ||
575 | timingfunction : 'linear' | ||
576 | }, | ||
577 | $overlay = $( '<div class="overlay">close</div>' ).css( { | ||
578 | 'z-index' : 9998, | ||
579 | '-webkit-transition' : 'opacity ' + transitionProp.speed + 'ms ' + transitionProp.timingfunction, | ||
580 | '-moz-transition' : 'opacity ' + transitionProp.speed + 'ms ' + transitionProp.timingfunction | ||
581 | } ).prependTo( $( 'body' ) ), | ||
582 | prop = { | ||
583 | width : $box.outerWidth(true), | ||
584 | height : $box.outerHeight(true), | ||
585 | left : $box.offset().left, | ||
586 | top : $box.offset().top | ||
587 | }, | ||
588 | $placeholder = $box.clone().css( { | ||
589 | 'position' : 'absolute', | ||
590 | 'width' : prop.width, | ||
591 | 'height' : prop.height, | ||
592 | 'left' : prop.left, | ||
593 | 'top' : prop.top, | ||
594 | 'zIndex' : 9999, | ||
595 | 'overflow-y' : 'auto', | ||
596 | '-webkit-transition': 'all ' + transitionProp.speed + 'ms ' + transitionProp.timingfunction, | ||
597 | '-moz-transition': 'all ' + transitionProp.speed + 'ms ' + transitionProp.timingfunction | ||
598 | } ) | ||
599 | .insertAfter( $overlay ) | ||
600 | .end() | ||
601 | .append( $boxClose.on( 'click.flips', function( event ) { | ||
602 | |||
603 | $overlay.css( 'opacity', 0 ); | ||
604 | |||
605 | $placeholder.children().hide().end().removeClass( 'box-expanded' ).css( { | ||
606 | width : _self.windowProp.width, | ||
607 | height : _self.windowProp.height, | ||
608 | 'overflow-y' : 'hidden' | ||
609 | } ); | ||
610 | |||
611 | setTimeout( function() { | ||
612 | $placeholder.css( { | ||
613 | left : prop.left, | ||
614 | top : prop.top, | ||
615 | width : prop.width, | ||
616 | height : prop.height, | ||
617 | '-webkit-transition' : 'all ' + transitionProp.speed + 'ms ' + transitionProp.timingfunction, | ||
618 | '-moz-transition' : 'all ' + transitionProp.speed + 'ms ' + transitionProp.timingfunction | ||
619 | }); | ||
620 | }, 0 ); | ||
621 | |||
622 | }) ) | ||
623 | .children() | ||
624 | .hide() | ||
625 | .end() | ||
626 | .on( 'webkitTransitionEnd.flips transitionend.flips OTransitionEnd.flips', function( event ) { | ||
627 | |||
628 | if( $( event.target ).hasClass( 'box-expanded' ) ) { // expanding | ||
629 | |||
630 | $(this).css( { | ||
631 | width : '100%', | ||
632 | height : '100%', | ||
633 | '-webkit-transition' : 'none', | ||
634 | '-moz-transition' : 'none' | ||
635 | } ).children().fadeIn(); | ||
636 | |||
637 | } | ||
638 | else { // collapsing | ||
639 | |||
640 | $overlay.remove(); | ||
641 | $(this).remove(); | ||
642 | |||
643 | } | ||
644 | |||
645 | }); | ||
646 | |||
647 | setTimeout( function() { | ||
648 | |||
649 | $overlay.css( { | ||
650 | opacity : 1 | ||
651 | } ); | ||
652 | |||
653 | $placeholder.addClass( 'box-expanded' ).css( { | ||
654 | left : 0, | ||
655 | top : 0, | ||
656 | width : _self.windowProp.width, | ||
657 | height : _self.windowProp.height | ||
658 | }); | ||
659 | |||
660 | }, 0 ); | ||
661 | |||
662 | } ); | ||
663 | |||
664 | }, | ||
665 | _onEndFlip : function( $page ) { | ||
666 | |||
667 | // if the page flips from left to right we will need to change the z-index of the flipped page | ||
668 | if( ( this.flipSide === 'l2r' && $page.data( 'flip' ) ) || | ||
669 | ( this.flipSide === 'r2l' && !$page.data( 'flip' ) ) ) { | ||
670 | |||
671 | $page.css( 'z-index', this.pagesCount - 2 - $page.index() ); | ||
672 | |||
673 | } | ||
674 | |||
675 | this.$flippingPage.css( { | ||
676 | '-webkit-transition' : 'none', | ||
677 | '-moz-transition' : 'none' | ||
678 | } ); | ||
679 | |||
680 | // remove overlays | ||
681 | this._removeOverlays(); | ||
682 | this._saveState(); | ||
683 | this.isAnimating = false; | ||
684 | |||
685 | // hack (todo: issues with safari / z-indexes) | ||
686 | if( this.flipSide === 'r2l' || ( this.flipSide === 'l2r' && !$page.data( 'flip' ) ) ) { | ||
687 | |||
688 | this.$flippingPage.find('.back').css( '-webkit-transform', 'rotateY(-180deg)' ); | ||
689 | |||
690 | } | ||
691 | |||
692 | }, | ||
693 | // given the touch/drag start point (s), the end point (e) and a value in between (x) | ||
694 | // calculate the respective angle ( 0deg - 180deg ) | ||
695 | _calcAngle : function( x, s, e ) { | ||
696 | |||
697 | return ( -180 / ( s - e ) ) * x + ( ( s * 180 ) / ( s - e ) ); | ||
698 | |||
699 | }, | ||
700 | // given the current angle and the default speed, calculate the respective speed to accomplish the flip | ||
701 | _calculateSpeed : function() { | ||
702 | |||
703 | ( this.flipDirection === 'right' ) ? | ||
704 | this.flipSpeed = ( this.options.flipspeed / 180 ) * this.angle : | ||
705 | this.flipSpeed = - ( this.options.flipspeed / 180 ) * this.angle + this.options.flipspeed; | ||
706 | |||
707 | }, | ||
708 | _turnPage : function( angle, update ) { | ||
709 | |||
710 | // hack / todo: before page that was set to -181deg should have -180deg | ||
711 | this.$beforePage.css({ | ||
712 | '-webkit-transform' : 'rotateY( -180deg )', | ||
713 | '-moz-transform' : 'rotateY( -180deg )' | ||
714 | }); | ||
715 | |||
716 | // if not moving manually set a transition to flip the page | ||
717 | if( !update ) { | ||
718 | |||
719 | this.$flippingPage.css( { | ||
720 | '-webkit-transition' : '-webkit-transform ' + this.flipSpeed + 'ms ' + this.options.fliptimingfunction, | ||
721 | '-moz-transition' : '-moz-transform ' + this.flipSpeed + 'ms ' + this.options.fliptimingfunction | ||
722 | } ); | ||
723 | |||
724 | } | ||
725 | |||
726 | // if page is a right side page, we need to set its z-index higher as soon the page starts to flip. | ||
727 | // this will make the page be on "top" of the left ones. | ||
728 | // note: if the flipping page is on the left side then we set the z-index after the flip is over. | ||
729 | // this is done on the _onEndFlip function. | ||
730 | var idx = ( this.flipSide === 'r2l' ) ? this.currentPage : this.currentPage - 1; | ||
731 | if( this.flipSide === 'r2l' ) { | ||
732 | |||
733 | this.$flippingPage.css( 'z-index', this.flipPagesCount - 1 + idx ); | ||
734 | |||
735 | } | ||
736 | |||
737 | // hack (todo: issues with safari / z-indexes) | ||
738 | this.$flippingPage.find('.back').css( '-webkit-transform', 'rotateY(180deg)' ); | ||
739 | |||
740 | // update the angle | ||
741 | this.$flippingPage.css( { | ||
742 | '-webkit-transform' : 'rotateY(-' + angle + 'deg)', | ||
743 | '-moz-transform' : 'rotateY(-' + angle + 'deg)' | ||
744 | } ); | ||
745 | |||
746 | // show overlays | ||
747 | this._overlay( angle, update ); | ||
748 | |||
749 | }, | ||
750 | _addOverlays : function() { | ||
751 | |||
752 | var _self = this; | ||
753 | |||
754 | // remove current overlays | ||
755 | this._removeOverlays(); | ||
756 | |||
757 | this.hasOverlays = true; | ||
758 | |||
759 | // overlays for the flipping page. One in the front, one in the back. | ||
760 | |||
761 | this.$frontoverlay = $( '<div class="flipoverlay"></div>' ).appendTo( this.$flippingPage.find( 'div.front > .outer' ) ); | ||
762 | this.$backoverlay = $( '<div class="flipoverlay"></div>' ).appendTo( this.$flippingPage.find( 'div.back > .outer' ) ) | ||
763 | |||
764 | // overlay for the page "under" the flipping page. | ||
765 | if( this.$afterPage ) { | ||
766 | |||
767 | this.$afterOverlay = $( '<div class="overlay"></div>' ).appendTo( this.$afterPage.find( 'div.front > .outer' ) ); | ||
768 | |||
769 | } | ||
770 | |||
771 | // overlay for the page "before" the flipping page | ||
772 | if( this.$beforePage ) { | ||
773 | |||
774 | this.$beforeOverlay = $( '<div class="overlay"></div>' ).appendTo( this.$beforePage.find( 'div.back > .outer' ) ); | ||
775 | |||
776 | } | ||
777 | |||
778 | }, | ||
779 | _removeOverlays : function() { | ||
780 | |||
781 | // removes the 4 overlays | ||
782 | if( this.$frontoverlay ) | ||
783 | this.$frontoverlay.remove(); | ||
784 | if( this.$backoverlay ) | ||
785 | this.$backoverlay.remove(); | ||
786 | if( this.$afterOverlay ) | ||
787 | this.$afterOverlay.remove(); | ||
788 | if( this.$beforeOverlay ) | ||
789 | this.$beforeOverlay.remove(); | ||
790 | |||
791 | this.hasOverlays = false; | ||
792 | |||
793 | }, | ||
794 | _overlay : function( angle, update ) { | ||
795 | |||
796 | // changes the opacity of each of the overlays. | ||
797 | if( update ) { | ||
798 | |||
799 | // if update is true, meaning we are manually flipping the page, | ||
800 | // we need to calculate the opacity that corresponds to the current angle | ||
801 | var afterOverlayOpacity = - ( 1 / 90 ) * angle + 1, | ||
802 | beforeOverlayOpacity = ( 1 / 90 ) * angle - 1; | ||
803 | |||
804 | if( this.$afterOverlay ) { | ||
805 | |||
806 | this.$afterOverlay.css( 'opacity', afterOverlayOpacity ); | ||
807 | |||
808 | } | ||
809 | if( this.$beforeOverlay ) { | ||
810 | |||
811 | this.$beforeOverlay.css( 'opacity', beforeOverlayOpacity ); | ||
812 | |||
813 | } | ||
814 | |||
815 | // the flipping page will have a fixed value. | ||
816 | // todo: add a gradient instead. | ||
817 | var flipOpacity = 0.1; | ||
818 | this.$frontoverlay.css( 'opacity', flipOpacity ); | ||
819 | this.$backoverlay.css( 'opacity', flipOpacity ); | ||
820 | |||
821 | } | ||
822 | else { | ||
823 | |||
824 | var _self = this; | ||
825 | |||
826 | // if we release the mouse / touchend then we will set a transition for the overlays. | ||
827 | // we will need to take in consideration the current angle, the speed (given the angle) | ||
828 | // and the delays for each overlay (the opacity of the overlay will only change | ||
829 | // when the flipping page is on the same side). | ||
830 | var afterspeed = this.flipSpeed, | ||
831 | beforespeed = this.flipSpeed, | ||
832 | margin = 60; // hack (todo: issues with safari / z-indexes) | ||
833 | |||
834 | if( this.$afterOverlay ) { | ||
835 | |||
836 | var afterdelay = 0; | ||
837 | |||
838 | if( this.flipDirection === 'right' ) { | ||
839 | |||
840 | if( this.angle > 90 ) { | ||
841 | |||
842 | afterdelay = Math.abs( this.flipSpeed - this.options.flipspeed / 2 - margin ); | ||
843 | afterspeed = this.options.flipspeed / 2 - margin ; | ||
844 | |||
845 | } | ||
846 | else { | ||
847 | |||
848 | afterspeed -= margin; | ||
849 | |||
850 | } | ||
851 | |||
852 | } | ||
853 | else { | ||
854 | |||
855 | afterspeed = Math.abs( this.flipSpeed - this.options.flipspeed / 2 ); | ||
856 | |||
857 | } | ||
858 | |||
859 | if( afterspeed <= 0 ) afterspeed = 1; | ||
860 | |||
861 | this.$afterOverlay.css( { | ||
862 | '-webkit-transition' : 'opacity ' + afterspeed + 'ms ' + this.options.fliptimingfunction + ' ' + afterdelay + 'ms', | ||
863 | '-moz-transition' : 'opacity ' + afterspeed + 'ms ' + this.options.fliptimingfunction + ' ' + afterdelay + 'ms', | ||
864 | 'opacity' : ( this.flipDirection === 'left' ) ? 0 : 1 | ||
865 | } ).on( 'webkitTransitionEnd.flips transitionend.flips OTransitionEnd.flips', function( event ) { | ||
866 | if( _self.$beforeOverlay ) _self.$beforeOverlay.off( 'webkitTransitionEnd.flips transitionend.flips OTransitionEnd.flips'); | ||
867 | setTimeout( function() { | ||
868 | _self._adjustLayout(_self.currentPage); | ||
869 | }, _self.options.flipspeed / 2 - margin ); | ||
870 | } ); | ||
871 | |||
872 | } | ||
873 | |||
874 | if( this.$beforeOverlay ) { | ||
875 | |||
876 | var beforedelay = 0; | ||
877 | |||
878 | if( this.flipDirection === 'left' ) { | ||
879 | |||
880 | if( this.angle < 90 ) { | ||
881 | |||
882 | beforedelay = Math.abs( this.flipSpeed - this.options.flipspeed / 2 - margin ) ; | ||
883 | beforespeed = this.options.flipspeed / 2 - margin; | ||
884 | |||
885 | } | ||
886 | else { | ||
887 | |||
888 | beforespeed -= margin; | ||
889 | |||
890 | } | ||
891 | |||
892 | } | ||
893 | else { | ||
894 | |||
895 | beforespeed = Math.abs( this.flipSpeed - this.options.flipspeed / 2 ); | ||
896 | |||
897 | } | ||
898 | |||
899 | if( beforespeed <= 0 ) beforespeed = 1; | ||
900 | |||
901 | this.$beforeOverlay.css( { | ||
902 | '-webkit-transition' : 'opacity ' + beforespeed + 'ms ' + this.options.fliptimingfunction + ' ' + beforedelay + 'ms', | ||
903 | '-moz-transition' : 'opacity ' + beforespeed + 'ms ' + this.options.fliptimingfunction + ' ' + beforedelay + 'ms', | ||
904 | 'opacity' : ( this.flipDirection === 'left' ) ? 1 : 0 | ||
905 | } ).on( 'webkitTransitionEnd.flips transitionend.flips OTransitionEnd.flips', function( event ) { | ||
906 | if( _self.$afterOverlay ) _self.$afterOverlay.off( 'webkitTransitionEnd.flips transitionend.flips OTransitionEnd.flips'); | ||
907 | _self._adjustLayout(_self.currentPage); | ||
908 | } ); | ||
909 | |||
910 | } | ||
911 | |||
912 | } | ||
913 | |||
914 | } | ||
915 | }; | ||
916 | |||
917 | var logError = function( message ) { | ||
918 | if ( this.console ) { | ||
919 | console.error( message ); | ||
920 | } | ||
921 | }; | ||
922 | |||
923 | $.fn.flips = function( options ) { | ||
924 | |||
925 | if ( typeof options === 'string' ) { | ||
926 | |||
927 | var args = Array.prototype.slice.call( arguments, 1 ); | ||
928 | |||
929 | this.each(function() { | ||
930 | |||
931 | var instance = $.data( this, 'flips' ); | ||
932 | |||
933 | if ( !instance ) { | ||
934 | logError( "cannot call methods on flips prior to initialization; " + | ||
935 | "attempted to call method '" + options + "'" ); | ||
936 | return; | ||
937 | } | ||
938 | |||
939 | if ( !$.isFunction( instance[options] ) || options.charAt(0) === "_" ) { | ||
940 | logError( "no such method '" + options + "' for flips instance" ); | ||
941 | return; | ||
942 | } | ||
943 | |||
944 | instance[ options ].apply( instance, args ); | ||
945 | |||
946 | }); | ||
947 | |||
948 | } | ||
949 | else { | ||
950 | |||
951 | this.each(function() { | ||
952 | |||
953 | var instance = $.data( this, 'flips' ); | ||
954 | if ( !instance ) { | ||
955 | $.data( this, 'flips', new $.Flips( options, this ) ); | ||
956 | } | ||
957 | }); | ||
958 | |||
959 | } | ||
960 | |||
961 | return this; | ||
962 | |||
963 | }; | ||
964 | |||
965 | })( window ); \ No newline at end of file | ||