From d7d1f4068ffbb75a29ed1af0ef7dd2eabadd87f1 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Sat, 25 Apr 2015 13:51:18 -0500 Subject: [PATCH] animation refactor --- ...lement-process.js => animation-process.js} | 88 +++------- ionic/collide/animation-start.js | 64 ++++++++ ionic/collide/animation-stop.js | 125 ++++++++++++++ ionic/collide/animation.js | 84 ++++++++++ ionic/collide/collide.js | 13 ++ ionic/collide/complete-call.js | 8 +- ionic/collide/css.js | 59 +++++-- ionic/collide/easing.js | 4 +- ionic/collide/tick.js | 8 +- ionic/collide/transition-action.js | 155 ------------------ ionic/collide/transition.js | 66 -------- .../{transitions => animations}/main.html | 0 .../test/{transitions => animations}/main.js | 4 +- ionic/ionic.js | 2 +- 14 files changed, 373 insertions(+), 307 deletions(-) rename ionic/collide/{element-process.js => animation-process.js} (91%) create mode 100644 ionic/collide/animation-start.js create mode 100644 ionic/collide/animation-stop.js create mode 100644 ionic/collide/animation.js delete mode 100644 ionic/collide/transition-action.js delete mode 100644 ionic/collide/transition.js rename ionic/components/app/test/{transitions => animations}/main.html (100%) rename ionic/components/app/test/{transitions => animations}/main.js (77%) diff --git a/ionic/collide/element-process.js b/ionic/collide/animation-process.js similarity index 91% rename from ionic/collide/element-process.js rename to ionic/collide/animation-process.js index a1702fede3..ad2b5ba580 100644 --- a/ionic/collide/element-process.js +++ b/ionic/collide/animation-process.js @@ -1,10 +1,10 @@ -/* Ported from Velocity.js, MIT License. Julian Shapiro http://twitter.com/shapiro */ +/* Forked from Velocity.js, MIT License. Julian Shapiro http://twitter.com/shapiro */ import * as util from 'ionic/util/util' -import {Collide} from 'ionic/collide/collide' -import {CSS} from 'ionic/collide/css' -import {getEasing} from 'ionic/collide/easing' -import {tick} from 'ionic/collide/tick' +import {Collide} from './collide' +import {CSS} from './css' +import {getEasing} from './easing' +import {tick} from './tick' const data = Collide.data; @@ -18,7 +18,7 @@ const data = Collide.data; 3) Pushing: Consolidation of the tween data followed by its push onto the global in-progress calls container. */ -export function elementProcess(action, elements, elementsIndex, options, propertiesMap) { +export function animationProcess(action, elements, elementsIndex, options, propertiesMap, callUnitConversionData, call) { var resolve; var promise = new Promise(function(res) { resolve = res; @@ -28,31 +28,6 @@ export function elementProcess(action, elements, elementsIndex, options, propert var elementsLength = elements.length; - /************************** - Call-Wide Variables - **************************/ - - /* A container for CSS unit conversion ratios (e.g. %, rem, and em ==> px) that is used to cache ratios across all elements - being animated in a single Collide call. Calculating unit ratios necessitates DOM querying and updating, and is therefore - avoided (via caching) wherever possible. This container is call-wide instead of page-wide to avoid the risk of using stale - conversion metrics across Collide animations that are not immediately consecutively chained. */ - var callUnitConversionData = { - lastParent: null, - lastPosition: null, - lastFontSize: null, - lastPercentToPxWidth: null, - lastPercentToPxHeight: null, - lastEmToPx: null, - remToPx: null, - vwToPx: null, - vhToPx: null - }; - - /* A container for all the ensuing tween data and metadata associated with this call. This container gets pushed to the page-wide - Collide.State.calls array that is processed during animation ticking. */ - var call = []; - - /************************* Part I: Pre-Queueing *************************/ @@ -84,8 +59,6 @@ export function elementProcess(action, elements, elementsIndex, options, propert ******************/ /* Since queue:false doesn't respect the item's existing queue, we avoid injecting its delay here (it's set later on). */ - /* Note: Collide rolls its own delay function since jQuery doesn't have a utility alias for $.fn.delay() - (and thus requires jQuery element creation, which we avoid since its overhead includes DOM querying). */ if (parseFloat(opts.delay) && opts.queue !== false) { Collide.queue(element, opts.queue, function(next) { // This is a flag used to indicate to the upcoming completeCall() function that this queue entry was initiated by Collide. @@ -195,21 +168,19 @@ export function elementProcess(action, elements, elementsIndex, options, propert /* Scroll also uniquely takes an optional 'container' option, which indicates the parent element that should be scrolled -- as opposed to the browser window itself. This is useful for scrolling toward an element that's inside an overflowing parent element. */ if (opts.container) { - /* Ensure that either a jQuery object or a raw DOM element was passed in. */ - if (util.isWrapped(opts.container) || util.isNode(opts.container)) { - /* Extract the raw DOM element from the jQuery wrapper. */ - opts.container = opts.container[0] || opts.container; + /* Ensure that a raw DOM element was passed in. */ + if (opts.container.nodeType) { /* Note: Unlike other properties in Collide, the browser's scroll position is never cached since it so frequently changes (due to the user's natural interaction with the page). */ scrollPositionCurrent = opts.container['scroll' + scrollDirection]; /* GET */ - /* $.position() values are relative to the container's currently viewable area (without taking into account the container's true dimensions - -- say, for example, if the container was not overflowing). Thus, the scroll end value is the sum of the child element's position *and* - the scroll container's current scroll position. */ - scrollPositionEnd = (scrollPositionCurrent + $(element).position()[scrollDirection.toLowerCase()]) + scrollOffset; /* GET */ + /* CSS.position(element) values are relative to the container's currently viewable area (without taking into + account the container's true dimensions, for example, if the container was not overflowing). Thus, the scroll end + value is the sum of the child element's position *and* the scroll container's current scroll position. */ + scrollPositionEnd = (scrollPositionCurrent + CSS.position(element)[scrollDirection.toLowerCase()]) + scrollOffset; /* GET */ } else { - /* If a value other than a jQuery object or a raw DOM element was passed in, default to null so that this option is ignored. */ + /* If a value other than a raw DOM element was passed in, default to null so that this option is ignored. */ opts.container = null; } @@ -217,12 +188,13 @@ export function elementProcess(action, elements, elementsIndex, options, propert /* If the window itself is being scrolled -- not a containing element -- perform a live scroll position lookup using the appropriate cached property names (which differ based on browser type). */ scrollPositionCurrent = Collide.State.scrollAnchor[Collide.State['scrollProperty' + scrollDirection]]; /* GET */ + /* When scrolling the browser window, cache the alternate axis's current value since window.scrollTo() doesn't let us change only one value at a time. */ scrollPositionCurrentAlternate = Collide.State.scrollAnchor[Collide.State['scrollProperty' + (scrollDirection === 'Left' ? 'Top' : 'Left')]]; /* GET */ - /* Unlike $.position(), $.offset() values are relative to the browser window's true dimensions -- not merely its currently viewable area -- - and therefore end values do not need to be compounded onto current values. */ - scrollPositionEnd = $(element).offset()[scrollDirection.toLowerCase()] + scrollOffset; /* GET */ + /* Unlike CSS.position(element), CSS.offset(element) values are relative to the browser window's true dimensions + -- not merely its currently viewable area -- and therefore end values do not need to be compounded onto current values. */ + scrollPositionEnd = CSS.offset(element)[scrollDirection.toLowerCase()] + scrollOffset; /* GET */ } /* Since there's only one format that scroll's associated tweensContainer can take, we create it manually. */ @@ -243,6 +215,8 @@ export function elementProcess(action, elements, elementsIndex, options, propert element: element }; + if (Collide.debug) console.log("tweensContainer (scroll): ", tweensContainer.scroll, element); + /****************************************** Tween Data Construction (for Reverse) @@ -319,6 +293,8 @@ export function elementProcess(action, elements, elementsIndex, options, propert if (!util.isEmptyObject(options)) { lastTweensContainer[lastTween].easing = opts.easing; } + + if (Collide.debug) console.log("reverse tweensContainer (" + lastTween + "): " + JSON.stringify(lastTweensContainer[lastTween]), element); } } @@ -361,7 +337,7 @@ export function elementProcess(action, elements, elementsIndex, options, propert /* Property map values can either take the form of 1) a single value representing the end value, or 2) an array in the form of [ endValue, [, easing] [, startValue] ]. The optional third parameter is a forcefed startValue to be used instead of querying the DOM for - the element's current value. Read Velocity's docmentation to learn more about forcefeeding: VelocityJS.org/#forcefeeding */ + the element's current value. */ function parsePropertyValue(valueData, skipResolvingEasing) { var endValue = undefined, easing = undefined, @@ -482,7 +458,7 @@ export function elementProcess(action, elements, elementsIndex, options, propert /* Note: Since SVG elements have some of their properties directly applied as HTML attributes, there is no way to check for their explicit browser support, and so we skip skip this check for them. */ if (!data(element).isSVG && rootProperty !== 'tween' && CSS.Names.prefixCheck(rootProperty)[1] === false && CSS.Normalizations.registered[rootProperty] === undefined) { - //if (Collide.debug) console.log('Skipping [' + rootProperty + '] due to a lack of browser support.'); + if (Collide.debug) console.log('Skipping [' + rootProperty + '] due to a lack of browser support.'); continue; } @@ -729,7 +705,7 @@ export function elementProcess(action, elements, elementsIndex, options, propert unitRatios.vwToPx = callUnitConversionData.vwToPx; unitRatios.vhToPx = callUnitConversionData.vhToPx; - //if (Collide.debug >= 1) console.log('Unit ratios: ' + JSON.stringify(unitRatios), element); + if (Collide.debug >= 1) console.log('Unit ratios: ' + JSON.stringify(unitRatios), element); return unitRatios; @@ -842,7 +818,7 @@ export function elementProcess(action, elements, elementsIndex, options, propert easing: easing }; - //if (Collide.debug) console.log('tweensContainer (' + property + '): ' + JSON.stringify(tweensContainer[property]), element); + if (Collide.debug) console.log('tweensContainer (' + property + '): ' + JSON.stringify(tweensContainer[property]), element); } /* Along with its property data, store a reference to the element itself onto tweensContainer. */ @@ -878,14 +854,6 @@ export function elementProcess(action, elements, elementsIndex, options, propert Anything on this call container is subjected to tick() processing. */ Collide.State.calls.push([ call, elements, opts, null, resolve ]); - /* If the animation tick isn't running, start it. (Collide shuts it off when there are no active calls to process.) */ - if (Collide.State.isTicking === false) { - Collide.State.isTicking = true; - - /* Start the tick loop. */ - tick(); - } - } else { elementsIndex++; } @@ -930,13 +898,11 @@ export function elementProcess(action, elements, elementsIndex, options, propert /* To fire the first non-custom-queue entry on an element, the element must be dequeued if its queue stack consists *solely* of the current call. (This can be determined by checking - for the 'inprogress' item that jQuery prepends to active queue stack arrays.) Regardless, whenever the element's - queue is further appended with additional items -- including $.delay()'s or even $.animate() calls, the queue's + for the 'inprogress' item that is prepended to active queue stack arrays.) Regardless, whenever the element's + queue is further appended with additional items -- including delay()'s calls, the queue's first entry is automatically fired. This behavior contrasts that of custom queues, which never auto-fire. */ /* Note: When an element set is being subjected to a non-parallel Collide call, the animation will not begin until each one of the elements in the set has reached the end of its individually pre-existing queue chain. */ - /* Note: Unfortunately, most people don't fully grasp jQuery's powerful, yet quirky, Collide.queue() function. - Lean more here: http://stackoverflow.com/questions/1058158/can-somebody-explain-jquery-queue-to-me */ if ((opts.queue === '' || opts.queue === 'fx') && Collide.queue(element)[0] !== 'inprogress') { Collide.dequeue(element); } diff --git a/ionic/collide/animation-start.js b/ionic/collide/animation-start.js new file mode 100644 index 0000000000..5b5a978ed9 --- /dev/null +++ b/ionic/collide/animation-start.js @@ -0,0 +1,64 @@ +/* Forked from Collide.js, MIT License. Julian Shapiro http://twitter.com/shapiro */ + +import {Collide} from './collide' +import {animationProcess} from './animation-process' + + +export function animationStart(elements, options, propertiesMap) { + + if (!elements || !elements.length) { + return Promise.resolve(); + } + + /* The length of the element set (in the form of a nodeList or an array of elements) is defaulted to 1 in case a + single raw DOM element is passed in (which doesn't contain a length property). */ + var elementsLength = elements.length; + var elementsIndex = 0; + var eleData; + + + /********************************** + Animation Call-Wide Variables + **********************************/ + + /* A container for CSS unit conversion ratios (e.g. %, rem, and em ==> px) that is used to cache ratios across all elements + being animated in a single Collide call. Calculating unit ratios necessitates DOM querying and updating, and is therefore + avoided (via caching) wherever possible. This container is call-wide instead of page-wide to avoid the risk of using stale + conversion metrics across Collide animations that are not immediately consecutively chained. */ + var callUnitConversionData = { + lastParent: null, + lastPosition: null, + lastFontSize: null, + lastPercentToPxWidth: null, + lastPercentToPxHeight: null, + lastEmToPx: null, + remToPx: null, + vwToPx: null, + vhToPx: null + }; + + /* A container for all the ensuing tween data and metadata associated with this call. This container gets pushed to the page-wide + Collide.State.calls array that is processed during animation ticking. */ + var call = []; + + + /************************** + Element Set Iteration + **************************/ + + var promises = []; + + if (elements && elements.length) { + for (var i = 0, l = elements.length; i < l; i++) { + if (elements[i] && elements[i].parentElement) { + + promises.push( + animationProcess('start', elements, i, options, propertiesMap, callUnitConversionData, call) + ); + + } + } + } + + return Promise.all(promises); +}; diff --git a/ionic/collide/animation-stop.js b/ionic/collide/animation-stop.js new file mode 100644 index 0000000000..ec6321d94b --- /dev/null +++ b/ionic/collide/animation-stop.js @@ -0,0 +1,125 @@ +/* Forked from Collide.js, MIT License. Julian Shapiro http://twitter.com/shapiro */ + +import {Collide} from './collide' + + +export function animationStop(elements, options, propertiesMap) { + + if (!elements || !elements.length) { + return Promise.resolve(); + } + + var eleData; + + + /****************** + Action: Stop + *******************/ + + /* Clear the currently-active delay on each targeted element. */ + for (var x = 0; x < elements.length; x++) { + eleData = Collide.data(elements[x]); + + if (eleData && eleData.delayTimer) { + /* Stop the timer from triggering its cached next() function. */ + clearTimeout(eleData.delayTimer.setTimeout); + + /* Manually call the next() function so that the subsequent queue items can progress. */ + if (eleData.delayTimer.next) { + eleData.delayTimer.next(); + } + + delete eleData.delayTimer; + } + } + + + var callsToStop = []; + var activeCall; + + /* When the stop action is triggered, the elements' currently active call is immediately stopped. The active call might have + been applied to multiple elements, in which case all of the call's elements will be stopped. When an element + is stopped, the next item in its animation queue is immediately triggered. */ + /* An additional argument may be passed in to clear an element's remaining queued calls. Either true (which defaults to the 'fx' queue) + or a custom queue string can be passed in. */ + /* Note: The stop command runs prior to Collide's Queueing phase since its behavior is intended to take effect *immediately*, + regardless of the element's current queue state. */ + + /* Iterate through every active call. */ + for (var x = 0, callsLength = Collide.State.calls.length; x < callsLength; x++) { + + /* Inactive calls are set to false by the logic inside completeCall(). Skip them. */ + activeCall = Collide.State.calls[x]; + if (activeCall) { + + /* Iterate through the active call's targeted elements. */ + + $.each(activeCall[1], function(k, activeElement) { + /* If true was passed in as a secondary argument, clear absolutely all calls on this element. Otherwise, only + clear calls associated with the relevant queue. */ + /* Call stopping logic works as follows: + - options === true --> stop current default queue calls (and queue:false calls), including remaining queued ones. + - options === undefined --> stop current queue:'' call and all queue:false calls. + - options === false --> stop only queue:false calls. + - options === 'custom' --> stop current queue:'custom' call, including remaining queued ones (there is no functionality to only clear the currently-running queue:'custom' call). */ + var queueName = (options === undefined) ? '' : options; + + if (queueName !== true && (activeCall[2].queue !== queueName) && !(options === undefined && activeCall[2].queue === false)) { + return true; + } + + /* Iterate through the calls targeted by the stop command. */ + $.each(elements, function(l, element) { + /* Check that this call was applied to the target element. */ + if (element === activeElement) { + /* Optionally clear the remaining queued calls. */ + if (options === true || Type.isString(options)) { + /* Iterate through the items in the element's queue. */ + $.each($.queue(element, Type.isString(options) ? options : ''), function(_, item) { + /* The queue array can contain an 'inprogress' string, which we skip. */ + if (Type.isFunction(item)) { + /* Pass the item's callback a flag indicating that we want to abort from the queue call. + (Specifically, the queue will resolve the call's associated promise then abort.) */ + item(null, true); + } + }); + + /* Clearing the $.queue() array is achieved by resetting it to []. */ + $.queue(element, Type.isString(options) ? options : '', []); + } + + if (propertiesMap === 'stop') { + /* Since 'reverse' uses cached start values (the previous call's endValues), these values must be + changed to reflect the final value that the elements were actually tweened to. */ + /* Note: If only queue:false animations are currently running on an element, it won't have a tweensContainer + object. Also, queue:false animations can't be reversed. */ + if (Data(element) && Data(element).tweensContainer && queueName !== false) { + $.each(Data(element).tweensContainer, function(m, activeTween) { + activeTween.endValue = activeTween.currentValue; + }); + } + + callsToStop.push(i); + } else if (propertiesMap === 'finish') { + /* To get active tweens to finish immediately, we forcefully shorten their durations to 1ms so that + they finish upon the next rAf tick then proceed with normal call completion logic. */ + activeCall[2].duration = 1; + } + } + }); + }); + + } + + } // END: for (var x = 0, l = Collide.State.calls.length; x < l; x++) { + + /* Prematurely call completeCall() on each matched active call. Pass an additional flag for 'stop' to indicate + that the complete callback and display:none setting should be skipped since we're completing prematurely. */ + if (propertiesMap === 'stop') { + $.each(callsToStop, function(i, j) { + completeCall(j, true); + }); + + return Promise.reject(); + } +}; diff --git a/ionic/collide/animation.js b/ionic/collide/animation.js new file mode 100644 index 0000000000..e5435fed44 --- /dev/null +++ b/ionic/collide/animation.js @@ -0,0 +1,84 @@ +import {Collide} from './collide' +import {animationStart} from './animation-start' +import {animationStop} from './animation-stop' + + +export class Animation { + constructor() { + this._elements = null + + this._options = {} + this._properties = {} + + this.isRunning = false + } + + elements(ele) { + if (!ele) { + this._elements = null + } else { + this._elements = !ele.length ? [ele] : ele + } + } + + + /************* + Actions + *************/ + + start() { + let promise = animationStart(this._elements, this._options, this._properties) + + Collide.startTick(); + + return promise + } + + stop() { + let promise = animationStop(this._elements, this._options, this._properties) + + return promise + } + + + /*********************** + Options + ***********************/ + options(val) { + this._options = val || {} + } + + option(key, val) { + this._options[key] = val + } + + removeOption(key) { + delete this._options[key] + } + + duration(val) { + this._options.duration = val + } + + easing(val) { + this._options.easing = val + } + + + /************************** + Properties + **************************/ + + properties(val) { + this._properties = val || {} + } + + property(key, val) { + this._properties[key] = val + } + + removeProperty(key) { + delete this._properties[key] + } + +} diff --git a/ionic/collide/collide.js b/ionic/collide/collide.js index 19ff8f1b7e..4b5e31bf8e 100644 --- a/ionic/collide/collide.js +++ b/ionic/collide/collide.js @@ -1,3 +1,5 @@ +/* Forked from Collide.js, MIT License. Julian Shapiro http://twitter.com/shapiro */ + import {dom} from 'ionic/util' @@ -53,6 +55,17 @@ export let Collide = { /* Set to 1 or 2 (most verbose) to output debug info to console. */ debug: false, + startTick: function() { + /* If the animation tick isn't running, start it. + Collide shuts it off when there are no active calls to process. */ + if (Collide.State.isTicking === false) { + Collide.State.isTicking = true; + + /* Start the tick loop. */ + tick(); + } + }, + /* initialize element data */ initData: function(element) { element.$collide = { diff --git a/ionic/collide/complete-call.js b/ionic/collide/complete-call.js index 15f37dd0d7..e3b257915d 100644 --- a/ionic/collide/complete-call.js +++ b/ionic/collide/complete-call.js @@ -1,7 +1,7 @@ -/* Ported from Velocity.js, MIT License. Julian Shapiro http://twitter.com/shapiro */ +/* Forked from Velocity.js, MIT License. Julian Shapiro http://twitter.com/shapiro */ -import {Collide} from 'ionic/collide/collide' -import {CSS} from 'ionic/collide/css' +import {Collide} from './collide' +import {CSS} from './css' /*************************** @@ -136,7 +136,7 @@ export function completeCall (callIndex, isStopped) { /* Fire the next call in the queue so long as this call's queue wasn't set to false (to trigger a parallel animation), which would have already caused the next call to fire. Note: Even if the end of the animation queue has been reached, - Collide.dequeue() must still be called in order to completely clear jQuery's animation queue. */ + Collide.dequeue() must still be called in order to completely clear animation queue. */ if (opts.queue !== false) { Collide.dequeue(element, opts.queue); } diff --git a/ionic/collide/css.js b/ionic/collide/css.js index 3d2cb4283d..f716041752 100644 --- a/ionic/collide/css.js +++ b/ionic/collide/css.js @@ -1,6 +1,6 @@ -/* Ported from Velocity.js, MIT License. Julian Shapiro http://twitter.com/shapiro */ +/* Forked from Collide.js, MIT License. Julian Shapiro http://twitter.com/shapiro */ -import {Collide} from 'ionic/collide/collide' +import {Collide} from './collide' const data = Collide.data; @@ -9,9 +9,8 @@ const data = Collide.data; CSS Stack *****************/ -/* The CSS object is a highly condensed and performant CSS stack that fully replaces jQuery's. - It handles the validation, getting, and setting of both standard CSS properties and CSS property hooks. */ -/* Note: A 'CSS' shorthand is aliased so that our code is easier to read. */ +/* The CSS object handles the validation, getting, and setting of both standard + CSS properties and CSS property hooks. */ export var CSS = { @@ -690,14 +689,10 @@ export var CSS = { if (computedValue === 'auto' && /^(top|right|bottom|left)$/i.test(property)) { var position = computePropertyValue(element, 'position'); /* GET */ - /* For absolute positioning, jQuery's $.position() only returns values for top and left; + /* For absolute positioning, CSS.position(element) only returns values for top and left; right and bottom will have their 'auto' value reverted to 0. */ - /* Note: A jQuery object must be created here since jQuery doesn't have a low-level alias for $.position(). - Not a big deal since we're currently in a GET batch anyway. */ if (position === 'fixed' || (position === 'absolute' && /top|left/i.test(property))) { - /* Note: jQuery strips the pixel unit from its returned values; we re-add it here to conform with computePropertyValue's behavior. */ - // TODO!!!! - computedValue = $(element).position()[property] + 'px'; /* GET */ + computedValue = CSS.position(element)[property] + 'px'; /* GET */ } } @@ -785,6 +780,8 @@ export var CSS = { propertyValue = 0; } + if (Collide.debug >= 2) console.log('Get ' + property + ': ' + propertyValue); + return propertyValue; }, @@ -853,7 +850,7 @@ export var CSS = { element.style[propertyName] = propertyValue; } - //if (Collide.debug >= 2) console.log('Set ' + property + ' (' + propertyName + '): ' + propertyValue); + if (Collide.debug >= 2) console.log('Set ' + property + ' (' + propertyName + '): ' + propertyValue); } } @@ -889,6 +886,44 @@ export var CSS = { } CSS.setPropertyValue(element, 'transform', transformString); + }, + + offset: function(element) { + var box = element.getBoundingClientRect ? element.getBoundingClientRect() : { top: 0, left: 0 }; + + return { + top: box.top + (window.pageYOffset || document.scrollTop || 0) - (document.clientTop || 0), + left: box.left + (window.pageXOffset || document.scrollLeft || 0) - (document.clientLeft || 0) + }; + }, + + position: function(element) { + function offsetParent() { + var offsetParent = this.offsetParent || document; + + while (offsetParent && (!offsetParent.nodeType.toLowerCase === 'html' && offsetParent.style.position === 'static')) { + offsetParent = offsetParent.offsetParent; + } + + return offsetParent || document; + } + + var offsetParent = offsetParent.apply(element), + offset = this.offset(element), + parentOffset = /^(?:body|html)$/i.test(offsetParent.nodeName) ? { top: 0, left: 0 } : this.offset(offsetParent) + + offset.top -= parseFloat(element.style.marginTop) || 0; + offset.left -= parseFloat(element.style.marginLeft) || 0; + + if (offsetParent.style) { + parentOffset.top += parseFloat(offsetParent.style.borderTopWidth) || 0 + parentOffset.left += parseFloat(offsetParent.style.borderLeftWidth) || 0 + } + + return { + top: offset.top - parentOffset.top, + left: offset.left - parentOffset.left + }; } }; diff --git a/ionic/collide/easing.js b/ionic/collide/easing.js index 58ca1bb0c8..b2f5e3f985 100644 --- a/ionic/collide/easing.js +++ b/ionic/collide/easing.js @@ -1,7 +1,7 @@ -/* Ported from Velocity.js, MIT License. Julian Shapiro http://twitter.com/shapiro */ +/* Forked from Collide.js, MIT License. Julian Shapiro http://twitter.com/shapiro */ import * as util from 'ionic/util/util' -import {Collide} from 'ionic/collide/collide' +import {Collide} from './collide' /************** diff --git a/ionic/collide/tick.js b/ionic/collide/tick.js index 162efd7368..22d1a04cf6 100644 --- a/ionic/collide/tick.js +++ b/ionic/collide/tick.js @@ -1,9 +1,9 @@ -/* Ported from Velocity.js, MIT License. Julian Shapiro http://twitter.com/shapiro */ +/* Forked from Collide.js, MIT License. Julian Shapiro http://twitter.com/shapiro */ -import {Collide} from 'ionic/collide/collide' -import {CSS} from 'ionic/collide/css' -import {completeCall} from 'ionic/collide/complete-call' import {dom} from 'ionic/util' +import {Collide} from './collide' +import {CSS} from './css' +import {completeCall} from './complete-call' /************ diff --git a/ionic/collide/transition-action.js b/ionic/collide/transition-action.js deleted file mode 100644 index 780f1bdaef..0000000000 --- a/ionic/collide/transition-action.js +++ /dev/null @@ -1,155 +0,0 @@ -/* Ported from Collide.js, MIT License. Julian Shapiro http://twitter.com/shapiro */ - -import {Collide} from 'ionic/collide/collide' -import {elementProcess} from 'ionic/collide/element-process' - - -export function transitionAction(action, elements, options, propertiesMap) { - - elements = elements && !elements.length ? [elements] : elements - - if (!elements) { - return Promise.reject(); - } - - /* The length of the element set (in the form of a nodeList or an array of elements) is defaulted to 1 in case a - single raw DOM element is passed in (which doesn't contain a length property). */ - var elementsLength = elements.length; - var elementsIndex = 0; - var eleData; - - - if (action === 'stop') { - /****************** - Action: Stop - *******************/ - - /* Clear the currently-active delay on each targeted element. */ - for (var x = 0; x < elements.length; x++) { - eleData = Collide.data(elements[x]); - - if (eleData && eleData.delayTimer) { - /* Stop the timer from triggering its cached next() function. */ - clearTimeout(eleData.delayTimer.setTimeout); - - /* Manually call the next() function so that the subsequent queue items can progress. */ - if (eleData.delayTimer.next) { - eleData.delayTimer.next(); - } - - delete eleData.delayTimer; - } - } - - - var callsToStop = []; - var activeCall; - - /* When the stop action is triggered, the elements' currently active call is immediately stopped. The active call might have - been applied to multiple elements, in which case all of the call's elements will be stopped. When an element - is stopped, the next item in its animation queue is immediately triggered. */ - /* An additional argument may be passed in to clear an element's remaining queued calls. Either true (which defaults to the 'fx' queue) - or a custom queue string can be passed in. */ - /* Note: The stop command runs prior to Collide's Queueing phase since its behavior is intended to take effect *immediately*, - regardless of the element's current queue state. */ - - /* Iterate through every active call. */ - for (var x = 0, callsLength = Collide.State.calls.length; x < callsLength; x++) { - - /* Inactive calls are set to false by the logic inside completeCall(). Skip them. */ - activeCall = Collide.State.calls[x]; - if (activeCall) { - - /* Iterate through the active call's targeted elements. */ - - $.each(activeCall[1], function(k, activeElement) { - /* If true was passed in as a secondary argument, clear absolutely all calls on this element. Otherwise, only - clear calls associated with the relevant queue. */ - /* Call stopping logic works as follows: - - options === true --> stop current default queue calls (and queue:false calls), including remaining queued ones. - - options === undefined --> stop current queue:'' call and all queue:false calls. - - options === false --> stop only queue:false calls. - - options === 'custom' --> stop current queue:'custom' call, including remaining queued ones (there is no functionality to only clear the currently-running queue:'custom' call). */ - var queueName = (options === undefined) ? '' : options; - - if (queueName !== true && (activeCall[2].queue !== queueName) && !(options === undefined && activeCall[2].queue === false)) { - return true; - } - - /* Iterate through the calls targeted by the stop command. */ - $.each(elements, function(l, element) { - /* Check that this call was applied to the target element. */ - if (element === activeElement) { - /* Optionally clear the remaining queued calls. */ - if (options === true || Type.isString(options)) { - /* Iterate through the items in the element's queue. */ - $.each($.queue(element, Type.isString(options) ? options : ''), function(_, item) { - /* The queue array can contain an 'inprogress' string, which we skip. */ - if (Type.isFunction(item)) { - /* Pass the item's callback a flag indicating that we want to abort from the queue call. - (Specifically, the queue will resolve the call's associated promise then abort.) */ - item(null, true); - } - }); - - /* Clearing the $.queue() array is achieved by resetting it to []. */ - $.queue(element, Type.isString(options) ? options : '', []); - } - - if (propertiesMap === 'stop') { - /* Since 'reverse' uses cached start values (the previous call's endValues), these values must be - changed to reflect the final value that the elements were actually tweened to. */ - /* Note: If only queue:false animations are currently running on an element, it won't have a tweensContainer - object. Also, queue:false animations can't be reversed. */ - if (Data(element) && Data(element).tweensContainer && queueName !== false) { - $.each(Data(element).tweensContainer, function(m, activeTween) { - activeTween.endValue = activeTween.currentValue; - }); - } - - callsToStop.push(i); - } else if (propertiesMap === 'finish') { - /* To get active tweens to finish immediately, we forcefully shorten their durations to 1ms so that - they finish upon the next rAf tick then proceed with normal call completion logic. */ - activeCall[2].duration = 1; - } - } - }); - }); - - } - - } // END: for (var x = 0, l = Collide.State.calls.length; x < l; x++) { - - /* Prematurely call completeCall() on each matched active call. Pass an additional flag for 'stop' to indicate - that the complete callback and display:none setting should be skipped since we're completing prematurely. */ - if (propertiesMap === 'stop') { - $.each(callsToStop, function(i, j) { - completeCall(j, true); - }); - - return Promise.reject(); - } - } - - - /************************** - Element Set Iteration - **************************/ - - var promises = []; - - if (elements && elements.length) { - for (var i = 0, l = elements.length; i < l; i++) { - if (elements[i] && elements[i].parentElement) { - - promises.push( - elementProcess(action, elements, i, options, propertiesMap) - ); - - } - } - } - - return Promise.all(promises); -}; diff --git a/ionic/collide/transition.js b/ionic/collide/transition.js deleted file mode 100644 index d70b11f041..0000000000 --- a/ionic/collide/transition.js +++ /dev/null @@ -1,66 +0,0 @@ -import {transitionAction} from 'ionic/collide/transition-action' - - -export class Transition { - constructor(ele) { - console.log('Transition', ele) - - if (!ele || ele.length === 0) return - - this.elements = !ele.length ? [ele] : ele - ele = null - - // Animations that happen to this element - this.animations = [] - - // Sub transitions that happen to sub elements - this.transitions = [] - - this.options = {} - this.propertiesMap = {} - - this.isRunning = false - } - - start() { - var p = transitionAction('start', this.elements, this.options, this.propertiesMap) - - p.then(() => { - console.log('start success done') - }).catch(() => { - console.log('start error done') - }) - } - - stop() { - transitionAction('stop', this.elements, this.options, this.propertiesMap) - } - - properties(val) { - this.propertiesMap = val || {} - } - - property(key, val) { - this.propertiesMap[key] = val - } - - removeProperty(key) { - delete this.propertiesMap[key] - } - - duration(val) { - this.options.duration = val - } - - easing(val) { - this.options.easing = val - } - -} - - -export class IOSTransition extends Transition { - constructor(ele) { - super(ele) - } -} diff --git a/ionic/components/app/test/transitions/main.html b/ionic/components/app/test/animations/main.html similarity index 100% rename from ionic/components/app/test/transitions/main.html rename to ionic/components/app/test/animations/main.html diff --git a/ionic/components/app/test/transitions/main.js b/ionic/components/app/test/animations/main.js similarity index 77% rename from ionic/components/app/test/transitions/main.js rename to ionic/components/app/test/animations/main.js index fa4b38bc4c..6882cd3cd3 100644 --- a/ionic/components/app/test/transitions/main.js +++ b/ionic/components/app/test/animations/main.js @@ -1,5 +1,5 @@ import {Component, Decorator, View as NgView, NgElement, bootstrap} from 'angular2/angular2'; -import {Transition, IOSTransition} from 'ionic/ionic'; +import {Animation} from 'ionic/ionic'; @@ -12,7 +12,7 @@ class IonicApp { @NgElement ngElement:NgElement ) { - this.trans = new Transition( ngElement.domElement.querySelector('.square') ) + this.trans = new Animation( ngElement.domElement.querySelector('.square') ) this.trans.duration(500) this.trans.easing('linear') diff --git a/ionic/ionic.js b/ionic/ionic.js index 8a29649ca7..64df090a29 100644 --- a/ionic/ionic.js +++ b/ionic/ionic.js @@ -13,4 +13,4 @@ export * from 'ionic/webview/webview' export * from 'ionic/webview/cordova/cordova' export * from 'ionic/webview/node-webkit/node-webkit' export * from 'ionic/util/focus' -export * from 'ionic/collide/transition' +export * from 'ionic/collide/animation'