Permalink
Browse files

AnimationComplete: Add transition, fallbacks and remove memory leaks

Also no its own module including support tests for animations and
transitions

Fixes gh-5156
Fixes gh-6816
Fixes gh-6697
Fixes gh-6895
Fixes gh-6148
  • Loading branch information...
arschmitz committed Jan 22, 2014
1 parent d8375b7 commit f4fbff2e357d95a79eeeee98138aa131c1e9bb20
@@ -69,6 +69,7 @@
/* Animate class is added to panel, wrapper and fixed toolbars */
.ui-panel-animate {
-webkit-transition: -webkit-transform 300ms ease;
-webkit-transition-duration: 300ms;
-moz-transition: -moz-transform 300ms ease;
transition: transform 300ms ease;
}
View
@@ -195,7 +195,8 @@ define( [ "jquery", "../jquery.mobile.vmouse", "../jquery.mobile.support.touch"
stop = $.event.special.swipe.stop( event );
if ( !emitted ) {
emitted = $.event.special.swipe.handleSwipe( start, stop, thisObject, origTarget );
emitted = true;
$.event.special.swipe.handleSwipe( start, stop, thisObject, origTarget );
}
// prevent scrolling
if ( Math.abs( start.coords[ 0 ] - stop.coords[ 0 ] ) > $.event.special.swipe.scrollSupressionThreshold ) {
View
@@ -22,6 +22,7 @@
'jquery.mobile.defaults.js',
'jquery.mobile.helpers.js',
'jquery.mobile.data.js',
'jquery.mobile.animationComplete.js',
'widgets/page.js',
'widgets/page.dialog.js',
'widgets/loader.js',
@@ -0,0 +1,105 @@
//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
//>>description: A handler for css transition & animation end events to ensure callback is executed
//>>label: Animation Complete
//>>group: Core
define( [
"jquery"
], function( jQuery ) {
//>>excludeEnd("jqmBuildExclude");
(function( $, undefined ) {
var props = {
"animation": {},
"transition": {}
},
testElement = document.createElement( "a" ),
vendorPrefixes = [ "", "webkit-", "moz-", "o-" ];
$.each( [ "animation", "transition" ], function( i, test ) {
// Get correct name for test
var testName = ( i === 0 ) ? test + "-" + "name" : test;
$.each( vendorPrefixes, function( j, prefix ){
if ( testElement.style[ $.camelCase( prefix + testName ) ] !== undefined ) {
props[ test ][ "prefix" ] = prefix;
return false;
}
});
// Set event and duration names for later use
props[ test ][ "duration" ] =
$.camelCase( props[ test ][ "prefix" ] + test + "-" + "duration" );
props[ test ][ "event" ] =
$.camelCase( props[ test ][ "prefix" ] + test + "-" + "end" );
// All lower case if not a vendor prop
if( props[ test ][ "prefix" ] === "" ){
props[ test ][ "duration" ] = props[ test ][ "duration" ].toLowerCase();
props[ test ][ "event" ] = props[ test ][ "event" ].toLowerCase();
}
});
// If a valid prefix was found then the it is supported by the browser
$.support.cssTransitions = ( props[ "transition" ][ "prefix" ] !== undefined );
$.support.cssAnimations = ( props[ "animation" ][ "prefix" ] !== undefined );
// Remove the testElement
$( testElement ).remove();
//animation complete callback
$.fn.animationComplete = function( callback, type, fallbackTime ) {
var timer, duration,
that = this,
animationType = ( !type || type === "animation" ) ? "animation" : "transition";
// Make sure selected type is supported by browser
if ( ( $.support.cssTransitions && animationType === "transition" ) ||
( $.support.cssAnimations && animationType === "animation" ) ) {
// If a fallback time was not passed set one
if ( fallbackTime === undefined ) {
// Make sure the was not bound to document before checking .css
if( $( this ).context !== document ) {
// Parse the durration since its in second multiple by 1000 for milliseconds
// Multiply by 3 to make sure we give the animation plenty of time.
duration = parseFloat(
$( this ).css( props[ animationType ].duration )
) * 3000;
}
// If we could not read a duration use the default
if( duration === 0 || duration === undefined ) {
duration = $.fn.animationComplete.default;
}
}
// Sets up the fallback if event never comes
timer = setTimeout( function() {
$( that ).off( props[ animationType ].event );
callback.apply( that );
}, duration );
// Bind the event
return $( this ).one( props[ animationType ].event, function() {
// Clear the timer so we dont call callback twice
clearTimeout( timer );
callback.call( this, arguments );
});
} else {
// CSS animation / transitions not supported
// Defer execution for consistency between webkit/non webkit
setTimeout( $.proxy( callback, this ), 0 );
return $( this );
}
};
// Allow default callback to be configured on mobileInit
$.fn.animationComplete.default = 1000;
})( jQuery );
//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
});
//>>excludeEnd("jqmBuildExclude");
View
@@ -11,6 +11,7 @@ define([
"./navigation/method",
"./transitions/handlers",
"./transitions/visuals",
"./jquery.mobile.animationComplete.js",
"./jquery.mobile.navigation",
"./jquery.mobile.degradeInputs",
"./widgets/page.dialog",
@@ -12,6 +12,7 @@ define( [
"./navigation/method",
"./jquery.mobile.events",
"./jquery.mobile.support",
"./jquery.mobile.animationComplete",
"jquery.hashchange",
"./widgets/pagecontainer",
"./widgets/page",
@@ -110,17 +111,7 @@ define( [
/* exposed $.mobile methods */
//animation complete callback
$.fn.animationComplete = function( callback ) {
if ( $.support.cssTransitions ) {
return $( this ).one( "webkitAnimationEnd animationend", callback );
}
else{
// defer execution for consistency between webkit/non webkit
setTimeout( callback, 0 );
return $( this );
}
};
$.mobile.changePage = function( to, options ) {
$.mobile.pageContainer.pagecontainer( "change", to, options );
@@ -23,43 +23,10 @@ var fakeBody = $( "<body>" ).prependTo( "html" ),
fbCSS = fakeBody[ 0 ].style,
vendors = [ "Webkit", "Moz", "O" ],
webos = "palmGetResource" in window, //only used to rule out scrollTop
opera = window.opera,
operamini = window.operamini && ({}).toString.call( window.operamini ) === "[object OperaMini]",
bb = window.blackberry && !propExists( "-webkit-transform" ), //only used to rule out box shadow, as it's filled opaque on BB 5 and lower
nokiaLTE7_3;
function validStyle( prop, value, check_vend ) {
var div = document.createElement( "div" ),
uc = function( txt ) {
return txt.charAt( 0 ).toUpperCase() + txt.substr( 1 );
},
vend_pref = function( vend ) {
if ( vend === "" ) {
return "";
} else {
return "-" + vend.charAt( 0 ).toLowerCase() + vend.substr( 1 ) + "-";
}
},
check_style = function( vend ) {
var vend_prop = vend_pref( vend ) + prop + ": " + value + ";",
uc_vend = uc( vend ),
propStyle = uc_vend + ( uc_vend === "" ? prop : uc( prop ) );
div.setAttribute( "style", vend_prop );
if ( !!div.style[ propStyle ] ) {
ret = true;
}
},
check_vends = check_vend ? check_vend : vendors,
i, ret;
for( i = 0; i < check_vends.length; i++ ) {
check_style( check_vends[i] );
}
return !!ret;
}
// inline SVG support test
function inlineSVG() {
// Thanks Modernizr & Erik Dahlstrom
@@ -206,10 +173,6 @@ function fixedPosition() {
}
$.extend( $.support, {
cssTransitions: "WebKitTransitionEvent" in window ||
validStyle( "transition", "height 100ms linear", [ "Webkit", "Moz", "" ] ) &&
!$.mobile.browser.oldIE && !opera,
// Note, Chrome for iOS has an extremely quirky implementation of popstate.
// We've chosen to take the shortest path to a bug fix here for issue #5426
// See the following link for information about the regex chosen
@@ -224,7 +187,6 @@ $.extend( $.support, {
cssPseudoElement: !!propExists( "content" ),
touchOverflow: !!propExists( "overflowScrolling" ),
cssTransform3d: transform3dTest(),
cssAnimations: !!propExists( "animationName" ),
boxShadow: !!propExists( "boxShadow" ) && !bb,
fixedPosition: fixedPosition(),
scrollTop: ("pageXOffset" in window ||
View
@@ -3,7 +3,7 @@
//>>label: Transition Serial
//>>group: Transitions
define( [ "jquery", "./transition" ], function( jQuery ) {
define( [ "jquery", "../jquery.mobile.animationComplete", "./transition" ], function( jQuery ) {
//>>excludeEnd("jqmBuildExclude");
(function( $ ) {
@@ -10,6 +10,7 @@ define( [ "jquery",
// TODO event.special.scrollstart
"../events/touch",
"../jquery.mobile.animationComplete",
// TODO $.mobile.focusPage reference
"../jquery.mobile.navigation" ], function( jQuery ) {
@@ -109,17 +110,15 @@ define( [ "jquery",
}
});
if ( !none ) {
this.$to.animationComplete( $.proxy(function() {
this.doneIn();
}, this ));
}
this.$to
.removeClass( this.toPreClass )
.addClass( this.name + " in " + reverseClass );
if ( none ) {
if ( !none ) {
this.$to.animationComplete( $.proxy(function() {
this.doneIn();
}, this ));
} else {
this.doneIn();
}
@@ -5,7 +5,7 @@
//>>css.structure: ../css/structure/jquery.mobile.fixedToolbar.css
//>>css.theme: ../css/themes/default/jquery.mobile.theme.css
define( [ "jquery", "../jquery.mobile.widget", "../jquery.mobile.core", "../jquery.mobile.navigation", "./page","./toolbar","../jquery.mobile.zoom" ], function( jQuery ) {
define( [ "jquery", "../jquery.mobile.widget", "../jquery.mobile.core", "../jquery.mobile.animationComplete", "../jquery.mobile.navigation", "./page","./toolbar","../jquery.mobile.zoom" ], function( jQuery ) {
//>>excludeEnd("jqmBuildExclude");
(function( $, undefined ) {
@@ -60,10 +60,11 @@ define( [
if ( event.type !== "popupbeforeposition" ) {
this.element
.addClass( "ui-textinput-autogrow-resize" )
.one( "transitionend webkitTransitionEnd oTransitionEnd",
.animationComplete(
$.proxy( function() {
this.element.removeClass( "ui-textinput-autogrow-resize" );
}, this ) );
}, this ),
"transition" );
}
this._prepareHeightUpdate();
}
View
@@ -297,7 +297,7 @@ $.widget( "mobile.panel", {
}
if ( !immediate && $.support.cssTransform3d && !!o.animate ) {
self.document.on( self._transitionEndEvents, complete );
self.element.animationComplete( complete, "transition" );
} else {
setTimeout( complete, 0 );
}
@@ -329,7 +329,6 @@ $.widget( "mobile.panel", {
}
},
complete = function() {
self.document.off( self._transitionEndEvents, complete );
if ( o.display !== "overlay" ) {
self._wrapper().addClass( o.classes.pageContentPrefix + "-open" );
@@ -361,11 +360,6 @@ $.widget( "mobile.panel", {
o = this.options,
_closePanel = function() {
if ( !immediate && $.support.cssTransform3d && !!o.animate ) {
self.document.on( self._transitionEndEvents, complete );
} else {
setTimeout( complete, 0 );
}
self.element.removeClass( o.classes.panelOpen );
@@ -374,13 +368,17 @@ $.widget( "mobile.panel", {
self._fixedToolbars().removeClass( self._pageContentOpenClasses );
}
if ( !immediate && $.support.cssTransform3d && !!o.animate ) {
self.element.animationComplete( complete, "transition" );
} else {
setTimeout( complete, 0 );
}
if ( self._modal ) {
self._modal.removeClass( self._modalOpenClasses );
}
},
complete = function() {
self.document.off( self._transitionEndEvents, complete );
if ( o.theme && o.display !== "overlay" ) {
self._page().parent().removeClass( o.classes.pageContainer + "-themed " + o.classes.pageContainer + "-" + o.theme );
}
@@ -419,8 +417,6 @@ $.widget( "mobile.panel", {
this[ this._open ? "close" : "open" ]();
},
_transitionEndEvents: "webkitTransitionEnd oTransitionEnd otransitionend transitionend msTransitionEnd",
_destroy: function() {
var otherPanels,
o = this.options,
@@ -454,10 +450,6 @@ $.widget( "mobile.panel", {
this.document.off( "panelopen panelclose" );
if ( this._open ) {
this.document.off( this._transitionEndEvents );
$.mobile.resetActivePageHeight();
}
}
if ( this._open ) {
@@ -472,8 +464,7 @@ $.widget( "mobile.panel", {
.off( "panelbeforeopen" )
.off( "panelhide" )
.off( "keyup.panel" )
.off( "updatelayout" )
.off( this._transitionEndEvents );
.off( "updatelayout" );
this._closeLink.off( "click.panel" );
View
@@ -21,6 +21,7 @@ define( [
"../navigation/history",
"../navigation/navigator",
"../navigation/method",
"../jquery.mobile.animationComplete",
"../jquery.mobile.navigation",
"jquery.hashchange" ], function( jQuery ) {
//>>excludeEnd("jqmBuildExclude");
@@ -542,9 +543,9 @@ $.widget( "mobile.popup", {
}
if ( this._fallbackTransition ) {
this._ui.container
.animationComplete( $.proxy( args.prerequisites.container, "resolve" ) )
.addClass( args.containerClassToAdd )
.removeClass( args.classToRemove );
.removeClass( args.classToRemove )
.animationComplete( $.proxy( args.prerequisites.container, "resolve" ) );
return;
}
}
Oops, something went wrong.

0 comments on commit f4fbff2

Please sign in to comment.