From a13455132124c80013cacaa773a8d237ff3a2ef9 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Tue, 5 May 2015 14:03:10 -0500 Subject: [PATCH] transition updates --- ionic/collide/animation.js | 113 +++++++++++++--------- ionic/collide/process-element.js | 35 +------ ionic/components/nav/test/basic/main.html | 12 +++ ionic/transitions/ios-transition.js | 57 ++++++++--- 4 files changed, 128 insertions(+), 89 deletions(-) diff --git a/ionic/collide/animation.js b/ionic/collide/animation.js index f820eb5dc5..50e5889b1d 100644 --- a/ionic/collide/animation.js +++ b/ionic/collide/animation.js @@ -9,32 +9,45 @@ const data = Collide.data; export class Animation { - constructor() { + constructor(ele) { this.parent = null; - this._elements = null; this._options = {}; this._properties = {}; this._resolve = null; this._call = null; this.children = []; + + this.elements(ele); } - addChild(animation) { - animation.parent = this; - this.children.push(animation); + addChild(childAnimation) { + childAnimation.parent = this; + this.children.push(childAnimation); + return this; + } + + setChildren(childAnimations) { + for (let i = 0; i < childAnimations.length; i++) { + this.addChild(childAnimations[i]); + } return this; } elements(ele) { - if (ele && ele.length > 0) { - this._elements = ele; + this._ele = []; - } else if (ele && ele.nodeType) { - this._elements = [ele]; + if (ele) { + if (typeof ele === 'string') { + ele = document.querySelectorAll(ele); + } - } else { - this._elements = null; + if (ele.length) { + this._ele = ele; + + } else if (ele.nodeType) { + this._ele.push(ele); + } } return this; @@ -51,8 +64,8 @@ export class Animation { this.stop(); } - if (this._elements && this._elements.length) { - this._promise = new Promise(res => { + if (this._ele.length) { + let promise = new Promise(res => { this._resolve = res; }); @@ -85,35 +98,35 @@ export class Animation { var opts = util.extend({}, Collide.defaults); - if (this.parent && this.parent._options) { + if (this.parent) { opts = util.extend(opts, this.parent._options); } this._options = util.extend(opts, this._options); // get the elements ready - for (var i = 0, ii = this._elements.length; i < ii; i++) { + for (var i = 0, ii = this._ele.length; i < ii; i++) { processElement('start', this, i, clearCache); } onNextFrame(); }); - } else { - this._promise = Promise.resolve(); + return promise; } + return Promise.resolve(); } _queueAnimation() { - if (this._elements) { + if (this._ele.length) { if (this._call === null) { return; } var eleData; - for (var i = 0, ii = this._elements.length, element; i < ii && (element = this._elements[i]); i++) { + for (var i = 0, ii = this._ele.length, element; i < ii && (element = this._ele[i]); i++) { if (element) { eleData = data(element); if (eleData) { @@ -141,7 +154,7 @@ export class Animation { /* Add the current call plus its associated metadata (the element set and the call's options) onto the global call container. Anything on this call container is subjected to tick() processing. */ Collide.State.calls.push([ this._call, - this._elements, + this._ele, this._options, null, this._resolve ]); @@ -160,12 +173,12 @@ export class Animation { var clearCache = (this._aniType !== 'start'); - this._setupElements(clearCache, () => { + var promise = this._setupElements(clearCache, () => { this._aniType = 'start'; this._queueAnimation(); }); - promises.push(this._promise); + promises.push(promise); return Promise.all(promises); } @@ -178,25 +191,25 @@ export class Animation { ); } - var clearCache = (this._aniType !== 'percent'); + var clearCache = (this._aniType !== 'progress'); - this._setupElements(clearCache, () => { - this._aniType = 'percent'; + var promise = this._setupElements(clearCache, () => { + this._aniType = 'progress'; }); - promises.push(this._promise); + promises.push(promise); return Promise.all(promises); } - percent(percentComplete) { + progress(value) { // go to and stop at a specific point in the animation - if (this._aniType = 'percent') { - this._options.percentComplete = percentComplete; + if (this._aniType = 'progress') { + this._options.percentComplete = value; for (var i = 0; i < this.children.length; i++) { - this.children[i].percent(percentComplete); + this.children[i].progress(value); } this._queueAnimation(); @@ -205,21 +218,21 @@ export class Animation { stop() { // immediately stop where it's at - animationStop(this._elements, 'stop'); + animationStop(this._ele, 'stop'); return this; } finish() { // immediately go to the end of the animation - animationStop(this._elements, 'finish'); + animationStop(this._ele, 'finish'); return this; } isAnimating() { var eleData; - if (this._elements) { - for (var i = 0, ii = this._elements.length; i < ii; i++) { - eleData = data(this._elements[i]); + if (this._ele) { + for (var i = 0, ii = this._ele.length; i < ii; i++) { + eleData = data(this._ele[i]); if (eleData && eleData.isAnimating) { return true; } @@ -242,11 +255,6 @@ export class Animation { return this; } - removeOption(key) { - delete this._options[key]; - return this; - } - duration(val) { this._options.duration = val; return this; @@ -257,23 +265,40 @@ export class Animation { return this; } + display(val) { + this._options.display = val; + return this; + } + /*************** Properties ***************/ + from(propertyName, value) { + // [endValue, easing, startValue] + let prop = this.getProperty(propertyName); + prop[2] = value; + return this; + } + to() { + // [endValue, easing, startValue] if (arguments.length > 1) { - this._properties[ arguments[0] ] = arguments[1]; + let prop = this.getProperty(arguments[0]); + prop[0] = arguments[1]; } else { this._properties = arguments[0] || {}; } return this; } - removeProperty(key) { - delete this._properties[key]; - return this; + getProperty(propertyName) { + // [endValue, easing, startValue] + if (!this._properties[propertyName]) { + this._properties[propertyName] = [null, null, null]; + } + return this._properties[propertyName]; } diff --git a/ionic/collide/process-element.js b/ionic/collide/process-element.js index 42dfe709e0..d3113171dc 100644 --- a/ionic/collide/process-element.js +++ b/ionic/collide/process-element.js @@ -20,7 +20,7 @@ const data = Collide.data; */ export function processElement(action, animation, elementIndex, clearCache) { - var elements = animation._elements; + var elements = animation._ele; var elementsLength = elements.length; var element = elements[elementIndex]; @@ -254,36 +254,9 @@ export function processElement(action, animation, elementIndex, clearCache) { The optional third parameter is a forcefed startValue to be used instead of querying the DOM for the element's current value. */ function parsePropertyValue(valueData, skipResolvingEasing) { - var endValue = undefined, - easing = undefined, - startValue = undefined; - - /* Handle the array format, which can be structured as one of three potential overloads: - A) [ endValue, easing, startValue ], B) [ endValue, easing ], or C) [ endValue, startValue ] */ - if (Array.isArray(valueData)) { - /* endValue is always the first item in the array. Don't bother validating endValue's value now - since the ensuing property cycling logic does that. */ - endValue = valueData[0]; - - /* Two-item array format: If the second item is a number, function, or hex string, treat it as a - start value since easings can only be non-hex strings or arrays. */ - if ((!Array.isArray(valueData[1]) && /^[\d-]/.test(valueData[1])) || typeof valueData[1] === 'function' || CSS.RegEx.isHex.test(valueData[1])) { - startValue = valueData[1]; - - /* Two or three-item array: If the second item is a non-hex string or an array, treat it as an easing. */ - } else if ((util.isString(valueData[1]) && !CSS.RegEx.isHex.test(valueData[1])) || Array.isArray(valueData[1])) { - easing = skipResolvingEasing ? valueData[1] : getEasing(valueData[1], opts.duration); - - /* Don't bother validating startValue's value now since the ensuing property cycling logic inherently does that. */ - if (valueData[2] !== undefined) { - startValue = valueData[2]; - } - } - - } else { - /* Handle the single-value format. */ - endValue = valueData; - } + var endValue = valueData[0]; + var easing = skipResolvingEasing ? valueData[1] : getEasing(valueData[1], opts.duration); + var startValue = valueData[2]; /* Default to the call's easing if a per-property easing type was not defined. */ if (!skipResolvingEasing) { diff --git a/ionic/components/nav/test/basic/main.html b/ionic/components/nav/test/basic/main.html index a942ebb790..946b4caa4a 100644 --- a/ionic/components/nav/test/basic/main.html +++ b/ionic/components/nav/test/basic/main.html @@ -1,2 +1,14 @@ + + diff --git a/ionic/transitions/ios-transition.js b/ionic/transitions/ios-transition.js index 7f8fb1e623..6f433cdf64 100644 --- a/ionic/transitions/ios-transition.js +++ b/ionic/transitions/ios-transition.js @@ -6,6 +6,12 @@ import {Transition} from './transition' const EASING_FN = [.36, .66, .04, 1]; const DURATION = 500; +const OFF_RIGHT = '100%'; +const OFF_LEFT = '-33%'; +const OFF_OPACITY = 0.8; + +const TRANSLATE_X = 'translateX'; +const OPACITY = 'opacity'; class IOSTransition extends Animation { @@ -13,6 +19,7 @@ class IOSTransition extends Animation { constructor(navCtrl, opts) { super(); + // global duration and easing for all child animations this.duration(DURATION); this.easing('ios'); @@ -20,30 +27,52 @@ class IOSTransition extends Animation { this.enteringItem = navCtrl.getStagedEnteringItem(); this.leavingItem = navCtrl.getStagedLeavingItem(); - // create animation for entering item - let enteringItemAnimation = new Animation(); - enteringItemAnimation.elements(this.enteringItem.navItem.domElement); + // create animation for the entering item + let enteringItemAnimation = new Animation(this.enteringItem.navItem.domElement); - // show the item - this.enteringItem.navItem.domElement.style.display = 'block'; + // create animation for the leaving item + // leavingItem could be null, but the animation instance knows to do nothing + let leavingItemAnimation = new Animation(this.leavingItem && this.leavingItem.navItem.domElement); + // entering item moves to center + // before starting, set enteringItem to display: block + enteringItemAnimation + .display('block') + .to(TRANSLATE_X, 0) + .to(OPACITY, 1); + + // leaving view moves off screen + // when completed, set leavingItem to display: none + leavingItemAnimation + .from(TRANSLATE_X, 0) + .from(OPACITY, 1) + .display('none'); + + // set properties depending on direction if (opts.direction === 'back') { // back direction - this.enteringItem.navItem.domElement.style.transform = 'translateX(-33%)'; - if (this.leavingItem) { - this.leavingItem.navItem.domElement.style.display = ''; - } + enteringItemAnimation + .from(TRANSLATE_X, OFF_LEFT) + .from(OPACITY, OFF_OPACITY) + .to(OPACITY, 1); + + leavingItemAnimation + .to(TRANSLATE_X, OFF_RIGHT) + .to(OPACITY, 1); } else { // forward direction - this.enteringItem.navItem.domElement.style.transform = 'translateX(100%)'; + enteringItemAnimation + .from(TRANSLATE_X, OFF_RIGHT) + .from(OPACITY, 1); + leavingItemAnimation + .to(TRANSLATE_X, OFF_LEFT) + .to(OPACITY, OFF_OPACITY); } - // entering item moves to dead center - enteringItemAnimation.to('translateX', ['0%', '100%']); - - this.addChild(enteringItemAnimation); + // set child animations + this.setChildren([enteringItemAnimation, leavingItemAnimation]); } stage() {