transition updates

This commit is contained in:
Adam Bradley
2015-05-05 14:03:10 -05:00
parent 3a1054eb84
commit a134551321
4 changed files with 128 additions and 89 deletions

View File

@ -9,32 +9,45 @@ const data = Collide.data;
export class Animation { export class Animation {
constructor() { constructor(ele) {
this.parent = null; this.parent = null;
this._elements = null;
this._options = {}; this._options = {};
this._properties = {}; this._properties = {};
this._resolve = null; this._resolve = null;
this._call = null; this._call = null;
this.children = []; this.children = [];
this.elements(ele);
} }
addChild(animation) { addChild(childAnimation) {
animation.parent = this; childAnimation.parent = this;
this.children.push(animation); this.children.push(childAnimation);
return this;
}
setChildren(childAnimations) {
for (let i = 0; i < childAnimations.length; i++) {
this.addChild(childAnimations[i]);
}
return this; return this;
} }
elements(ele) { elements(ele) {
if (ele && ele.length > 0) { this._ele = [];
this._elements = ele;
} else if (ele && ele.nodeType) { if (ele) {
this._elements = [ele]; if (typeof ele === 'string') {
ele = document.querySelectorAll(ele);
}
} else { if (ele.length) {
this._elements = null; this._ele = ele;
} else if (ele.nodeType) {
this._ele.push(ele);
}
} }
return this; return this;
@ -51,8 +64,8 @@ export class Animation {
this.stop(); this.stop();
} }
if (this._elements && this._elements.length) { if (this._ele.length) {
this._promise = new Promise(res => { let promise = new Promise(res => {
this._resolve = res; this._resolve = res;
}); });
@ -85,35 +98,35 @@ export class Animation {
var opts = util.extend({}, Collide.defaults); var opts = util.extend({}, Collide.defaults);
if (this.parent && this.parent._options) { if (this.parent) {
opts = util.extend(opts, this.parent._options); opts = util.extend(opts, this.parent._options);
} }
this._options = util.extend(opts, this._options); this._options = util.extend(opts, this._options);
// get the elements ready // 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); processElement('start', this, i, clearCache);
} }
onNextFrame(); onNextFrame();
}); });
} else { return promise;
this._promise = Promise.resolve();
} }
return Promise.resolve();
} }
_queueAnimation() { _queueAnimation() {
if (this._elements) { if (this._ele.length) {
if (this._call === null) { if (this._call === null) {
return; return;
} }
var eleData; 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) { if (element) {
eleData = data(element); eleData = data(element);
if (eleData) { 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. /* 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. */ Anything on this call container is subjected to tick() processing. */
Collide.State.calls.push([ this._call, Collide.State.calls.push([ this._call,
this._elements, this._ele,
this._options, this._options,
null, null,
this._resolve ]); this._resolve ]);
@ -160,12 +173,12 @@ export class Animation {
var clearCache = (this._aniType !== 'start'); var clearCache = (this._aniType !== 'start');
this._setupElements(clearCache, () => { var promise = this._setupElements(clearCache, () => {
this._aniType = 'start'; this._aniType = 'start';
this._queueAnimation(); this._queueAnimation();
}); });
promises.push(this._promise); promises.push(promise);
return Promise.all(promises); return Promise.all(promises);
} }
@ -178,25 +191,25 @@ export class Animation {
); );
} }
var clearCache = (this._aniType !== 'percent'); var clearCache = (this._aniType !== 'progress');
this._setupElements(clearCache, () => { var promise = this._setupElements(clearCache, () => {
this._aniType = 'percent'; this._aniType = 'progress';
}); });
promises.push(this._promise); promises.push(promise);
return Promise.all(promises); return Promise.all(promises);
} }
percent(percentComplete) { progress(value) {
// go to and stop at a specific point in the animation // go to and stop at a specific point in the animation
if (this._aniType = 'percent') { if (this._aniType = 'progress') {
this._options.percentComplete = percentComplete; this._options.percentComplete = value;
for (var i = 0; i < this.children.length; i++) { for (var i = 0; i < this.children.length; i++) {
this.children[i].percent(percentComplete); this.children[i].progress(value);
} }
this._queueAnimation(); this._queueAnimation();
@ -205,21 +218,21 @@ export class Animation {
stop() { stop() {
// immediately stop where it's at // immediately stop where it's at
animationStop(this._elements, 'stop'); animationStop(this._ele, 'stop');
return this; return this;
} }
finish() { finish() {
// immediately go to the end of the animation // immediately go to the end of the animation
animationStop(this._elements, 'finish'); animationStop(this._ele, 'finish');
return this; return this;
} }
isAnimating() { isAnimating() {
var eleData; var eleData;
if (this._elements) { if (this._ele) {
for (var i = 0, ii = this._elements.length; i < ii; i++) { for (var i = 0, ii = this._ele.length; i < ii; i++) {
eleData = data(this._elements[i]); eleData = data(this._ele[i]);
if (eleData && eleData.isAnimating) { if (eleData && eleData.isAnimating) {
return true; return true;
} }
@ -242,11 +255,6 @@ export class Animation {
return this; return this;
} }
removeOption(key) {
delete this._options[key];
return this;
}
duration(val) { duration(val) {
this._options.duration = val; this._options.duration = val;
return this; return this;
@ -257,23 +265,40 @@ export class Animation {
return this; return this;
} }
display(val) {
this._options.display = val;
return this;
}
/*************** /***************
Properties Properties
***************/ ***************/
from(propertyName, value) {
// [endValue, easing, startValue]
let prop = this.getProperty(propertyName);
prop[2] = value;
return this;
}
to() { to() {
// [endValue, easing, startValue]
if (arguments.length > 1) { if (arguments.length > 1) {
this._properties[ arguments[0] ] = arguments[1]; let prop = this.getProperty(arguments[0]);
prop[0] = arguments[1];
} else { } else {
this._properties = arguments[0] || {}; this._properties = arguments[0] || {};
} }
return this; return this;
} }
removeProperty(key) { getProperty(propertyName) {
delete this._properties[key]; // [endValue, easing, startValue]
return this; if (!this._properties[propertyName]) {
this._properties[propertyName] = [null, null, null];
}
return this._properties[propertyName];
} }

View File

@ -20,7 +20,7 @@ const data = Collide.data;
*/ */
export function processElement(action, animation, elementIndex, clearCache) { export function processElement(action, animation, elementIndex, clearCache) {
var elements = animation._elements; var elements = animation._ele;
var elementsLength = elements.length; var elementsLength = elements.length;
var element = elements[elementIndex]; 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 optional third parameter is a forcefed startValue to be used instead of querying the DOM for
the element's current value. */ the element's current value. */
function parsePropertyValue(valueData, skipResolvingEasing) { function parsePropertyValue(valueData, skipResolvingEasing) {
var endValue = undefined, var endValue = valueData[0];
easing = undefined, var easing = skipResolvingEasing ? valueData[1] : getEasing(valueData[1], opts.duration);
startValue = undefined; var startValue = valueData[2];
/* 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;
}
/* Default to the call's easing if a per-property easing type was not defined. */ /* Default to the call's easing if a per-property easing type was not defined. */
if (!skipResolvingEasing) { if (!skipResolvingEasing) {

View File

@ -1,2 +1,14 @@
<ion-nav [initial]="initial"> <ion-nav [initial]="initial">
</ion-nav> </ion-nav>
<style>
ion-nav {
background: black;
}
.nav-item {
background: white;
}
</style>

View File

@ -6,6 +6,12 @@ import {Transition} from './transition'
const EASING_FN = [.36, .66, .04, 1]; const EASING_FN = [.36, .66, .04, 1];
const DURATION = 500; 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 { class IOSTransition extends Animation {
@ -13,6 +19,7 @@ class IOSTransition extends Animation {
constructor(navCtrl, opts) { constructor(navCtrl, opts) {
super(); super();
// global duration and easing for all child animations
this.duration(DURATION); this.duration(DURATION);
this.easing('ios'); this.easing('ios');
@ -20,30 +27,52 @@ class IOSTransition extends Animation {
this.enteringItem = navCtrl.getStagedEnteringItem(); this.enteringItem = navCtrl.getStagedEnteringItem();
this.leavingItem = navCtrl.getStagedLeavingItem(); this.leavingItem = navCtrl.getStagedLeavingItem();
// create animation for entering item // create animation for the entering item
let enteringItemAnimation = new Animation(); let enteringItemAnimation = new Animation(this.enteringItem.navItem.domElement);
enteringItemAnimation.elements(this.enteringItem.navItem.domElement);
// show the item // create animation for the leaving item
this.enteringItem.navItem.domElement.style.display = 'block'; // 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') { if (opts.direction === 'back') {
// back direction // back direction
this.enteringItem.navItem.domElement.style.transform = 'translateX(-33%)'; enteringItemAnimation
if (this.leavingItem) { .from(TRANSLATE_X, OFF_LEFT)
this.leavingItem.navItem.domElement.style.display = ''; .from(OPACITY, OFF_OPACITY)
} .to(OPACITY, 1);
leavingItemAnimation
.to(TRANSLATE_X, OFF_RIGHT)
.to(OPACITY, 1);
} else { } else {
// forward direction // 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 // set child animations
enteringItemAnimation.to('translateX', ['0%', '100%']); this.setChildren([enteringItemAnimation, leavingItemAnimation]);
this.addChild(enteringItemAnimation);
} }
stage() { stage() {