Skip to content
This repository has been archived by the owner on Oct 8, 2021. It is now read-only.

Commit

Permalink
AnimationComplete: Add transition, fallbacks and remove memory leaks
Browse files Browse the repository at this point in the history
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
Closes gh-7001
  • Loading branch information
arschmitz committed Feb 12, 2014
1 parent bf9ce84 commit 749c78e
Show file tree
Hide file tree
Showing 15 changed files with 384 additions and 82 deletions.
1 change: 1 addition & 0 deletions css/structure/jquery.mobile.panel.css
Expand Up @@ -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;
}
Expand Down
1 change: 1 addition & 0 deletions js/index.php
Expand Up @@ -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',
Expand Down
105 changes: 105 additions & 0 deletions js/jquery.mobile.animationComplete.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;

This comment has been minimized.

Copy link
@dayyeung

dayyeung Feb 14, 2014

default is a reserved keyword and this results in syntax error on a lot of old browsers including Android 2.3 and IE8.

}
}

// 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");
1 change: 1 addition & 0 deletions js/jquery.mobile.js
Expand Up @@ -11,6 +11,7 @@ define([
"./navigation/method",
"./transitions/handlers",
"./transitions/visuals",
"./jquery.mobile.animationComplete.js",
"./jquery.mobile.navigation",
"./jquery.mobile.degradeInputs",
"./widgets/page.dialog",
Expand Down
17 changes: 3 additions & 14 deletions js/jquery.mobile.navigation.js
Expand Up @@ -13,6 +13,7 @@ define( [
"./jquery.mobile.events",
"./jquery.mobile.support",
"jquery-plugins/jquery.hashchange",
"./jquery.mobile.animationComplete",
"./widgets/pagecontainer",
"./widgets/page",
"./transitions/handlers" ], function( jQuery ) {
Expand Down Expand Up @@ -86,7 +87,7 @@ define( [
}
};

//direct focus to the page title, or otherwise first focusable element
// Direct focus to the page title, or otherwise first focusable element
$.mobile.focusPage = function ( page ) {
var autofocus = page.find( "[autofocus]" ),
pageTitle = page.find( ".ui-title:eq(0)" );
Expand All @@ -108,19 +109,7 @@ define( [
return transition;
};

/* 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 );
}
};
// Exposed $.mobile methods

$.mobile.changePage = function( to, options ) {
$.mobile.pageContainer.pagecontainer( "change", to, options );
Expand Down
38 changes: 0 additions & 38 deletions js/jquery.mobile.support.js
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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 ||
Expand Down
2 changes: 1 addition & 1 deletion js/transitions/serial.js
Expand Up @@ -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( $ ) {
Expand Down
13 changes: 6 additions & 7 deletions js/transitions/transition.js
Expand Up @@ -10,6 +10,7 @@ define( [ "jquery",

// TODO event.special.scrollstart
"../events/touch",
"../jquery.mobile.animationComplete",

// TODO $.mobile.focusPage reference
"../jquery.mobile.navigation" ], function( jQuery ) {
Expand Down Expand Up @@ -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();
}

Expand Down
2 changes: 1 addition & 1 deletion js/widgets/fixedToolbar.js
Expand Up @@ -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 ) {

Expand Down
5 changes: 3 additions & 2 deletions js/widgets/forms/autogrow.js
Expand Up @@ -62,10 +62,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._timeout();
}
Expand Down
25 changes: 8 additions & 17 deletions js/widgets/panel.js
Expand Up @@ -308,7 +308,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 );
}
Expand Down Expand Up @@ -340,7 +340,6 @@ $.widget( "mobile.panel", {
}
},
complete = function() {
self.document.off( self._transitionEndEvents, complete );

if ( o.display !== "overlay" ) {
self._wrapper().addClass( o.classes.pageContentPrefix + "-open" );
Expand Down Expand Up @@ -372,11 +371,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 );

Expand All @@ -385,13 +379,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 );
}
Expand Down Expand Up @@ -430,8 +428,6 @@ $.widget( "mobile.panel", {
this[ this._open ? "close" : "open" ]();
},

_transitionEndEvents: "webkitTransitionEnd oTransitionEnd otransitionend transitionend msTransitionEnd",

_destroy: function() {
var otherPanels,
o = this.options,
Expand Down Expand Up @@ -465,10 +461,6 @@ $.widget( "mobile.panel", {

this.document.off( "panelopen panelclose" );

if ( this._open ) {
this.document.off( this._transitionEndEvents );
$.mobile.resetActivePageHeight();
}
}

if ( this._open ) {
Expand All @@ -483,8 +475,7 @@ $.widget( "mobile.panel", {
.off( "panelbeforeopen" )
.off( "panelhide" )
.off( "keyup.panel" )
.off( "updatelayout" )
.off( this._transitionEndEvents );
.off( "updatelayout" );

if ( this._modal ) {
this._modal.remove();
Expand Down
5 changes: 3 additions & 2 deletions js/widgets/popup.js
Expand Up @@ -21,6 +21,7 @@ define( [
"../navigation/history",
"../navigation/navigator",
"../navigation/method",
"../jquery.mobile.animationComplete",
"../jquery.mobile.navigation",
"jquery-plugins/jquery.hashchange" ], function( jQuery ) {
//>>excludeEnd("jqmBuildExclude");
Expand Down Expand Up @@ -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;
}
}
Expand Down

0 comments on commit 749c78e

Please sign in to comment.