Accordion: Rewrote the animation code. Fixes #4178 - Accordion animation a bit jumpy in some browsers. Fixes #7371 - Accordion: Incorrect size when zoomed.

This commit is contained in:
Scott González 2012-02-16 16:51:46 -05:00
parent f262a531fa
commit 3d9f6b5bc7
4 changed files with 112 additions and 164 deletions

View File

@ -1,7 +1,7 @@
commonWidgetTests( "accordion", { commonWidgetTests( "accordion", {
defaults: { defaults: {
active: 0, active: 0,
animated: "slide", animate: {},
collapsible: false, collapsible: false,
disabled: false, disabled: false,
event: "click", event: "click",

View File

@ -1,6 +1,7 @@
commonWidgetTests( "accordion", { commonWidgetTests( "accordion", {
defaults: { defaults: {
active: 0, active: 0,
animate: null,
animated: "slide", animated: "slide",
autoHeight: true, autoHeight: true,
clearStyle: false, clearStyle: false,

View File

@ -18,13 +18,13 @@ function accordion_equalHeights( accordion, min, max ) {
} }
function accordion_setupTeardown() { function accordion_setupTeardown() {
var animated = $.ui.accordion.prototype.options.animated; var animate = $.ui.accordion.prototype.options.animate;
return { return {
setup: function() { setup: function() {
$.ui.accordion.prototype.options.animated = false; $.ui.accordion.prototype.options.animate = false;
}, },
teardown: function() { teardown: function() {
$.ui.accordion.prototype.options.animated = animated; $.ui.accordion.prototype.options.animate = animate;
} }
}; };
} }

View File

@ -17,7 +17,7 @@ $.widget( "ui.accordion", {
version: "@VERSION", version: "@VERSION",
options: { options: {
active: 0, active: 0,
animated: "slide", animate: {},
collapsible: false, collapsible: false,
event: "click", event: "click",
header: "> li > :first-child,> :not(li):even", header: "> li > :first-child,> :not(li):even",
@ -35,7 +35,7 @@ $.widget( "ui.accordion", {
_create: function() { _create: function() {
var options = this.options; var options = this.options;
this.lastToggle = {}; this.prevShow = this.prevHide = $();
this.element.addClass( "ui-accordion ui-widget ui-helper-reset" ); this.element.addClass( "ui-accordion ui-widget ui-helper-reset" );
this.headers = this.element.find( options.header ) this.headers = this.element.find( options.header )
@ -62,6 +62,7 @@ $.widget( "ui.accordion", {
this.active.next().addClass( "ui-accordion-content-active" ); this.active.next().addClass( "ui-accordion-content-active" );
this._createIcons(); this._createIcons();
this.originalHeight = this.element[0].style.height;
this.refresh(); this.refresh();
// ARIA // ARIA
@ -156,6 +157,7 @@ $.widget( "ui.accordion", {
.removeAttr( "role" ) .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" ); .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" ) { if ( this.options.heightStyle !== "content" ) {
this.element.css( "height", this.originalHeight );
contents.css( "height", "" ); contents.css( "height", "" );
} }
}, },
@ -229,12 +231,12 @@ $.widget( "ui.accordion", {
}, },
refresh: function() { refresh: function() {
var options = this.options, var heightStyle = this.options.heightStyle,
parent = this.element.parent(), parent = this.element.parent(),
maxHeight, maxHeight,
overflow; overflow;
if ( options.heightStyle === "fill" ) { if ( heightStyle === "fill" ) {
// IE 6 treats height like minHeight, so we need to turn off overflow // IE 6 treats height like minHeight, so we need to turn off overflow
// in order to get a reliable height // in order to get a reliable height
// we use the minHeight support test because we assume that only // we use the minHeight support test because we assume that only
@ -267,7 +269,7 @@ $.widget( "ui.accordion", {
$( this ).innerHeight() + $( this ).height() ) ); $( this ).innerHeight() + $( this ).height() ) );
}) })
.css( "overflow", "auto" ); .css( "overflow", "auto" );
} else if ( options.heightStyle === "auto" ) { } else if ( heightStyle === "auto" ) {
maxHeight = 0; maxHeight = 0;
this.headers.next() this.headers.next()
.each(function() { .each(function() {
@ -276,7 +278,9 @@ $.widget( "ui.accordion", {
.height( maxHeight ); .height( maxHeight );
} }
return this; if ( heightStyle !== "content" ) {
this.element.height( this.element.height() );
}
}, },
_activate: function( index ) { _activate: function( index ) {
@ -361,41 +365,20 @@ $.widget( "ui.accordion", {
}, },
_toggle: function( data ) { _toggle: function( data ) {
var that = this, var toShow = data.newContent,
options = this.options, toHide = this.prevShow.length ? this.prevShow : data.oldContent;
toShow = data.newContent,
toHide = data.oldContent;
function complete() { // handle activating a panel during the animation for another activation
that._completed( data ); this.prevShow.add( this.prevHide ).stop( true, true );
} this.prevShow = toShow;
this.prevHide = toHide;
if ( options.animated ) { if ( this.options.animate ) {
var animations = $.ui.accordion.animations, this._animate( toShow, toHide, data );
animation = options.animated,
additional;
if ( !animations[ animation ] ) {
additional = {
easing: $.easing[ animation ] ? animation : "slide",
duration: 700
};
animation = "slide";
}
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 );
} else { } else {
toHide.hide(); toHide.hide();
toShow.show(); toShow.show();
complete(); this._completed( data );
} }
// TODO assert that the blur and focus triggers are really necessary, remove otherwise // TODO assert that the blur and focus triggers are really necessary, remove otherwise
@ -415,17 +398,51 @@ $.widget( "ui.accordion", {
.focus(); .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 ) { _completed: function( data ) {
var toShow = data.newContent, var toShow = data.newContent,
toHide = data.oldContent; 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 // other classes are removed before the animation; this one needs to stay until completed
toHide.removeClass( "ui-accordion-content-active" ); toHide.removeClass( "ui-accordion-content-active" );
// Work around for rendering bug in IE (#5421) // Work around for rendering bug in IE (#5421)
@ -437,123 +454,19 @@ $.widget( "ui.accordion", {
} }
}); });
$.extend( $.ui.accordion, { $.fx.step.accordionHeight = function( fx ) {
animations: { var elem = $( fx.elem ),
slide: function( options, additions ) { data = elem.data( "accordionHeight" );
if ( options.prevShow || options.prevHide ) { elem.height( data.total - elem.outerHeight() - data.toHide.outerHeight() + elem.height() );
options.prevHide.stop( true, true ); };
options.toHide = options.prevShow; var hideProps = {},
} showProps = {},
showPropsAdjust = {};
var showOverflow = options.toShow.css( "overflow" ), hideProps.height = hideProps.paddingTop = hideProps.paddingBottom =
showOverflowX = options.toHide.css( "overflowX" ), hideProps.borderTopWidth = hideProps.borderBottomWidth = "hide";
showOverflowY = options.toHide.css( "overflowY" ), showProps.height = showProps.paddingTop = showProps.paddingBottom =
hideOverflow = options.toHide.css( "overflow" ), showProps.borderTopWidth = showProps.borderBottomWidth = "show";
hideOverflowX = options.toHide.css( "overflowX" ), $.extend( showPropsAdjust, showProps, { accordionHeight: "show" } );
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
});
}
}
});
@ -697,6 +610,40 @@ if ( $.uiBackCompat !== false ) {
return ret; return ret;
}; };
}( jQuery, jQuery.ui.accordion.prototype ) ); }( 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 ); })( jQuery );