Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

We’re showing branches in this repository, but you can also compare across forks.

base fork: lojjic/PIE
...
head fork: lojjic/PIE
  • 1 commit
  • 6 files changed
  • 0 commit comments
  • 1 contributor
Commits on May 07, 2012
Jason Johnston Beginning of CSS3 transitions implementation. Added parser for transi…
…tion property, implementations for cubic bezier and step timing functions, and basic color interpolation.
59a89ed
4 build.xml
View
@@ -32,6 +32,7 @@
<fileset file="${src_dir}/BgSize.js" />
<fileset file="${src_dir}/Angle.js" />
<fileset file="${src_dir}/Color.js" />
+ <fileset file="${src_dir}/TransitionTimingFunction.js" />
<fileset file="${src_dir}/Tokenizer.js" />
<fileset file="${src_dir}/BoundsInfo.js" />
<fileset file="${src_dir}/StyleInfoBase.js" />
@@ -41,6 +42,7 @@
<fileset file="${src_dir}/BorderImageStyleInfo.js" />
<fileset file="${src_dir}/BoxShadowStyleInfo.js" />
<fileset file="${src_dir}/PaddingStyleInfo.js" />
+ <fileset file="${src_dir}/TransitionStyleInfo.js" />
<fileset file="${src_dir}/VisibilityStyleInfo.js" />
<fileset file="${src_dir}/VmlShape.js" />
<fileset file="${src_dir}/RendererBase.js" />
@@ -79,6 +81,7 @@
<fileset file="${src_dir}/BgSize.js" />
<fileset file="${src_dir}/Angle.js" />
<fileset file="${src_dir}/Color.js" />
+ <fileset file="${src_dir}/TransitionTimingFunction.js" />
<fileset file="${src_dir}/Tokenizer.js" />
<fileset file="${src_dir}/BoundsInfo.js" />
<fileset file="${src_dir}/StyleInfoBase.js" />
@@ -86,6 +89,7 @@
<fileset file="${src_dir}/BorderStyleInfo.js" />
<fileset file="${src_dir}/BorderImageStyleInfo.js" />
<fileset file="${src_dir}/PaddingStyleInfo.js" />
+ <fileset file="${src_dir}/TransitionStyleInfo.js" />
<fileset file="${src_dir}/RendererBase.js" />
<fileset file="${src_dir}/IE9RootRenderer.js" />
<fileset file="${src_dir}/IE9BackgroundRenderer.js" />
31 sources/Color.js
View
@@ -189,6 +189,37 @@ PIE.Color = (function() {
alpha: function() {
this.parse();
return this._alpha;
+ },
+
+
+ /**
+ * Given another target color, calculate and return an intermediate color at a given
+ * percentage between the two.
+ * @param {PIE.Color} toColor The other color being transitioned to
+ * @param {number} percent The percent (number from 0 to 1) between the two colors to calculate
+ * @param {Element} el
+ * @return {PIE.Color} The interpolated color
+ */
+ interpolate: function( toColor, percent, el ) {
+ var hex1 = this.hexValue( el ),
+ hex2 = toColor.hexValue( el ),
+ parse = parseInt,
+ r1 = parse( hex1.substr(1, 2), 16 ),
+ g1 = parse( hex1.substr(3, 2), 16 ),
+ b1 = parse( hex1.substr(5, 2), 16 ),
+ r2 = parse( hex2.substr(1, 2), 16 ),
+ g2 = parse( hex2.substr(3, 2), 16 ),
+ b2 = parse( hex2.substr(5, 2), 16 ),
+ a1 = this.alpha(),
+ a2 = toColor.alpha(),
+ newColor = new Color();
+
+ // Set the internal color/alpha cached values directly so it doesn't have to re-parse
+ newColor._alpha = ( a2 - a1 ) * percent + a1;
+ newColor._color = '#' + ( ( r2 - r1 ) * percent + r1 ).toString( 16 ) +
+ ( ( g2 - g1 ) * percent + g1 ).toString( 16 ) +
+ ( ( b2 - b1 ) * percent + b1 ).toString( 16 );
+ return newColor;
}
};
8 sources/Element.js
View
@@ -112,13 +112,15 @@ PIE.Element = (function() {
backgroundInfo: new PIE.BackgroundStyleInfo( el ),
borderImageInfo: new PIE.BorderImageStyleInfo( el ),
borderInfo: new PIE.BorderStyleInfo( el ),
- paddingInfo: new PIE.PaddingStyleInfo( el )
+ paddingInfo: new PIE.PaddingStyleInfo( el ),
+ transitionInfo: new PIE.TransitionStyleInfo( el )
};
styleInfosArr = [
styleInfos.backgroundInfo,
styleInfos.borderInfo,
styleInfos.borderImageInfo,
- styleInfos.paddingInfo
+ styleInfos.paddingInfo,
+ styleInfos.transitionInfo
];
rootRenderer = new PIE.IE9RootRenderer( el, boundsInfo, styleInfos );
childRenderers = [
@@ -134,6 +136,7 @@ PIE.Element = (function() {
borderRadiusInfo: new PIE.BorderRadiusStyleInfo( el ),
boxShadowInfo: new PIE.BoxShadowStyleInfo( el ),
paddingInfo: new PIE.PaddingStyleInfo( el ),
+ transitionInfo: new PIE.TransitionStyleInfo( el ),
visibilityInfo: new PIE.VisibilityStyleInfo( el )
};
styleInfosArr = [
@@ -143,6 +146,7 @@ PIE.Element = (function() {
styleInfos.borderRadiusInfo,
styleInfos.boxShadowInfo,
styleInfos.paddingInfo,
+ styleInfos.transitionInfo,
styleInfos.visibilityInfo
];
rootRenderer = new PIE.RootRenderer( el, boundsInfo, styleInfos );
6 sources/Tokenizer.js
View
@@ -27,7 +27,8 @@ PIE.Tokenizer = (function() {
OPERATOR: 256,
PERCENT: 512,
STRING: 1024,
- URL: 2048
+ URL: 2048,
+ TIME: 4096
};
/**
@@ -63,7 +64,8 @@ PIE.Tokenizer = (function() {
'px': Type.LENGTH, 'em': Type.LENGTH, 'ex': Type.LENGTH,
'mm': Type.LENGTH, 'cm': Type.LENGTH, 'in': Type.LENGTH,
'pt': Type.LENGTH, 'pc': Type.LENGTH,
- 'deg': Type.ANGLE, 'rad': Type.ANGLE, 'grad': Type.ANGLE
+ 'deg': Type.ANGLE, 'rad': Type.ANGLE, 'grad': Type.ANGLE,
+ 's': Type.TIME, 'ms': Type.TIME
},
colorFunctions: {
119 sources/TransitionStyleInfo.js
View
@@ -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;
+ }
+} );
78 sources/TransitionTimingFunction.js
View
@@ -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' )
+};
+
+

No commit comments for this range

Something went wrong with that request. Please try again.