Fix #13855: line-height animations. Close gh-1265.

This commit is contained in:
Richard Gibson 2013-05-08 22:56:51 -04:00
parent c1b8edfcc9
commit 3971c2ebb2
3 changed files with 100 additions and 57 deletions

View File

@ -164,7 +164,7 @@ jQuery.extend({
} }
}, },
// Exclude the following css properties to add px // Don't automatically add "px" to these possibly-unitless properties
cssNumber: { cssNumber: {
"columnCount": true, "columnCount": true,
"fillOpacity": true, "fillOpacity": true,

117
src/effects.js vendored
View File

@ -5,44 +5,51 @@ var fxNow, timerId,
animationPrefilters = [ defaultPrefilter ], animationPrefilters = [ defaultPrefilter ],
tweeners = { tweeners = {
"*": [function( prop, value ) { "*": [function( prop, value ) {
var end, unit, var tween = this.createTween( prop, value ),
tween = this.createTween( prop, value ),
parts = rfxnum.exec( value ),
target = tween.cur(), target = tween.cur(),
start = +target || 0, parts = rfxnum.exec( value ),
unit = parts && parts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ),
// Starting value computation is required for potential unit mismatches
start = ( jQuery.cssNumber[ prop ] || unit !== "px" && +target ) &&
rfxnum.exec( jQuery.css( tween.elem, prop ) ),
scale = 1, scale = 1,
maxIterations = 20; maxIterations = 20;
if ( parts ) { if ( start && start[ 3 ] !== unit ) {
end = +parts[2]; // Trust units reported by jQuery.css
unit = parts[3] || ( jQuery.cssNumber[ prop ] ? "" : "px" ); unit = unit || start[ 3 ];
// We need to compute starting value // Make sure we update the tween properties later on
if ( unit !== "px" && start ) { parts = parts || [];
// Iteratively approximate from a nonzero starting point
// Prefer the current property, because this process will be trivial if it uses the same units
// Fallback to end or a simple constant
start = jQuery.css( tween.elem, prop, true ) || end || 1;
do { // Iteratively approximate from a nonzero starting point
// If previous iteration zeroed out, double until we get *something* start = +target || 1;
// Use a string for doubling factor so we don't accidentally see scale as unchanged below
scale = scale || ".5";
// Adjust and apply do {
start = start / scale; // If previous iteration zeroed out, double until we get *something*
jQuery.style( tween.elem, prop, start + unit ); // Use a string for doubling factor so we don't accidentally see scale as unchanged below
scale = scale || ".5";
// Update scale, tolerating zero or NaN from tween.cur() // Adjust and apply
// And breaking the loop if scale is unchanged or perfect, or if we've just had enough start = start / scale;
} while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations ); jQuery.style( tween.elem, prop, start + unit );
}
tween.unit = unit; // Update scale, tolerating zero or NaN from tween.cur()
tween.start = start; // And breaking the loop if scale is unchanged or perfect, or if we've just had enough
// If a +=/-= token was provided, we're doing a relative animation } while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations );
tween.end = parts[1] ? start + ( parts[1] + 1 ) * end : end;
} }
// Update tween properties
if ( parts ) {
tween.unit = unit;
tween.start = +start || +target || 0;
// If a +=/-= token was provided, we're doing a relative animation
tween.end = parts[ 1 ] ?
start + ( parts[ 1 ] + 1 ) * parts[ 2 ] :
+parts[ 2 ];
}
return tween; return tween;
}] }]
}; };
@ -55,19 +62,18 @@ function createFxNow() {
return ( fxNow = jQuery.now() ); return ( fxNow = jQuery.now() );
} }
function createTweens( animation, props ) { function createTween( value, prop, animation ) {
jQuery.each( props, function( prop, value ) { var tween,
var collection = ( tweeners[ prop ] || [] ).concat( tweeners[ "*" ] ), collection = ( tweeners[ prop ] || [] ).concat( tweeners[ "*" ] ),
index = 0, index = 0,
length = collection.length; length = collection.length;
for ( ; index < length; index++ ) { for ( ; index < length; index++ ) {
if ( collection[ index ].call( animation, prop, value ) ) { if ( (tween = collection[ index ].call( animation, prop, value )) ) {
// we're done with this property // we're done with this property
return; return tween;
}
} }
}); }
} }
function Animation( elem, properties, options ) { function Animation( elem, properties, options ) {
@ -153,7 +159,7 @@ function Animation( elem, properties, options ) {
} }
} }
createTweens( animation, props ); jQuery.map( props, createTween, animation );
if ( jQuery.isFunction( animation.opts.start ) ) { if ( jQuery.isFunction( animation.opts.start ) ) {
animation.opts.start.call( elem, animation ); animation.opts.start.call( elem, animation );
@ -243,12 +249,12 @@ jQuery.Animation = jQuery.extend( Animation, {
function defaultPrefilter( elem, props, opts ) { function defaultPrefilter( elem, props, opts ) {
/* jshint validthis: true */ /* jshint validthis: true */
var index, prop, value, length, dataShow, toggle, tween, hooks, oldfire, var index, prop, value, toggle, tween, hooks, oldfire,
anim = this, anim = this,
style = elem.style,
orig = {}, orig = {},
handled = [], style = elem.style,
hidden = elem.nodeType && isHidden( elem ); hidden = elem.nodeType && isHidden( elem ),
dataShow = data_priv.get( elem, "fxshow" );
// handle queue: false promises // handle queue: false promises
if ( !opts.queue ) { if ( !opts.queue ) {
@ -304,7 +310,6 @@ function defaultPrefilter( elem, props, opts ) {
// show/hide pass // show/hide pass
dataShow = data_priv.get( elem, "fxshow" );
for ( index in props ) { for ( index in props ) {
value = props[ index ]; value = props[ index ];
if ( rfxtypes.exec( value ) ) { if ( rfxtypes.exec( value ) ) {
@ -313,21 +318,23 @@ function defaultPrefilter( elem, props, opts ) {
if ( value === ( hidden ? "hide" : "show" ) ) { if ( value === ( hidden ? "hide" : "show" ) ) {
// If there is dataShow left over from a stopped hide or show and we are going to proceed with show, we should pretend to be hidden // If there is dataShow left over from a stopped hide or show and we are going to proceed with show, we should pretend to be hidden
if( value === "show" && dataShow !== undefined && dataShow[ index ] !== undefined ) { if ( value === "show" && dataShow && dataShow[ index ] !== undefined ) {
hidden = true; hidden = true;
} else { } else {
continue; continue;
} }
} }
handled.push( index ); orig[ index ] = dataShow && dataShow[ index ] || jQuery.style( elem, index );
} }
} }
length = handled.length; if ( !jQuery.isEmptyObject( orig ) ) {
if ( length ) { if ( dataShow ) {
dataShow = data_priv.get( elem, "fxshow" ) || data_priv.access( elem, "fxshow", {} ); if ( "hidden" in dataShow ) {
if ( "hidden" in dataShow ) { hidden = dataShow.hidden;
hidden = dataShow.hidden; }
} else {
dataShow = data_priv.access( elem, "fxshow", {} );
} }
// store state if its toggle - enables .stop().toggle() to "reverse" // store state if its toggle - enables .stop().toggle() to "reverse"
@ -349,10 +356,8 @@ function defaultPrefilter( elem, props, opts ) {
jQuery.style( elem, prop, orig[ prop ] ); jQuery.style( elem, prop, orig[ prop ] );
} }
}); });
for ( index = 0 ; index < length ; index++ ) { for ( prop in orig ) {
prop = handled[ index ]; tween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim );
tween = anim.createTween( prop, hidden ? dataShow[ prop ] : 0 );
orig[ prop ] = dataShow[ prop ] || jQuery.style( elem, prop );
if ( !( prop in dataShow ) ) { if ( !( prop in dataShow ) ) {
dataShow[ prop ] = tween.start; dataShow[ prop ] = tween.start;

38
test/unit/effects.js vendored
View File

@ -1371,6 +1371,44 @@ test("Do not append px to 'fill-opacity' #9548", 1, function() {
}); });
}); });
test("line-height animates correctly (#13855)", function() {
expect( 12 );
stop();
var
animated = jQuery(
"<p style='line-height: 4;'>unitless</p>" +
"<p style='line-height: 50px;'>px</p>" +
"<p style='line-height: 120%;'>percent</p>" +
"<p style='line-height: 1.5em;'>em</p>"
).appendTo("#qunit-fixture"),
initialHeight = jQuery.map( animated, function( el ) {
return jQuery( el ).height();
});
animated.animate( { "line-height": "hide" }, 1500 );
setTimeout(function() {
animated.each(function( i ) {
var label = jQuery.text( this ),
initial = initialHeight[ i ],
height = jQuery( this ).height();
ok( height < initial, "hide " + label + ": upper bound" );
ok( height > initial / 2, "hide " + label + ": lower bound" );
});
animated.stop( true, true ).hide().animate( { "line-height": "show" }, 1500 );
setTimeout(function() {
animated.each(function( i ) {
var label = jQuery.text( this ),
initial = initialHeight[ i ],
height = jQuery( this ).height();
ok( height < initial / 2, "show " + label + ": upper bound" );
});
animated.stop( true, true );
start();
}, 400 );
}, 400 );
});
// Start 1.8 Animation tests // Start 1.8 Animation tests
asyncTest( "jQuery.Animation( object, props, opts )", 4, function() { asyncTest( "jQuery.Animation( object, props, opts )", 4, function() {
var animation, var animation,