Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Accordion: Attempt at improving animation. #587

Closed
wants to merge 11 commits into from

2 participants

Scott González Jörn Zaefferer
Scott González

This is an attempt at fixing #4178.

The logic is to use core as much as possible, then override the animation of the height of the panel being shown. Most browsers don't support fractional pixels, and even browsers that do support fractions can't handle the level of precision that we get during an animation. This causes the combined height of the panel being shown and the panel being hidden to jump around a bit during the animation. We try to deal with that by recalculating the total height at each step of the animation and adjusting the height of the panel being shown to account for any pixels that were lost or gained by the browser not being able to handle the exact values that were calculated by .animate().

This works great in Chrome and Opera. It's ok in IE and not so great in Firefox. However, this is better than what we have now in all browsers.

This code should not e committed as is. $.fx.step._height and the data key should probably be prefixed for accordion, there are var statements in the middle of functions, etc. This pull is just to keep notes around and hopefully provide a good starting point for a final solution.

ui/jquery.ui.accordion.js
@@ -17,6 +17,7 @@ $.widget( "ui.accordion", {
version: "@VERSION",
options: {
active: 0,
+ // TODO: animation option
Jörn Zaefferer Owner

actually animate

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Scott González

Landed in 3d9f6b5 :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jan 31, 2012
  1. Scott González
Commits on Feb 9, 2012
  1. Scott González

    Merge branch 'master' into accordion-animation

    scottgonzalez authored
    Conflicts:
    	ui/jquery.ui.accordion.js
  2. Scott González
  3. Scott González
Commits on Feb 11, 2012
  1. Scott González
Commits on Feb 12, 2012
  1. Scott González
  2. Scott González
  3. Scott González

    Merge branch 'master' into accordion-animation

    scottgonzalez authored
    Conflicts:
    	ui/jquery.ui.accordion.js
  4. Scott González

    Accordion: Fixed animation for heightStyle: content; Force height of …

    scottgonzalez authored
    …accordion for non-content heightStyle to improve animation in Firefox.
Commits on Feb 16, 2012
  1. Scott González
  2. Scott González
This page is out of date. Refresh to see the latest.
2  tests/unit/accordion/accordion_defaults.js
View
@@ -1,7 +1,7 @@
commonWidgetTests( "accordion", {
defaults: {
active: 0,
- animated: "slide",
+ animate: {},
collapsible: false,
disabled: false,
event: "click",
1  tests/unit/accordion/accordion_defaults_deprecated.js
View
@@ -1,6 +1,7 @@
commonWidgetTests( "accordion", {
defaults: {
active: 0,
+ animate: null,
animated: "slide",
autoHeight: true,
clearStyle: false,
6 tests/unit/accordion/accordion_test_helpers.js
View
@@ -18,13 +18,13 @@ function accordion_equalHeights( accordion, min, max ) {
}
function accordion_setupTeardown() {
- var animated = $.ui.accordion.prototype.options.animated;
+ var animate = $.ui.accordion.prototype.options.animate;
return {
setup: function() {
- $.ui.accordion.prototype.options.animated = false;
+ $.ui.accordion.prototype.options.animate = false;
},
teardown: function() {
- $.ui.accordion.prototype.options.animated = animated;
+ $.ui.accordion.prototype.options.animate = animate;
}
};
}
267 ui/jquery.ui.accordion.js
View
@@ -17,7 +17,7 @@ $.widget( "ui.accordion", {
version: "@VERSION",
options: {
active: 0,
- animated: "slide",
+ animate: {},
collapsible: false,
event: "click",
header: "> li > :first-child,> :not(li):even",
@@ -35,7 +35,7 @@ $.widget( "ui.accordion", {
_create: function() {
var options = this.options;
- this.lastToggle = {};
+ this.prevShow = this.prevHide = $();
this.element.addClass( "ui-accordion ui-widget ui-helper-reset" );
this.headers = this.element.find( options.header )
@@ -62,6 +62,7 @@ $.widget( "ui.accordion", {
this.active.next().addClass( "ui-accordion-content-active" );
this._createIcons();
+ this.originalHeight = this.element[0].style.height;
this.refresh();
// ARIA
@@ -156,6 +157,7 @@ $.widget( "ui.accordion", {
.removeAttr( "role" )
.removeClass( "ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-accordion-disabled ui-state-disabled" );
if ( this.options.heightStyle !== "content" ) {
+ this.element.css( "height", this.originalHeight );
contents.css( "height", "" );
}
},
@@ -229,12 +231,12 @@ $.widget( "ui.accordion", {
},
refresh: function() {
- var options = this.options,
+ var heightStyle = this.options.heightStyle,
parent = this.element.parent(),
maxHeight,
overflow;
- if ( options.heightStyle === "fill" ) {
+ if ( heightStyle === "fill" ) {
// IE 6 treats height like minHeight, so we need to turn off overflow
// in order to get a reliable height
// we use the minHeight support test because we assume that only
@@ -267,7 +269,7 @@ $.widget( "ui.accordion", {
$( this ).innerHeight() + $( this ).height() ) );
})
.css( "overflow", "auto" );
- } else if ( options.heightStyle === "auto" ) {
+ } else if ( heightStyle === "auto" ) {
maxHeight = 0;
this.headers.next()
.each(function() {
@@ -276,7 +278,9 @@ $.widget( "ui.accordion", {
.height( maxHeight );
}
- return this;
+ if ( heightStyle !== "content" ) {
+ this.element.height( this.element.height() );
+ }
},
_activate: function( index ) {
@@ -361,41 +365,20 @@ $.widget( "ui.accordion", {
},
_toggle: function( data ) {
- var that = this,
- options = this.options,
- toShow = data.newContent,
- toHide = data.oldContent;
-
- function complete() {
- that._completed( data );
- }
+ var toShow = data.newContent,
+ toHide = this.prevShow.length ? this.prevShow : data.oldContent;
- if ( options.animated ) {
- var animations = $.ui.accordion.animations,
- animation = options.animated,
- additional;
-
- if ( !animations[ animation ] ) {
- additional = {
- easing: $.easing[ animation ] ? animation : "slide",
- duration: 700
- };
- animation = "slide";
- }
+ // handle activating a panel during the animation for another activation
+ this.prevShow.add( this.prevHide ).stop( true, true );
+ this.prevShow = toShow;
+ this.prevHide = toHide;
- animations[ animation ]({
- widget: this,
- toShow: toShow,
- toHide: toHide,
- prevShow: this.lastToggle.toShow,
- prevHide: this.lastToggle.toHide,
- complete: complete,
- down: toShow.length && ( !toHide.length || ( toShow.index() < toHide.index() ) )
- }, additional );
+ if ( this.options.animate ) {
+ this._animate( toShow, toHide, data );
} else {
toHide.hide();
toShow.show();
- complete();
+ this._completed( data );
}
// TODO assert that the blur and focus triggers are really necessary, remove otherwise
@@ -415,17 +398,51 @@ $.widget( "ui.accordion", {
.focus();
},
+ _animate: function( toShow, toHide, data ) {
+ var total, easing, duration,
+ that = this,
+ down = toShow.length &&
+ ( !toHide.length || ( toShow.index() < toHide.index() ) ),
+ animate = this.options.animate || {},
+ options = down && animate.down || animate,
+ complete = function() {
+ toShow.removeData( "accordionHeight" );
+ that._completed( data );
+ };
+
+ if ( typeof options === "number" ) {
+ duration = options;
+ }
+ if ( typeof options === "string" ) {
+ easing = options;
+ }
+ // fall back from options to animation in case of partial down settings
+ easing = easing || options.easing || animate.easing;
+ duration = duration || options.duration || animate.duration;
+
+ if ( !toHide.size() ) {
+ return toShow.animate( showProps, duration, easing, complete );
+ }
+ if ( !toShow.size() ) {
+ return toHide.animate( hideProps, duration, easing, complete );
+ }
+
+ total = toShow.show().outerHeight();
+ toHide.animate( hideProps, duration, easing );
+ toShow
+ .hide()
+ .data( "accordionHeight", {
+ total: total,
+ toHide: toHide
+ })
+ .animate( this.options.heightStyle === "content" ? showProps : showPropsAdjust,
+ duration, easing, complete );
+ },
+
_completed: function( data ) {
var toShow = data.newContent,
toHide = data.oldContent;
- if ( this.options.heightStyle === "content" ) {
- toShow.add( toHide ).css({
- height: "",
- overflow: ""
- });
- }
-
// other classes are removed before the animation; this one needs to stay until completed
toHide.removeClass( "ui-accordion-content-active" );
// Work around for rendering bug in IE (#5421)
@@ -437,123 +454,19 @@ $.widget( "ui.accordion", {
}
});
-$.extend( $.ui.accordion, {
- animations: {
- slide: function( options, additions ) {
- if ( options.prevShow || options.prevHide ) {
- options.prevHide.stop( true, true );
- options.toHide = options.prevShow;
- }
-
- var showOverflow = options.toShow.css( "overflow" ),
- showOverflowX = options.toHide.css( "overflowX" ),
- showOverflowY = options.toHide.css( "overflowY" ),
- hideOverflow = options.toHide.css( "overflow" ),
- hideOverflowX = options.toHide.css( "overflowX" ),
- hideOverflowY = options.toHide.css( "overflowY" ),
- percentDone = 0,
- showProps = {},
- hideProps = {},
- fxAttrs = [ "height", "paddingTop", "paddingBottom" ],
- originalWidth;
- options = $.extend({
- easing: "swing",
- duration: 300
- }, options, additions );
-
- options.widget.lastToggle = options;
-
- if ( !options.toHide.size() ) {
- originalWidth = options.toShow[0].style.width;
- options.toShow
- .show()
- .width( options.toShow.width() )
- .hide()
- .animate({
- height: "show",
- paddingTop: "show",
- paddingBottom: "show"
- }, {
- duration: options.duration,
- easing: options.easing,
- complete: function() {
- options.toShow.width( originalWidth );
- options.complete();
- }
- });
- return;
- }
- if ( !options.toShow.size() ) {
- options.toHide.animate({
- height: "hide",
- paddingTop: "hide",
- paddingBottom: "hide"
- }, options );
- return;
- }
- // fix width before calculating height of hidden element
- var s = options.toShow;
- originalWidth = s[0].style.width;
- s.width( s.parent().width()
- - parseFloat( s.css( "paddingLeft" ) )
- - parseFloat( s.css( "paddingRight" ) )
- - ( parseFloat( s.css( "borderLeftWidth" ) ) || 0 )
- - ( parseFloat( s.css( "borderRightWidth" ) ) || 0 ) );
-
- $.each( fxAttrs, function( i, prop ) {
- hideProps[ prop ] = "hide";
-
- var parts = ( "" + $.css( options.toShow[0], prop ) ).match( /^([\d+-.]+)(.*)$/ ),
- // work around bug when a panel has no height - #7335
- propVal = prop === "height" && parts[ 1 ] === "0" ? 1 : parts[ 1 ];
- showProps[ prop ] = {
- value: propVal,
- unit: parts[ 2 ] || "px"
- };
- });
- options.toShow.css({ height: 0, overflow: "hidden" }).show();
- options.toHide
- .filter( ":hidden" )
- .each( options.complete )
- .end()
- .filter( ":visible" )
- .animate( hideProps, {
- step: function( now, settings ) {
- if ( settings.prop == "height" || settings.prop == "paddingTop" || settings.prop == "paddingBottom" ) {
- percentDone = ( settings.end - settings.start === 0 ) ? 0 :
- ( settings.now - settings.start ) / ( settings.end - settings.start );
- }
-
- options.toShow[ 0 ].style[ settings.prop ] =
- ( percentDone * showProps[ settings.prop ].value )
- + showProps[ settings.prop ].unit;
- },
- duration: options.duration,
- easing: options.easing,
- complete: function() {
- options.toShow.css({
- width: originalWidth,
- overflow: showOverflow,
- overflowX: showOverflowX,
- overflowY: showOverflowY
- });
- options.toHide.css({
- overflow: hideOverflow,
- overflowX: hideOverflowX,
- overflowY: hideOverflowY
- });
- options.complete();
- }
- });
- },
- bounceslide: function( options ) {
- this.slide( options, {
- easing: options.down ? "easeOutBounce" : "swing",
- duration: options.down ? 1000 : 200
- });
- }
- }
-});
+$.fx.step.accordionHeight = function( fx ) {
+ var elem = $( fx.elem ),
+ data = elem.data( "accordionHeight" );
+ elem.height( data.total - elem.outerHeight() - data.toHide.outerHeight() + elem.height() );
+};
+var hideProps = {},
+ showProps = {},
+ showPropsAdjust = {};
+hideProps.height = hideProps.paddingTop = hideProps.paddingBottom =
+ hideProps.borderTopWidth = hideProps.borderBottomWidth = "hide";
+showProps.height = showProps.paddingTop = showProps.paddingBottom =
+ showProps.borderTopWidth = showProps.borderBottomWidth = "show";
+$.extend( showPropsAdjust, showProps, { accordionHeight: "show" } );
@@ -697,6 +610,40 @@ if ( $.uiBackCompat !== false ) {
return ret;
};
}( jQuery, jQuery.ui.accordion.prototype ) );
+
+ // animated option
+ // NOTE: this only provides support for "slide", "bounceslide", and easings
+ // not the full $.ui.accordion.animations API
+ (function( $, prototype ) {
+ $.extend( prototype.options, {
+ animate: null,
+ animated: "slide"
+ });
+
+ var _create = prototype._create;
+ prototype._create = function() {
+ var options = this.options;
+ if ( options.animate === null ) {
+ if ( !options.animated ) {
+ options.animate = false;
+ } else if ( options.animated === "slide" ) {
+ options.animate = 300;
+ } else if ( options.animated === "bounceslide" ) {
+ options.animate = {
+ duration: 200,
+ down: {
+ easing: "easeOutBounce",
+ duration: 1000
+ }
+ }
+ } else {
+ options.animate = options.animated;
+ }
+ }
+
+ _create.call( this );
+ };
+ }( jQuery, jQuery.ui.accordion.prototype ) );
}
})( jQuery );
Something went wrong with that request. Please try again.