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: {
"columnCount": true,
"fillOpacity": true,

117
src/effects.js vendored
View File

@ -5,44 +5,51 @@ var fxNow, timerId,
animationPrefilters = [ defaultPrefilter ],
tweeners = {
"*": [function( prop, value ) {
var end, unit,
tween = this.createTween( prop, value ),
parts = rfxnum.exec( value ),
var tween = this.createTween( prop, value ),
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,
maxIterations = 20;
if ( parts ) {
end = +parts[2];
unit = parts[3] || ( jQuery.cssNumber[ prop ] ? "" : "px" );
if ( start && start[ 3 ] !== unit ) {
// Trust units reported by jQuery.css
unit = unit || start[ 3 ];
// We need to compute starting value
if ( unit !== "px" && start ) {
// 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;
// Make sure we update the tween properties later on
parts = parts || [];
do {
// If previous iteration zeroed out, double until we get *something*
// Use a string for doubling factor so we don't accidentally see scale as unchanged below
scale = scale || ".5";
// Iteratively approximate from a nonzero starting point
start = +target || 1;
// Adjust and apply
start = start / scale;
jQuery.style( tween.elem, prop, start + unit );
do {
// If previous iteration zeroed out, double until we get *something*
// 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()
// And breaking the loop if scale is unchanged or perfect, or if we've just had enough
} while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations );
}
// Adjust and apply
start = start / scale;
jQuery.style( tween.elem, prop, start + unit );
tween.unit = unit;
tween.start = start;
// If a +=/-= token was provided, we're doing a relative animation
tween.end = parts[1] ? start + ( parts[1] + 1 ) * end : end;
// Update scale, tolerating zero or NaN from tween.cur()
// And breaking the loop if scale is unchanged or perfect, or if we've just had enough
} while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations );
}
// 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;
}]
};
@ -55,19 +62,18 @@ function createFxNow() {
return ( fxNow = jQuery.now() );
}
function createTweens( animation, props ) {
jQuery.each( props, function( prop, value ) {
var collection = ( tweeners[ prop ] || [] ).concat( tweeners[ "*" ] ),
index = 0,
length = collection.length;
for ( ; index < length; index++ ) {
if ( collection[ index ].call( animation, prop, value ) ) {
function createTween( value, prop, animation ) {
var tween,
collection = ( tweeners[ prop ] || [] ).concat( tweeners[ "*" ] ),
index = 0,
length = collection.length;
for ( ; index < length; index++ ) {
if ( (tween = collection[ index ].call( animation, prop, value )) ) {
// we're done with this property
return;
}
// we're done with this property
return tween;
}
});
}
}
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 ) ) {
animation.opts.start.call( elem, animation );
@ -243,12 +249,12 @@ jQuery.Animation = jQuery.extend( Animation, {
function defaultPrefilter( elem, props, opts ) {
/* jshint validthis: true */
var index, prop, value, length, dataShow, toggle, tween, hooks, oldfire,
var index, prop, value, toggle, tween, hooks, oldfire,
anim = this,
style = elem.style,
orig = {},
handled = [],
hidden = elem.nodeType && isHidden( elem );
style = elem.style,
hidden = elem.nodeType && isHidden( elem ),
dataShow = data_priv.get( elem, "fxshow" );
// handle queue: false promises
if ( !opts.queue ) {
@ -304,7 +310,6 @@ function defaultPrefilter( elem, props, opts ) {
// show/hide pass
dataShow = data_priv.get( elem, "fxshow" );
for ( index in props ) {
value = props[ index ];
if ( rfxtypes.exec( value ) ) {
@ -313,21 +318,23 @@ function defaultPrefilter( elem, props, opts ) {
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( value === "show" && dataShow !== undefined && dataShow[ index ] !== undefined ) {
if ( value === "show" && dataShow && dataShow[ index ] !== undefined ) {
hidden = true;
} else {
continue;
}
}
handled.push( index );
orig[ index ] = dataShow && dataShow[ index ] || jQuery.style( elem, index );
}
}
length = handled.length;
if ( length ) {
dataShow = data_priv.get( elem, "fxshow" ) || data_priv.access( elem, "fxshow", {} );
if ( "hidden" in dataShow ) {
hidden = dataShow.hidden;
if ( !jQuery.isEmptyObject( orig ) ) {
if ( dataShow ) {
if ( "hidden" in dataShow ) {
hidden = dataShow.hidden;
}
} else {
dataShow = data_priv.access( elem, "fxshow", {} );
}
// 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 ] );
}
});
for ( index = 0 ; index < length ; index++ ) {
prop = handled[ index ];
tween = anim.createTween( prop, hidden ? dataShow[ prop ] : 0 );
orig[ prop ] = dataShow[ prop ] || jQuery.style( elem, prop );
for ( prop in orig ) {
tween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim );
if ( !( prop in dataShow ) ) {
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
asyncTest( "jQuery.Animation( object, props, opts )", 4, function() {
var animation,