diff --git a/ionic/animations/animation.js b/ionic/animations/animation.js index 87f9779698..f46be951c1 100644 --- a/ionic/animations/animation.js +++ b/ionic/animations/animation.js @@ -294,21 +294,25 @@ class Animate { // however, element.animate() seems locked in and uses the latest // and correct API methods under the hood, so really doesn't matter - // fromEffect must be manually computed if it wasn't provided - // https://github.com/web-animations/web-animations-js/issues/14 - fromEffect = fromEffect || {}; - let style = null; - for (let prop in toEffect) { - if (util.isBlank(fromEffect[prop])) { - style = style || window.getComputedStyle(ele); - fromEffect[prop] = style[prop]; + fromEffect = parseEffect(fromEffect); + toEffect = parseEffect(toEffect); + + this._duration = duration; + + var effects; + + if (easing in EASING_FN) { + effects = createEasingEffects(fromEffect, toEffect, easing); + + } else { + effects = [ convertProperties(fromEffect), convertProperties(toEffect) ]; + + if (easing in CUBIC_BEZIERS) { + easing = 'cubic-bezier(' + CUBIC_BEZIERS[easing] + ')'; } } - this._duration = duration; - this._easing = easing; - - this.player = ele.animate([fromEffect, toEffect], { + this.player = ele.animate(effects, { duration: duration, easing: easing, playbackRate: playbackRate || 1 @@ -358,3 +362,236 @@ class Animate { } } + +function roundValue(val) { + return Math.round(val * 10000) / 10000; +} + +function convertProperties(inputEffect) { + var outputEffect = {}; + var transforms = []; + + for (var property in inputEffect) { + var value = inputEffect[property].value; + + if (TRANSFORMS.indexOf(property) > -1) { + transforms.push(property + '(' + value + ')'); + } else { + outputEffect[property] = value; + } + } + + if (transforms.length) { + outputEffect.transform = transforms.join(' '); + } + + return outputEffect; +} + +function createEasingEffects(fromEffect, toEffect, easing) { + var inputEffects = buildEffects(fromEffect, toEffect, easing); + var outputEffects = []; + + inputEffects.forEach(function(effect) { + outputEffects.push( convertProperties(effect) ); + }); + + return outputEffects; +} + +function buildEffects(fromEffect, toEffect, easing) { + var increment = 0.04; + + var outputEffects = [fromEffect]; + var easingFn = EASING_FN[easing]; + + for(var pos = increment; pos <= (1 - increment); pos += increment) { + + var tweenEffect = {}; + var addEffect = false; + + for (var property in toEffect) { + var toProperty = toEffect[property]; + if (toProperty.tween) { + + var fromValue = fromEffect[property].num + var diffValue = toProperty.num - fromValue; + + tweenEffect[property] = { + value: roundValue( (easingFn(pos) * diffValue) + fromValue ) + toProperty.unit + }; + + addEffect = true; + } + } + + if (addEffect) { + outputEffects.push(tweenEffect); + } + + } + + outputEffects.push(toEffect); + + return outputEffects; +} + +function parseEffect(inputEffect) { + var val, r, num, property; + var outputEffect = {}; + + for (property in inputEffect) { + val = inputEffect[property]; + r = val.toString().match(/(\d*\.?\d*)(.*)/); + num = parseFloat(r[1]); + + outputEffect[property] = { + value: val, + num: num, + unit: (r[0] != r[2] ? r[2] : ''), + tween: !isNaN(num) && (ANIMATE_PROPERTIES.indexOf(property) > -1) + } + } + + return outputEffect; +} + + +const TRANSFORMS = ['translateX', 'translateY', 'translateZ', 'scale', 'scaleX', 'scaleY', 'scaleZ', + 'rotate', 'rotateX', 'rotateY', 'rotateZ', 'skewX', 'skewY', 'perspective']; + +const ANIMATE_PROPERTIES = TRANSFORMS.concat('opacity'); + + +// Default easings built into the browsers +const BUILTIN_EASING = ['linear', 'ease', 'ease-in', 'ease-out', 'ease-in-out']; + + +// Robert Penner's Easing Functions +// http://robertpenner.com/easing/ + +const CUBIC_BEZIERS = { + // Cubic + easeInCubic: '0.55,0.055,0.675,0.19', + easeOutCubic: '0.215,0.61,0.355,1', + easeInOutCubic: '0.645,0.045,0.355,1', + + // Circ + easeInCirc: '0.6,0.04,0.98,0.335', + easeOutCirc: '0.075,0.82,0.165,1', + easeInOutCirc: '0.785,0.135,0.15,0.86', + + // Expo + easeInExpo: '0.95,0.05,0.795,0.035', + easeOutExpo: '0.19,1,0.22,1', + easeInOutExpo: '1,0,0,1', + + // Quad + easeInQuad: '0.55,0.085,0.68,0.53', + easeOutQuad: '0.25,0.46,0.45,0.94', + easeInOutQuad: '0.455,0.03,0.515,0.955', + + // Quart + easeInQuart: '0.895,0.03,0.685,0.22', + easeOutQuart: '0.165,0.84,0.44,1', + easeInOutQuart: '0.77,0,0.175,1', + + // Quint + easeInQuint: '0.755,0.05,0.855,0.06', + easeOutQuint: '0.23,1,0.32,1', + easeInOutQuint: '0.86,0,0.07,1', + + // Sine + easeInSine: '0.47,0,0.745,0.715', + easeOutSine: '0.39,0.575,0.565,1', + easeInOutSine : '0.445,0.05,0.55,0.95', + + // Back + easeInBack: '0.6,-0.28,0.735,0.045', + easeOutBack: '0.175, 0.885,0.32,1.275', + easeInOutBack: '0.68,-0.55,0.265,1.55', +}; + + +const EASING_FN = { + + easeOutBounce: function(pos) { + if ((pos) < (1/2.75)) { + return (7.5625*pos*pos); + } else if (pos < (2/2.75)) { + return (7.5625*(pos-=(1.5/2.75))*pos + .75); + } else if (pos < (2.5/2.75)) { + return (7.5625*(pos-=(2.25/2.75))*pos + .9375); + } + return (7.5625*(pos-=(2.625/2.75))*pos + .984375); + }, + + elastic: function(pos) { + return -1 * Math.pow(4,-8*pos) * Math.sin((pos*6-1)*(2*Math.PI)/2) + 1; + }, + + swingFromTo: function(pos) { + var s = 1.70158; + return ((pos/=0.5) < 1) ? 0.5*(pos*pos*(((s*=(1.525))+1)*pos - s)) : + 0.5*((pos-=2)*pos*(((s*=(1.525))+1)*pos + s) + 2); + }, + + swingFrom: function(pos) { + var s = 1.70158; + return pos*pos*((s+1)*pos - s); + }, + + swingTo: function(pos) { + var s = 1.70158; + return (pos-=1)*pos*((s+1)*pos + s) + 1; + }, + + bounce: function(pos) { + if (pos < (1/2.75)) { + return (7.5625*pos*pos); + } else if (pos < (2/2.75)) { + return (7.5625*(pos-=(1.5/2.75))*pos + .75); + } else if (pos < (2.5/2.75)) { + return (7.5625*(pos-=(2.25/2.75))*pos + .9375); + } + return (7.5625*(pos-=(2.625/2.75))*pos + .984375); + }, + + bouncePast: function(pos) { + if (pos < (1/2.75)) { + return (7.5625*pos*pos); + } else if (pos < (2/2.75)) { + return 2 - (7.5625*(pos-=(1.5/2.75))*pos + .75); + } else if (pos < (2.5/2.75)) { + return 2 - (7.5625*(pos-=(2.25/2.75))*pos + .9375); + } + return 2 - (7.5625*(pos-=(2.625/2.75))*pos + .984375); + }, + + easeFromTo: function(pos) { + if ((pos/=0.5) < 1) return 0.5*Math.pow(pos,4); + return -0.5 * ((pos-=2)*Math.pow(pos,3) - 2); + }, + + easeFrom: function(pos) { + return Math.pow(pos, 4) + }, + + easeTo: function(pos) { + return Math.pow(pos, 0.25) + }, + + /* + * scripty2, Thomas Fuchs (MIT Licence) + * https://raw.github.com/madrobby/scripty2/master/src/effects/transitions/transitions.js + */ + spring: function(pos) { + return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6)); + }, + + sinusoidal: function(pos) { + return (-Math.cos(pos*Math.PI)/2) + 0.5; + } + +}; + diff --git a/ionic/components.js b/ionic/components.js index fcf2a4bd55..859a6e5087 100644 --- a/ionic/components.js +++ b/ionic/components.js @@ -22,6 +22,6 @@ export * from 'ionic/components/radio/radio' // export * from 'ionic/components/split-view/split-view' export * from 'ionic/components/segment/segment' export * from 'ionic/components/switch/switch' -export * from 'ionic/components/tabs/tabs' -export * from 'ionic/components/tabs/tab' +//export * from 'ionic/components/tabs/tabs' +//export * from 'ionic/components/tabs/tab' export * from 'ionic/components/toolbar/toolbar' diff --git a/ionic/components/app/test/animations/index.js b/ionic/components/app/test/animations/index.js index 881c28af0c..7e4c25f512 100644 --- a/ionic/components/app/test/animations/index.js +++ b/ionic/components/app/test/animations/index.js @@ -19,21 +19,31 @@ class IonicApp { this.animation = new Animation(); this.animation - .duration(1000) - .easing('ease-in-out'); + .duration(2000) + .easing('spring'); + + + var ball = new Animation( document.querySelector('.ball') ); + ball + .from('translateX', '0px') + .to('translateX', '250px') + + this.animation.addChild(ball); var row1 = new Animation( document.querySelectorAll('.square') ); row1 - .from('opacity', 1) - .to('opacity', 0) - .to('transform', 'scale(0)') - .beforePlay.addClass('added-before-play') - .afterFinish.addClass('added-after-finish') + .from('opacity', 0.8) + .to('opacity', 0.2) + + this.animation.addChild(row1); var row2 = new Animation( document.querySelectorAll('.square2') ); row2 - .to('transform', 'rotate(90deg) scale(0.5)') + .from('rotate', '0deg') + .from('scale', '1') + .to('rotate', '90deg') + .to('scale', '0.5') .beforePlay.addClass('added-before-play') .afterFinish.addClass('added-after-finish') diff --git a/ionic/components/app/test/animations/main.html b/ionic/components/app/test/animations/main.html index 10ddcbdf49..afe886b7da 100644 --- a/ionic/components/app/test/animations/main.html +++ b/ionic/components/app/test/animations/main.html @@ -2,9 +2,29 @@ Animation Tests + +
+
+
+
diff --git a/ionic/components/nav/nav-base.js b/ionic/components/nav/nav-base.js index 50fe556a63..1cf6a155fb 100644 --- a/ionic/components/nav/nav-base.js +++ b/ionic/components/nav/nav-base.js @@ -24,10 +24,10 @@ export class NavBase { this.sbActive = false; } - set initial(Class) { - if (!this._init) { + set initial(Component) { + if (!this._init && Component) { this._init = true; - this.push(Class); + this.push(Component); } } diff --git a/ionic/components/nav/test/basic/index.js b/ionic/components/nav/test/basic/index.js index a0e14745e5..a6d074d726 100644 --- a/ionic/components/nav/test/basic/index.js +++ b/ionic/components/nav/test/basic/index.js @@ -2,9 +2,8 @@ import {bootstrap} from 'angular2/angular2' import {Component, Directive} from 'angular2/src/core/annotations_impl/annotations'; import {View} from 'angular2/src/core/annotations_impl/view'; -import {Nav} from 'ionic/components/nav/nav' -import {Log} from 'ionic/util' -import {FirstPage} from './pages/first-page' +import {Nav} from 'ionic/components/nav/nav'; +import {FirstPage} from './pages/first-page'; @Component({ selector: 'ion-app' }) diff --git a/ionic/components/tabs/tabs.js b/ionic/components/tabs/tabs.js index afcceff047..6aa28cebb8 100644 --- a/ionic/components/tabs/tabs.js +++ b/ionic/components/tabs/tabs.js @@ -7,7 +7,7 @@ import {Injector} from 'angular2/di'; import {NavBase} from 'ionic/components/nav/nav-base'; import {IonicComponent} from 'ionic/config/component'; -import {Tab} from 'ionic/components/tabs/tab'; +import {Tab} from './tab'; @Component({ diff --git a/ionic/transitions/ios-transition.js b/ionic/transitions/ios-transition.js index 7464f88a32..211517f313 100644 --- a/ionic/transitions/ios-transition.js +++ b/ionic/transitions/ios-transition.js @@ -7,11 +7,11 @@ const DURATION = 500; const EASING = 'cubic-bezier(.36,.66,.04,1)'; const OPACITY = 'opacity'; -const TRANSFORM = 'transform'; +const TRANSLATEX = 'translateX'; -const CENTER = 'none'; -const OFF_RIGHT = 'translate3d(100%,0px,0px)'; -const OFF_LEFT = 'translate3d(-33%,0px,0px)'; +const OFF_RIGHT = '100%'; +const OFF_LEFT = '-33%'; +const CENTER = '0%' const OFF_OPACITY = 0.8; const SHOW_TOOLBAR_CSS = 'show-toolbar'; @@ -55,13 +55,13 @@ class IOSTransition extends Animation { // before starting, set enteringItem to display: block enteringContent .beforePlay.addClass(SHOW_NAV_ITEM_CSS) - .to(TRANSFORM, CENTER) + .to(TRANSLATEX, CENTER) .to(OPACITY, 1); enteringTitle .from(OPACITY, 0) .to(OPACITY, 1) - .to(TRANSFORM, CENTER); + .to(TRANSLATEX, CENTER); enteringToolbars .beforePlay.addClass(SHOW_TOOLBAR_CSS); @@ -77,14 +77,14 @@ class IOSTransition extends Animation { // when completed, set leavingItem to display: none leavingContent .afterFinish.removeClass(SHOW_NAV_ITEM_CSS) - .from(TRANSFORM, CENTER) + .from(TRANSLATEX, CENTER) .from(OPACITY, 1); leavingToolbars .afterFinish.removeClass(SHOW_TOOLBAR_CSS); leavingTitle - .from(TRANSFORM, CENTER) + .from(TRANSLATEX, CENTER) .from(OPACITY, 1); if (leavingItem) { @@ -97,42 +97,41 @@ class IOSTransition extends Animation { if (opts.direction === 'back') { // back direction enteringContent - .from(TRANSFORM, OFF_LEFT) + .from(TRANSLATEX, OFF_LEFT) .from(OPACITY, OFF_OPACITY) .to(OPACITY, 1); enteringTitle - .from(TRANSFORM, OFF_LEFT); + .from(TRANSLATEX, OFF_LEFT); leavingContent - .to(TRANSFORM, OFF_RIGHT) + .to(TRANSLATEX, OFF_RIGHT) .to(OPACITY, 1); leavingTitle - .to(TRANSFORM, OFF_RIGHT) + .to(TRANSLATEX, OFF_RIGHT) .to(OPACITY, 0); } else { // forward direction enteringContent - .from(TRANSFORM, OFF_RIGHT) + .from(TRANSLATEX, OFF_RIGHT) .from(OPACITY, 1); enteringTitle - .from(TRANSFORM, OFF_RIGHT); + .from(TRANSLATEX, OFF_RIGHT); leavingContent - .to(TRANSFORM, OFF_LEFT) + .to(TRANSLATEX, OFF_LEFT) .to(OPACITY, OFF_OPACITY); leavingTitle - .to(TRANSFORM, OFF_LEFT) + .to(TRANSLATEX, OFF_LEFT) .to(OPACITY, 0); } // set child animations this.children(enteringContent, enteringToolbars, enteringTitle, leavingContent, leavingToolbars, leavingTitle); - } stage() {