Skip to content

Commit

Permalink
Fix #13855: line-height animations. Close gh-1265.
Browse files Browse the repository at this point in the history
  • Loading branch information
gibson042 committed May 13, 2013
1 parent c1b8edf commit 3971c2e
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 59 deletions.
2 changes: 1 addition & 1 deletion src/css.js
Expand Up @@ -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,
Expand Down
121 changes: 63 additions & 58 deletions src/effects.js
Expand Up @@ -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" );

// 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;

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";

// Adjust and apply
start = start / scale;
jQuery.style( tween.elem, prop, start + unit );

// 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 );
}
if ( start && start[ 3 ] !== unit ) {
// Trust units reported by jQuery.css
unit = unit || start[ 3 ];

// Make sure we update the tween properties later on
parts = parts || [];

// Iteratively approximate from a nonzero starting point
start = +target || 1;

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";

// Adjust and apply
start = start / scale;
jQuery.style( tween.elem, prop, start + unit );

// 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;
tween.start = +start || +target || 0;
// If a +=/-= token was provided, we're doing a relative animation
tween.end = parts[1] ? start + ( parts[1] + 1 ) * end : end;
tween.end = parts[ 1 ] ?
start + ( parts[ 1 ] + 1 ) * parts[ 2 ] :
+parts[ 2 ];
}

return tween;
}]
};
Expand All @@ -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 ) {
Expand Down Expand Up @@ -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 );
Expand Down Expand Up @@ -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 ) {
Expand Down Expand Up @@ -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 ) ) {
Expand All @@ -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"
Expand All @@ -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;
Expand Down
38 changes: 38 additions & 0 deletions test/unit/effects.js
Expand Up @@ -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,
Expand Down

0 comments on commit 3971c2e

Please sign in to comment.