This repository has been archived by the owner on Apr 20, 2023. It is now read-only.
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Beginning of CSS3 transitions implementation. Added parser for transi…
…tion property, implementations for cubic bezier and step timing functions, and basic color interpolation.
- Loading branch information
Showing
6 changed files
with
242 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
/** | ||
* Handles parsing, caching, and detecting changes to the 'transition' CSS property | ||
* @constructor | ||
* @param {Element} el the target element | ||
*/ | ||
PIE.TransitionStyleInfo = PIE.StyleInfoBase.newStyleInfo( { | ||
|
||
cssProperty: 'transition', | ||
styleProperty: 'transition', | ||
|
||
parseCss: function( css ) { | ||
var props, | ||
Type = PIE.Tokenizer.Type, | ||
tokenizer, token, tokenType, tokenValue, currentTransition, | ||
a, b, c, d; | ||
|
||
function tokenIsChar( token, ch ) { | ||
return token && token.tokenValue === ch; | ||
} | ||
|
||
function tokenToNumber( token ) { | ||
return ( token && ( token.tokenType & Type.NUMBER ) ) ? parseFloat( token.tokenValue, 10 ) : NaN; | ||
} | ||
|
||
function flushCurrentTransition() { | ||
// Fill in defaults | ||
if (!currentTransition.property) { | ||
currentTransition.property = 'all'; | ||
} | ||
if (!currentTransition.timingFunction) { | ||
currentTransition.timingFunction = 'ease'; | ||
} | ||
if (!currentTransition.delay) { | ||
currentTransition.delay = '0'; | ||
} | ||
props.push(currentTransition); | ||
currentTransition = 0; | ||
} | ||
|
||
if( css ) { | ||
tokenizer = new PIE.Tokenizer( css ); | ||
props = []; | ||
|
||
while( token = tokenizer.next() ) { | ||
tokenType = token.tokenType; | ||
tokenValue = token.tokenValue; | ||
if (!currentTransition) { | ||
currentTransition = { | ||
// property: 'all', | ||
// duration: '0ms', | ||
// timingFunction: <PIE.TransitionTimingFunction.*>, | ||
// delay: '0ms' | ||
}; | ||
} | ||
|
||
// Time values: first one is duration and second one is delay | ||
if (tokenType & Type.TIME && !currentTransition.duration) { | ||
currentTransition.duration = tokenValue; | ||
} | ||
else if (tokenType & Type.TIME && !currentTransition.delay) { | ||
currentTransition.delay = tokenValue; | ||
} | ||
|
||
// Timing function | ||
else if (tokenType & Type.IDENT && !currentTransition.timingFunction && tokenValue in PIE.TransitionTimingFunction.namedEasings) { | ||
currentTransition.timingFunction = PIE.TransitionTimingFunction.namedEasings[tokenValue]; | ||
} | ||
else if (tokenType & Type.FUNCTION && !currentTransition.timingFunction) { | ||
if ( tokenValue === 'step' && | ||
!isNaN( a = tokenToNumber( tokenizer.next() ) ) && a > 0 && Math.floor( a ) === a && | ||
tokenIsChar( tokenizer.next(), ',' ) && | ||
( b = tokenizer.next() ) && ( b.tokenType & Type.IDENT ) && ( b.tokenValue === 'start' || b.tokenValue === 'end' ) && | ||
tokenIsChar( tokenizer.next(), ')' ) | ||
) { | ||
currentTransition.timingFunction = PIE.TransitionTimingFunction.getStepsTimingFunction( a, b ); | ||
} | ||
else if (tokenValue === 'cubic-bezier' && | ||
!isNaN( a = tokenToNumber( tokenizer.next() ) ) && a >= 0 && a <= 1 && | ||
tokenIsChar( tokenizer.next(), ',' ) && | ||
!isNaN( b = tokenToNumber( tokenizer.next() ) ) && | ||
tokenIsChar( tokenizer.next(), ',' ) && | ||
!isNaN( c = tokenToNumber( tokenizer.next() ) ) && c >= 0 && c <= 1 && | ||
tokenIsChar( tokenizer.next(), ',' ) && | ||
!isNaN( d = tokenToNumber( tokenizer.next() ) ) && | ||
tokenIsChar( tokenizer.next(), ')' ) | ||
) { | ||
currentTransition.timingFunction = PIE.TransitionTimingFunction.getCubicBezierTimingFunction( a, b, c, d ); | ||
} | ||
else { | ||
return null; | ||
} | ||
} | ||
|
||
// Property name | ||
else if (tokenType & Type.IDENT && !currentTransition.property) { | ||
currentTransition.property = tokenValue; | ||
} | ||
|
||
// Comma: start a new value. Only duration is required so fail if it wasn't found. | ||
else if (tokenType & Type.OPERATOR && tokenValue === ',' && currentTransition.duration) { | ||
flushCurrentTransition(); | ||
} | ||
|
||
// Something unrecognized - fail! | ||
else { | ||
return null; | ||
} | ||
} | ||
|
||
// leftovers | ||
if( currentTransition && currentTransition.duration ) { | ||
flushCurrentTransition(); | ||
} | ||
|
||
} | ||
|
||
return props && props.length ? props : null; | ||
} | ||
} ); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
PIE.TransitionTimingFunction = { | ||
|
||
/** | ||
* Cubic bezier easing utility by Gaëtan Renaudeau, originally "KeySpline.js", used with | ||
* permission of the author. From the writeup at: | ||
* http://blog.greweb.fr/2012/02/bezier-curve-based-easing-functions-from-concept-to-implementation/ | ||
* | ||
* Modified to be a factory that returns a Function, and to make the internal utility | ||
* functions shared across instances. | ||
* | ||
* @param {Number} x1 | ||
* @param {Number} y1 | ||
* @param {Number} x2 | ||
* @param {Number} y2 | ||
* @return Function | ||
*/ | ||
getCubicBezierTimingFunction: (function() { | ||
function A(aA1, aA2) { return 1.0 - 3.0 * aA2 + 3.0 * aA1; } | ||
function B(aA1, aA2) { return 3.0 * aA2 - 6.0 * aA1; } | ||
function C(aA1) { return 3.0 * aA1; } | ||
|
||
// Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2. | ||
function CalcBezier(aT, aA1, aA2) { | ||
return ((A(aA1, aA2)*aT + B(aA1, aA2))*aT + C(aA1))*aT; | ||
} | ||
|
||
// Returns dx/dt given t, x1, and x2, or dy/dt given t, y1, and y2. | ||
function GetSlope(aT, aA1, aA2) { | ||
return 3.0 * A(aA1, aA2)*aT*aT + 2.0 * B(aA1, aA2) * aT + C(aA1); | ||
} | ||
|
||
function GetTForX(aX, mX1, mX2) { | ||
// Newton raphson iteration | ||
var aGuessT = aX, | ||
i = 0, | ||
currentSlope, currentX; | ||
for (; i < 4; ++i) { | ||
currentSlope = GetSlope(aGuessT, mX1, mX2); | ||
if (currentSlope == 0.0) return aGuessT; | ||
currentX = CalcBezier(aGuessT, mX1, mX2) - aX; | ||
aGuessT -= currentX / currentSlope; | ||
} | ||
return aGuessT; | ||
} | ||
|
||
return function(mX1, mY1, mX2, mY2) { | ||
return function(time) { | ||
if (mX1 == mY1 && mX2 == mY2) return time; // linear | ||
return CalcBezier(GetTForX(time, mX1, mX2), mY1, mY2); | ||
}; | ||
} | ||
})(), | ||
|
||
/** | ||
* Stepping function - see http://www.w3.org/TR/css3-transitions/#transition-timing-function-property | ||
* @param numSteps The number of steps in the transition | ||
* @param changeAt Whether the step up occurs at the 'start' or 'end' of each step interval. Defaults to 'end'. | ||
* @return Function | ||
*/ | ||
getStepsTimingFunction: function(numSteps, changeAt) { | ||
return function(time) { | ||
return Math[ changeAt === 'start' ? 'ceil' : 'floor' ]( time * numSteps ) / numSteps; | ||
}; | ||
} | ||
}; | ||
|
||
|
||
PIE.TransitionTimingFunction.namedEasings = { | ||
'ease': PIE.TransitionTimingFunction.getCubicBezierTimingFunction( 0.25, 0.1, 0.25, 1 ), | ||
'linear': PIE.TransitionTimingFunction.getCubicBezierTimingFunction( 0, 0, 1, 1 ), | ||
'ease-in': PIE.TransitionTimingFunction.getCubicBezierTimingFunction( 0.42, 0, 1, 1 ), | ||
'ease-out': PIE.TransitionTimingFunction.getCubicBezierTimingFunction( 0, 0, 0.58, 1 ), | ||
'ease-in-out': PIE.TransitionTimingFunction.getCubicBezierTimingFunction( 0.42, 0, 0.58, 1 ), | ||
'step-start': PIE.TransitionTimingFunction.getStepsTimingFunction( 1, 'start' ), | ||
'step-end': PIE.TransitionTimingFunction.getStepsTimingFunction( 1, 'end' ) | ||
}; | ||
|
||
|