mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-24 14:58:36 +08:00
let there be web animations
This commit is contained in:
10
gulpfile.js
10
gulpfile.js
@ -25,7 +25,8 @@ gulp.task('build', function() {
|
|||||||
'ionic.copy.js',
|
'ionic.copy.js',
|
||||||
'ionic.examples',
|
'ionic.examples',
|
||||||
'sass',
|
'sass',
|
||||||
'fonts');
|
'fonts',
|
||||||
|
'polyfills');
|
||||||
})
|
})
|
||||||
|
|
||||||
gulp.task('watch', function() {
|
gulp.task('watch', function() {
|
||||||
@ -36,6 +37,7 @@ gulp.task('watch', function() {
|
|||||||
'ionic.examples',
|
'ionic.examples',
|
||||||
'sass',
|
'sass',
|
||||||
'fonts',
|
'fonts',
|
||||||
|
'polyfills',
|
||||||
|
|
||||||
function() {
|
function() {
|
||||||
watch('ionic/**/*.js', function() {
|
watch('ionic/**/*.js', function() {
|
||||||
@ -111,6 +113,12 @@ gulp.task('fonts', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
gulp.task('polyfills', function() {
|
||||||
|
return gulp.src('ionic/animations/web-animations*')
|
||||||
|
.pipe(gulp.dest('../angular-ionic/dist/js/dev/es5/polyfills'));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
gulp.task('update.angular', function(done) {
|
gulp.task('update.angular', function(done) {
|
||||||
|
|
||||||
if (!fs.existsSync('../angular-ionic')) {
|
if (!fs.existsSync('../angular-ionic')) {
|
||||||
|
268
ionic/animations/animation.js
Normal file
268
ionic/animations/animation.js
Normal file
@ -0,0 +1,268 @@
|
|||||||
|
import * as util from 'ionic/util/util';
|
||||||
|
|
||||||
|
|
||||||
|
export class Animation {
|
||||||
|
|
||||||
|
constructor(el) {
|
||||||
|
this._el = [];
|
||||||
|
this._parent = null;
|
||||||
|
this._children = [];
|
||||||
|
this._players = [];
|
||||||
|
|
||||||
|
this._from = null;
|
||||||
|
this._to = null;
|
||||||
|
this._duration = null;
|
||||||
|
this._easing = null;
|
||||||
|
|
||||||
|
this._beforeAddCls = [];
|
||||||
|
this._beforeRmvCls = [];
|
||||||
|
this._afterAddCls = [];
|
||||||
|
this._afterRmvCls = [];
|
||||||
|
|
||||||
|
this.elements(el);
|
||||||
|
}
|
||||||
|
|
||||||
|
elements(el) {
|
||||||
|
if (el) {
|
||||||
|
if (typeof el === 'string') {
|
||||||
|
el = document.querySelectorAll(ele);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (el.length) {
|
||||||
|
for (let i = 0; i < el.length; i++) {
|
||||||
|
this._el.push(el[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (el.nodeType) {
|
||||||
|
this._el.push(el);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
parent(parentAnimation) {
|
||||||
|
this._parent = parentAnimation;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
addChild(childAnimation) {
|
||||||
|
if (childAnimation) {
|
||||||
|
childAnimation.parent(this);
|
||||||
|
this._children.push(childAnimation);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
children(arr) {
|
||||||
|
arr = Array.isArray(arr) ? arr : arguments;
|
||||||
|
for (let i = 0; i < arr.length; i++) {
|
||||||
|
this.addChild(arr[i]);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
duration(value) {
|
||||||
|
if (arguments.length) {
|
||||||
|
this._duration = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return this._duration || (this._parent && this._parent.duration());
|
||||||
|
}
|
||||||
|
|
||||||
|
easing(value) {
|
||||||
|
if (arguments.length) {
|
||||||
|
this._easing = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return this._easing || (this._parent && this._parent.easing());
|
||||||
|
}
|
||||||
|
|
||||||
|
from(property, value) {
|
||||||
|
if (!this._from) {
|
||||||
|
this._from = {}
|
||||||
|
}
|
||||||
|
this._from[property] = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
to(property, value) {
|
||||||
|
if (!this._to) {
|
||||||
|
this._to = {}
|
||||||
|
}
|
||||||
|
this._to[property] = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
get beforePlay() {
|
||||||
|
return {
|
||||||
|
addClass: (className) => {
|
||||||
|
this._beforeAddCls.push(className);
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
removeClass: (className) => {
|
||||||
|
this._beforeRmvCls.push(className);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get afterFinish() {
|
||||||
|
return {
|
||||||
|
addClass: (className) => {
|
||||||
|
this._afterAddCls.push(className);
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
removeClass: (className) => {
|
||||||
|
this._afterRmvCls.push(className);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
play() {
|
||||||
|
let promises = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < this._children.length; i++) {
|
||||||
|
promises.push( this._children[i].play() );
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._to) {
|
||||||
|
// probably just add/removing classes
|
||||||
|
// create bogus transition
|
||||||
|
this._from = this._to = {'opacity': 1};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._players.length) {
|
||||||
|
for (let i = 0; i < this._el.length; i++) {
|
||||||
|
var ele = this._el[i];
|
||||||
|
|
||||||
|
for (let j = 0; j < this._beforeAddCls.length; j++) {
|
||||||
|
ele.classList.add(this._beforeAddCls[j]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let j = 0; j < this._beforeRmvCls.length; j++) {
|
||||||
|
ele.classList.remove(this._beforeRmvCls[j]);
|
||||||
|
}
|
||||||
|
|
||||||
|
var player = new Animate(ele, this._from, this._to, this.duration(), this.easing());
|
||||||
|
this._players.push(player);
|
||||||
|
|
||||||
|
promises.push(player.promise);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
for (let i = 0; i < this._players.length; i++) {
|
||||||
|
this._players[i].play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var promise = Promise.all(promises);
|
||||||
|
|
||||||
|
promise.then(() => {
|
||||||
|
for (let i = 0; i < this._el.length; i++) {
|
||||||
|
var ele = this._el[i];
|
||||||
|
|
||||||
|
for (let j = 0; j < this._afterAddCls.length; j++) {
|
||||||
|
ele.classList.add(this._afterAddCls[j]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let j = 0; j < this._afterRmvCls.length; j++) {
|
||||||
|
ele.classList.remove(this._afterRmvCls[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
pause() {
|
||||||
|
for (let i = 0; i < this._children.length; i++) {
|
||||||
|
this._children[i].pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < this._players.length; i++) {
|
||||||
|
this._players[i].pause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
progress(value) {
|
||||||
|
for (let i = 0; i < this._children.length; i++) {
|
||||||
|
this._children[i].progress(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._players.length) {
|
||||||
|
this.play();
|
||||||
|
this.pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < this._players.length; i++) {
|
||||||
|
this._players[i].progress(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class Animate {
|
||||||
|
|
||||||
|
constructor(ele, fromEffect, toEffect, duration, easing) {
|
||||||
|
// https://w3c.github.io/web-animations/
|
||||||
|
// not using the direct API methods because they're still in flux
|
||||||
|
// 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 (!fromEffect[prop]) {
|
||||||
|
style = style || window.getComputedStyle(ele);
|
||||||
|
fromEffect[prop] = style[prop];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._duration = duration;
|
||||||
|
this._easing = easing;
|
||||||
|
|
||||||
|
this.player = ele.animate([fromEffect, toEffect], {
|
||||||
|
duration: duration,
|
||||||
|
easing: easing,
|
||||||
|
fill: 'both'
|
||||||
|
});
|
||||||
|
|
||||||
|
this.promise = new Promise(resolve => {
|
||||||
|
this.player.onfinish = () => {
|
||||||
|
resolve();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
play() {
|
||||||
|
this.player.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
pause() {
|
||||||
|
this.player.pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
progress(value) {
|
||||||
|
let player = this.player;
|
||||||
|
|
||||||
|
// passed a number between 0 and 1
|
||||||
|
value = Math.max(0, Math.min(1, parseFloat(value)));
|
||||||
|
|
||||||
|
if (value === 1) {
|
||||||
|
player.currentTime = (this._duration * 0.9999);
|
||||||
|
player.play();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (player.playState !== 'paused') {
|
||||||
|
player.pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
player.currentTime = (this._duration * value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
17
ionic/animations/web-animations.min.js
vendored
Normal file
17
ionic/animations/web-animations.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
ionic/animations/web-animations.min.js.map
Normal file
1
ionic/animations/web-animations.min.js.map
Normal file
File diff suppressed because one or more lines are too long
@ -1,135 +0,0 @@
|
|||||||
/* Forked from VelocityJS, MIT License: https://github.com/julianshapiro/velocity | Julian Shapiro http://twitter.com/shapiro */
|
|
||||||
|
|
||||||
import {Collide} from './collide'
|
|
||||||
import {completeCall} from './complete-call'
|
|
||||||
|
|
||||||
|
|
||||||
export function animationStop(elements, action) {
|
|
||||||
|
|
||||||
var customQueue = undefined;
|
|
||||||
var elementsLength = elements.length;
|
|
||||||
|
|
||||||
/******************
|
|
||||||
Action: Stop
|
|
||||||
*******************/
|
|
||||||
|
|
||||||
/* Clear the currently-active delay on each targeted element. */
|
|
||||||
for (var i = 0; i < elementsLength; i++) {
|
|
||||||
var eleData = Collide.data(elements[i]);
|
|
||||||
|
|
||||||
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 = [];
|
|
||||||
|
|
||||||
/* 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 i = 0, callLength = Collide.State.calls.length; i < callLength; i++) {
|
|
||||||
|
|
||||||
/* Inactive calls are set to false by the logic inside completeCall(). Skip them. */
|
|
||||||
var activeCall = Collide.State.calls[i];
|
|
||||||
if (activeCall) {
|
|
||||||
|
|
||||||
/* Iterate through the active call's targeted elements. */
|
|
||||||
var activeElements = activeCall[1];
|
|
||||||
|
|
||||||
for (var j = 0, activeElementsLength = activeElements.length; j < activeElementsLength; j++) {
|
|
||||||
/* 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:
|
|
||||||
- customQueue === true --> stop current default queue calls (and queue:false calls), including remaining queued ones.
|
|
||||||
- customQueue === undefined --> stop current queue:'' call and all queue:false calls.
|
|
||||||
- customQueue === false --> stop only queue:false calls.
|
|
||||||
- customQueue === '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 = (customQueue === undefined) ? '' : customQueue;
|
|
||||||
if (queueName !== true && (activeCall[2].queue !== queueName) && !(customQueue === undefined && activeCall[2].queue === false)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
var activeElement = activeElements[j];
|
|
||||||
|
|
||||||
/* Iterate through the calls targeted by the stop command. */
|
|
||||||
for (var k = 0; k < elementsLength; k++) {
|
|
||||||
var element = elements[k];
|
|
||||||
|
|
||||||
/* Check that this call was applied to the target element. */
|
|
||||||
if (element === activeElement) {
|
|
||||||
|
|
||||||
/* Optionally clear the remaining queued calls. */
|
|
||||||
if (customQueue === true || typeof customQueue === 'string') {
|
|
||||||
|
|
||||||
/* Iterate through the items in the element's queue. */
|
|
||||||
|
|
||||||
var eleQueueName = typeof customQueue === 'string' ? customQueue : '';
|
|
||||||
var eleQueue = Collide.queue(element, eleQueueName);
|
|
||||||
for (var l = 0; l < eleQueue.length; l++) {
|
|
||||||
/* The queue array can contain an 'inprogress' string, which we skip. */
|
|
||||||
if (typeof eleQueue[l] === 'function') {
|
|
||||||
/* 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.) */
|
|
||||||
eleQueue[l](null, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Clearing the queue() array is achieved by resetting it to []. */
|
|
||||||
Collide.queue(element, eleQueueName, []);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (action === '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. */
|
|
||||||
var eleData = Collide.data(element);
|
|
||||||
if (eleData && eleData.tweensContainer && queueName !== false) {
|
|
||||||
for (var tweenName in eleData.tweensContainer) {
|
|
||||||
eleData.tweensContainer[tweenName].endValue = eleData.tweensContainer[tweenName].currentValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
callsToStop.push(i);
|
|
||||||
|
|
||||||
} else if (action === '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 i = 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 (action === 'stop') {
|
|
||||||
for (var i = 0; i < callsToStop.length; i++) {
|
|
||||||
completeCall(i, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
@ -1,340 +0,0 @@
|
|||||||
import * as util from 'ionic/util/util'
|
|
||||||
import {Collide} from './collide'
|
|
||||||
import {processElement} from './process-element'
|
|
||||||
import {animationStop} from './animation-stop'
|
|
||||||
import {startTick} from './tick'
|
|
||||||
import {dom} from 'ionic/util'
|
|
||||||
|
|
||||||
const data = Collide.data;
|
|
||||||
|
|
||||||
|
|
||||||
export class Animation {
|
|
||||||
constructor(ele) {
|
|
||||||
this.parent = null;
|
|
||||||
this._options = {};
|
|
||||||
this._properties = {};
|
|
||||||
this._resolve = null;
|
|
||||||
this._call = null;
|
|
||||||
|
|
||||||
this.children = [];
|
|
||||||
|
|
||||||
this._addStartClasses = [];
|
|
||||||
this._removeStartClasses = [];
|
|
||||||
|
|
||||||
this._addEndClasses = [];
|
|
||||||
this._removeEndClasses = [];
|
|
||||||
|
|
||||||
this.elements(ele);
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
this._ele = [];
|
|
||||||
|
|
||||||
if (ele) {
|
|
||||||
if (typeof ele === 'string') {
|
|
||||||
ele = document.querySelectorAll(ele);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ele.length) {
|
|
||||||
this._ele = ele;
|
|
||||||
|
|
||||||
} else if (ele.nodeType) {
|
|
||||||
this._ele.push(ele);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
_prepare(clearCache, onNextFrame) {
|
|
||||||
|
|
||||||
// ensure another animation wasnt about to start
|
|
||||||
if (this._nextAF) {
|
|
||||||
dom.rafCancel(this._nextAF);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.isAnimating()) {
|
|
||||||
this.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._ele.length) {
|
|
||||||
let promise = new Promise(res => {
|
|
||||||
this._resolve = res;
|
|
||||||
});
|
|
||||||
|
|
||||||
this._call = null;
|
|
||||||
|
|
||||||
// in the next frame, do all the DOM GETs to load element info
|
|
||||||
this._nextAF = dom.raf(() => {
|
|
||||||
|
|
||||||
/**********************************
|
|
||||||
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. */
|
|
||||||
this._unitConversion = {
|
|
||||||
lastParent: null,
|
|
||||||
lastPosition: null,
|
|
||||||
lastFontSize: null,
|
|
||||||
lastPercentToPxWidth: null,
|
|
||||||
lastPercentToPxHeight: null,
|
|
||||||
lastEmToPx: null,
|
|
||||||
remToPx: null,
|
|
||||||
vwToPx: null,
|
|
||||||
vhToPx: null
|
|
||||||
};
|
|
||||||
|
|
||||||
this._call = [];
|
|
||||||
|
|
||||||
var opts = util.extend({}, Collide.defaults);
|
|
||||||
|
|
||||||
if (this.parent) {
|
|
||||||
opts = util.extend(opts, this.parent._options);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._options = util.extend(opts, this._options);
|
|
||||||
|
|
||||||
// get the elements ready
|
|
||||||
for (let i = 0, ii = this._ele.length; i < ii; i++) {
|
|
||||||
processElement('start', this, i, clearCache);
|
|
||||||
}
|
|
||||||
|
|
||||||
onNextFrame();
|
|
||||||
});
|
|
||||||
|
|
||||||
return promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
_queue() {
|
|
||||||
if (this._ele.length) {
|
|
||||||
|
|
||||||
if (this._call === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var eleData;
|
|
||||||
for (var i = 0, ii = this._ele.length, element; i < ii && (element = this._ele[i]); i++) {
|
|
||||||
if (element) {
|
|
||||||
eleData = data(element);
|
|
||||||
if (eleData) {
|
|
||||||
eleData.isAnimating = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*********************
|
|
||||||
Auto-Dequeuing
|
|
||||||
*********************/
|
|
||||||
|
|
||||||
/* 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 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. */
|
|
||||||
if (this._options.queue === '' && Collide.queue(element)[0] !== 'inprogress') {
|
|
||||||
Collide.dequeue(element);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Push the call array onto Collide.State.calls for the animation tick to immediately begin processing. */
|
|
||||||
/* 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._ele,
|
|
||||||
this._options,
|
|
||||||
null,
|
|
||||||
this._resolve ]);
|
|
||||||
}
|
|
||||||
|
|
||||||
startTick();
|
|
||||||
}
|
|
||||||
|
|
||||||
start() {
|
|
||||||
var promises = [];
|
|
||||||
for (var i = 0; i < this.children.length; i++) {
|
|
||||||
promises.push(
|
|
||||||
this.children[i].start()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
var clearCache = (this._aniType !== 'start');
|
|
||||||
|
|
||||||
var promise = this._prepare(clearCache, () => {
|
|
||||||
this._aniType = 'start';
|
|
||||||
this._queue();
|
|
||||||
});
|
|
||||||
|
|
||||||
promises.push(promise);
|
|
||||||
|
|
||||||
return Promise.all(promises);
|
|
||||||
}
|
|
||||||
|
|
||||||
ready() {
|
|
||||||
var promises = [];
|
|
||||||
for (var i = 0; i < this.children.length; i++) {
|
|
||||||
promises.push(
|
|
||||||
this.children[i].ready()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
var clearCache = (this._aniType !== 'progress');
|
|
||||||
|
|
||||||
var promise = this._prepare(clearCache, () => {
|
|
||||||
this._aniType = 'progress';
|
|
||||||
});
|
|
||||||
|
|
||||||
promises.push(promise);
|
|
||||||
|
|
||||||
return Promise.all(promises);
|
|
||||||
}
|
|
||||||
|
|
||||||
progress(value) {
|
|
||||||
// go to and stop at a specific point in the animation
|
|
||||||
|
|
||||||
if (this._aniType = 'progress') {
|
|
||||||
this._options.percentComplete = value;
|
|
||||||
|
|
||||||
for (var i = 0; i < this.children.length; i++) {
|
|
||||||
this.children[i].progress(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._queue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stop() {
|
|
||||||
// immediately stop where it's at
|
|
||||||
animationStop(this._ele, 'stop');
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
finish() {
|
|
||||||
// immediately go to the end of the animation
|
|
||||||
animationStop(this._ele, 'finish');
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
addStartClass(className) {
|
|
||||||
this._addStartClasses.push(className);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
removeStartClass(className) {
|
|
||||||
this._removeStartClasses.push(className);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
addEndClass(className) {
|
|
||||||
this._addEndClasses.push(className);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
removeEndClass(className) {
|
|
||||||
this._removeEndClasses.push(className);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
isAnimating() {
|
|
||||||
var eleData;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/************
|
|
||||||
Options
|
|
||||||
************/
|
|
||||||
options(val) {
|
|
||||||
this._options = val || {};
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
option(key, val) {
|
|
||||||
this._options[key] = val;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
duration(val) {
|
|
||||||
this._options.duration = val;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
easing(val) {
|
|
||||||
this._options.easing = val;
|
|
||||||
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) {
|
|
||||||
let prop = this.getProperty(arguments[0]);
|
|
||||||
prop[0] = arguments[1];
|
|
||||||
} else {
|
|
||||||
this._properties = arguments[0] || {};
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
getProperty(propertyName) {
|
|
||||||
// [endValue, easing, startValue]
|
|
||||||
if (!this._properties[propertyName]) {
|
|
||||||
this._properties[propertyName] = [null, null, null];
|
|
||||||
}
|
|
||||||
return this._properties[propertyName];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*********
|
|
||||||
Misc
|
|
||||||
*********/
|
|
||||||
|
|
||||||
debug(val) {
|
|
||||||
Collide.debug = !!val;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,127 +0,0 @@
|
|||||||
/* Forked from VelocityJS, MIT License: https://github.com/julianshapiro/velocity | Julian Shapiro http://twitter.com/shapiro */
|
|
||||||
|
|
||||||
/***************************
|
|
||||||
Unit Ratio Calculation
|
|
||||||
***************************/
|
|
||||||
|
|
||||||
/* When queried, the browser returns (most) CSS property values in pixels. Therefore, if an endValue with a unit type of
|
|
||||||
%, em, or rem is animated toward, startValue must be converted from pixels into the same unit type as endValue in order
|
|
||||||
for value manipulation logic (increment/decrement) to proceed. Further, if the startValue was forcefed or transferred
|
|
||||||
from a previous call, startValue may also not be in pixels. Unit conversion logic therefore consists of two steps:
|
|
||||||
1) Calculating the ratio of %/em/rem/vh/vw relative to pixels
|
|
||||||
2) Converting startValue into the same unit of measurement as endValue based on these ratios. */
|
|
||||||
/* Unit conversion ratios are calculated by inserting a sibling node next to the target node, copying over its position property,
|
|
||||||
setting values with the target unit type then comparing the returned pixel value. */
|
|
||||||
/* Note: Even if only one of these unit types is being animated, all unit ratios are calculated at once since the overhead
|
|
||||||
of batching the SETs and GETs together upfront outweights the potential overhead
|
|
||||||
of layout thrashing caused by re-querying for uncalculated ratios for subsequently-processed properties. */
|
|
||||||
/* Todo: Shift this logic into the calls' first tick instance so that it's synced with RAF. */
|
|
||||||
export function calculateUnitRatios(element, callUnitConversionData) {
|
|
||||||
|
|
||||||
/**************************************************************
|
|
||||||
parsePropertyValue(), calculateUnitRatios(), Same Ratio Checks
|
|
||||||
**************************************************************/
|
|
||||||
|
|
||||||
/* The properties below are used to determine whether the element differs sufficiently from this call's
|
|
||||||
previously iterated element to also differ in its unit conversion ratios. If the properties match up with those
|
|
||||||
of the prior element, the prior element's conversion ratios are used. Like most optimizations in Collide,
|
|
||||||
this is done to minimize DOM querying. */
|
|
||||||
var sameRatioIndicators = {
|
|
||||||
myParent: element.parentNode || document.body, /* GET */
|
|
||||||
position: CSS.getPropertyValue(element, 'position'), /* GET */
|
|
||||||
fontSize: CSS.getPropertyValue(element, 'fontSize') /* GET */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Determine if the same % ratio can be used. % is based on the element's position value and its parent's width and height dimensions. */
|
|
||||||
var samePercentRatio = ((sameRatioIndicators.position === callUnitConversionData.lastPosition) && (sameRatioIndicators.myParent === callUnitConversionData.lastParent));
|
|
||||||
|
|
||||||
/* Determine if the same em ratio can be used. em is relative to the element's fontSize. */
|
|
||||||
var sameEmRatio = (sameRatioIndicators.fontSize === callUnitConversionData.lastFontSize);
|
|
||||||
|
|
||||||
/* Store these ratio indicators call-wide for the next element to compare against. */
|
|
||||||
callUnitConversionData.lastParent = sameRatioIndicators.myParent;
|
|
||||||
callUnitConversionData.lastPosition = sameRatioIndicators.position;
|
|
||||||
callUnitConversionData.lastFontSize = sameRatioIndicators.fontSize;
|
|
||||||
|
|
||||||
|
|
||||||
/**********************************************************************
|
|
||||||
parsePropertyValue(), calculateUnitRatios(), Element-Specific Units
|
|
||||||
**********************************************************************/
|
|
||||||
|
|
||||||
var measurement = 100,
|
|
||||||
unitRatios = {};
|
|
||||||
|
|
||||||
if (!sameEmRatio || !samePercentRatio) {
|
|
||||||
var dummy = data(element).isSVG ? document.createElementNS('http://www.w3.org/2000/svg', 'rect') : document.createElement('div');
|
|
||||||
|
|
||||||
Collide.init(dummy);
|
|
||||||
sameRatioIndicators.myParent.appendChild(dummy);
|
|
||||||
|
|
||||||
/* To accurately and consistently calculate conversion ratios, the element's cascaded overflow and box-sizing are stripped.
|
|
||||||
Similarly, since width/height can be artificially constrained by their min-/max- equivalents, these are controlled for as well. */
|
|
||||||
/* Note: Overflow must be also be controlled for per-axis since the overflow property overwrites its per-axis values. */
|
|
||||||
var cssPropNames = [ 'overflow', 'overflowX', 'overflowY' ];
|
|
||||||
for (var i = 0; i < overflows.length; i++) {
|
|
||||||
Collide.CSS.setPropertyValue(dummy, cssPropNames[i], 'hidden');
|
|
||||||
}
|
|
||||||
|
|
||||||
Collide.CSS.setPropertyValue(dummy, 'position', sameRatioIndicators.position);
|
|
||||||
Collide.CSS.setPropertyValue(dummy, 'fontSize', sameRatioIndicators.fontSize);
|
|
||||||
Collide.CSS.setPropertyValue(dummy, 'boxSizing', 'content-box');
|
|
||||||
|
|
||||||
/* width and height act as our proxy properties for measuring the horizontal and vertical % ratios. */
|
|
||||||
cssPropNames = [ 'minWidth', 'maxWidth', 'width', 'minHeight', 'maxHeight', 'height' ];
|
|
||||||
for (var i = 0; i < overflows.length; i++) {
|
|
||||||
Collide.CSS.setPropertyValue(dummy, cssPropNames[i], measurement + '%');
|
|
||||||
}
|
|
||||||
|
|
||||||
/* paddingLeft arbitrarily acts as our proxy property for the em ratio. */
|
|
||||||
Collide.CSS.setPropertyValue(dummy, 'paddingLeft', measurement + 'em');
|
|
||||||
|
|
||||||
/* Divide the returned value by the measurement to get the ratio between 1% and 1px. Default to 1 since working with 0 can produce Infinite. */
|
|
||||||
unitRatios.percentToPxWidth = callUnitConversionData.lastPercentToPxWidth = (parseFloat(CSS.getPropertyValue(dummy, 'width', null, true)) || 1) / measurement; /* GET */
|
|
||||||
unitRatios.percentToPxHeight = callUnitConversionData.lastPercentToPxHeight = (parseFloat(CSS.getPropertyValue(dummy, 'height', null, true)) || 1) / measurement; /* GET */
|
|
||||||
unitRatios.emToPx = callUnitConversionData.lastEmToPx = (parseFloat(CSS.getPropertyValue(dummy, 'paddingLeft')) || 1) / measurement; /* GET */
|
|
||||||
|
|
||||||
sameRatioIndicators.myParent.removeChild(dummy);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
unitRatios.emToPx = callUnitConversionData.lastEmToPx;
|
|
||||||
unitRatios.percentToPxWidth = callUnitConversionData.lastPercentToPxWidth;
|
|
||||||
unitRatios.percentToPxHeight = callUnitConversionData.lastPercentToPxHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**********************************************************************
|
|
||||||
parsePropertyValue(), calculateUnitRatios(), Element-Agnostic Units
|
|
||||||
***********************************************************************/
|
|
||||||
|
|
||||||
/* Whereas % and em ratios are determined on a per-element basis, the rem unit only needs to be checked
|
|
||||||
once per call since it's exclusively dependant upon document.body's fontSize. If this is the first time
|
|
||||||
that calculateUnitRatios() is being run during this call, remToPx will still be set to its default value of null,
|
|
||||||
so we calculate it now. */
|
|
||||||
if (callUnitConversionData.remToPx === null) {
|
|
||||||
/* Default to browsers' default fontSize of 16px in the case of 0. */
|
|
||||||
if (!remToPx) {
|
|
||||||
remToPx = parseFloat(CSS.getPropertyValue(document.body, 'fontSize')) || 16; /* GET */
|
|
||||||
}
|
|
||||||
callUnitConversionData.remToPx = remToPx
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Similarly, viewport units are %-relative to the window's inner dimensions. */
|
|
||||||
if (callUnitConversionData.vwToPx === null) {
|
|
||||||
callUnitConversionData.vwToPx = parseFloat(window.innerWidth) / 100; /* GET */
|
|
||||||
callUnitConversionData.vhToPx = parseFloat(window.innerHeight) / 100; /* GET */
|
|
||||||
}
|
|
||||||
|
|
||||||
unitRatios.remToPx = callUnitConversionData.remToPx;
|
|
||||||
unitRatios.vwToPx = callUnitConversionData.vwToPx;
|
|
||||||
unitRatios.vhToPx = callUnitConversionData.vhToPx;
|
|
||||||
|
|
||||||
if (Collide.debug >= 1) console.log('Unit ratios: ' + JSON.stringify(unitRatios), element);
|
|
||||||
|
|
||||||
return unitRatios;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
let remToPx = null;
|
|
@ -1,173 +0,0 @@
|
|||||||
/* Forked from VelocityJS, MIT License: https://github.com/julianshapiro/velocity | Julian Shapiro http://twitter.com/shapiro */
|
|
||||||
|
|
||||||
import {dom} from 'ionic/util'
|
|
||||||
|
|
||||||
|
|
||||||
export let Collide = {
|
|
||||||
|
|
||||||
/* Container for page-wide Collide state data. */
|
|
||||||
State: {
|
|
||||||
/* Create a cached element for re-use when checking for CSS property prefixes. */
|
|
||||||
prefixElement: document.createElement('div'),
|
|
||||||
|
|
||||||
/* Cache every prefix match to avoid repeating lookups. */
|
|
||||||
prefixMatches: {},
|
|
||||||
|
|
||||||
/* Cache the anchor used for animating window scrolling. */
|
|
||||||
scrollAnchor: null,
|
|
||||||
|
|
||||||
/* Cache the browser-specific property names associated with the scroll anchor. */
|
|
||||||
scrollPropertyLeft: null,
|
|
||||||
scrollPropertyTop: null,
|
|
||||||
|
|
||||||
/* Keep track of whether our RAF tick is running. */
|
|
||||||
isTicking: false,
|
|
||||||
|
|
||||||
/* Container for every in-progress call to Collide. */
|
|
||||||
calls: []
|
|
||||||
},
|
|
||||||
|
|
||||||
CSS: {},
|
|
||||||
Easings: {},
|
|
||||||
|
|
||||||
/* Collide option defaults, which can be overriden by the user. */
|
|
||||||
defaults: {
|
|
||||||
queue: '',
|
|
||||||
duration: 400,
|
|
||||||
easing: 'swing',
|
|
||||||
begin: undefined,
|
|
||||||
complete: undefined,
|
|
||||||
percentComplete: undefined,
|
|
||||||
percent: undefined,
|
|
||||||
display: undefined,
|
|
||||||
visibility: undefined,
|
|
||||||
loop: false,
|
|
||||||
delay: false,
|
|
||||||
/* Advanced: Set to false to prevent property values from being cached between consecutive Collide-initiated chain calls. */
|
|
||||||
_cacheValues: true
|
|
||||||
},
|
|
||||||
|
|
||||||
/* Used for getting/setting Collide's hooked CSS properties. */
|
|
||||||
hook: null, /* Defined below. */
|
|
||||||
|
|
||||||
/* Collide-wide animation time remapping for testing purposes. */
|
|
||||||
mock: false,
|
|
||||||
|
|
||||||
/* Set to 1 or 2 (most verbose) to output debug info to console. */
|
|
||||||
debug: false,
|
|
||||||
|
|
||||||
/* initialize element data */
|
|
||||||
initData: function(element) {
|
|
||||||
element.$collide = {
|
|
||||||
/* Store whether this is an SVG element, since its properties are retrieved and updated differently than standard HTML elements. */
|
|
||||||
isSVG: dom.isSVG(element),
|
|
||||||
|
|
||||||
/* Keep track of whether the element is currently being animated by Collide.
|
|
||||||
This is used to ensure that property values are not transferred between non-consecutive (stale) calls. */
|
|
||||||
isAnimating: false,
|
|
||||||
|
|
||||||
/* A reference to the element's live computedStyle object. Learn more here: https://developer.mozilla.org/en/docs/Web/API/window.getComputedStyle */
|
|
||||||
computedStyle: null,
|
|
||||||
|
|
||||||
/* Tween data is cached for each animation on the element so that data can be passed across calls --
|
|
||||||
in particular, end values are used as subsequent start values in consecutive Collide calls. */
|
|
||||||
tweensContainer: null,
|
|
||||||
|
|
||||||
/* The full root property values of each CSS hook being animated on this element are cached so that:
|
|
||||||
1) Concurrently-animating hooks sharing the same root can have their root values' merged into one while tweening.
|
|
||||||
2) Post-hook-injection root values can be transferred over to consecutively chained Collide calls as starting root values. */
|
|
||||||
rootPropertyValueCache: {},
|
|
||||||
|
|
||||||
/* A cache for transform updates, which must be manually flushed via CSS.flushTransformCache(). */
|
|
||||||
transformCache: {},
|
|
||||||
|
|
||||||
startAddCls: null,
|
|
||||||
startRmvCls: null,
|
|
||||||
endAddCls: null,
|
|
||||||
endRmvCls: null
|
|
||||||
};
|
|
||||||
|
|
||||||
return element.$collide;
|
|
||||||
},
|
|
||||||
|
|
||||||
/* get/set element data */
|
|
||||||
data: function(element, key, value) {
|
|
||||||
if (value === undefined) {
|
|
||||||
|
|
||||||
if (key === undefined) {
|
|
||||||
// get data object: Data(element)
|
|
||||||
return element.$collide;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (element.$collide) {
|
|
||||||
// get data by key: Data(element, key)
|
|
||||||
return element.$collide[key];
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (key !== undefined) {
|
|
||||||
// set data: Data(element, key, value)
|
|
||||||
if (!element.$collide) {
|
|
||||||
element.$collide = {};
|
|
||||||
}
|
|
||||||
element.$collide[key] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
/* get/set element queue data */
|
|
||||||
queue: function (element, type, data) {
|
|
||||||
function $makeArray (arr, results) {
|
|
||||||
let ret = results || [];
|
|
||||||
|
|
||||||
if (arr != null) {
|
|
||||||
[].push.call(ret, arr);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!element) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
type = (type || 'collide') + 'queue';
|
|
||||||
|
|
||||||
var q = this.data(element, type);
|
|
||||||
|
|
||||||
if (!data) {
|
|
||||||
return q || [];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!q || Array.isArray(data)) {
|
|
||||||
q = this.data(element, type, $makeArray(data));
|
|
||||||
|
|
||||||
} else {
|
|
||||||
q.push(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
return q;
|
|
||||||
},
|
|
||||||
|
|
||||||
/* dequeue element */
|
|
||||||
dequeue: function (element, type) {
|
|
||||||
type = type || 'collide';
|
|
||||||
|
|
||||||
let queue = this.queue(element, type);
|
|
||||||
let fn = queue.shift();
|
|
||||||
|
|
||||||
if (fn === 'inprogress') {
|
|
||||||
fn = queue.shift();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fn) {
|
|
||||||
if (type === 'collide') {
|
|
||||||
queue.unshift('inprogress');
|
|
||||||
}
|
|
||||||
|
|
||||||
fn.call(element, () => {
|
|
||||||
this.dequeue(element, type);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
@ -1,186 +0,0 @@
|
|||||||
/* Forked from VelocityJS, MIT License: https://github.com/julianshapiro/velocity | Julian Shapiro http://twitter.com/shapiro */
|
|
||||||
|
|
||||||
import {Collide} from './collide'
|
|
||||||
import {CSS} from './css'
|
|
||||||
|
|
||||||
|
|
||||||
/***************************
|
|
||||||
Call Completion
|
|
||||||
***************************/
|
|
||||||
|
|
||||||
/* Note: Unlike tick(), which processes all active calls at once, call completion is handled on a per-call basis. */
|
|
||||||
export function completeCall(callIndex, isStopped) {
|
|
||||||
|
|
||||||
/* Ensure the call exists. */
|
|
||||||
if (!Collide.State.calls[callIndex]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Pull the metadata from the call. */
|
|
||||||
var call = Collide.State.calls[callIndex][0],
|
|
||||||
elements = Collide.State.calls[callIndex][1],
|
|
||||||
opts = Collide.State.calls[callIndex][2],
|
|
||||||
resolve = Collide.State.calls[callIndex][4];
|
|
||||||
|
|
||||||
var remainingCallsExist = false;
|
|
||||||
|
|
||||||
|
|
||||||
/*************************
|
|
||||||
Element Finalization
|
|
||||||
*************************/
|
|
||||||
|
|
||||||
for (var i = 0, callLength = call.length; i < callLength; i++) {
|
|
||||||
var element = call[i].element;
|
|
||||||
var eleData = Collide.data(element);
|
|
||||||
|
|
||||||
/* If the user set display to 'none' (intending to hide the element), set it now that the animation has completed. */
|
|
||||||
/* Note: display:none isn't set when calls are manually stopped (via Collide('stop'). */
|
|
||||||
/* Note: Display gets ignored with 'reverse' calls and infinite loops, since this behavior would be undesirable. */
|
|
||||||
if (!isStopped && !opts.loop) {
|
|
||||||
if (opts.display === 'none') {
|
|
||||||
CSS.setPropertyValue(element, 'display', opts.display);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts.visibility === 'hidden') {
|
|
||||||
CSS.setPropertyValue(element, 'visibility', opts.visibility);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (eleData.endAddCls) {
|
|
||||||
for (var k = 0; k < eleData.endAddCls.length; k++) {
|
|
||||||
element.classList.add(eleData.endAddCls[k]);
|
|
||||||
}
|
|
||||||
eleData.endAddCls = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (eleData.endRmvCls) {
|
|
||||||
for (var k = 0; k < eleData.endRmvCls.length; k++) {
|
|
||||||
element.classList.remove(eleData.endRmvCls[k]);
|
|
||||||
}
|
|
||||||
eleData.endRmvCls = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If the element's queue is empty (if only the 'inprogress' item is left at position 0) or if its queue is about to run
|
|
||||||
a non-Collide-initiated entry, turn off the isAnimating flag. A non-Collide-initiatied queue entry's logic might alter
|
|
||||||
an element's CSS values and thereby cause Collide's cached value data to go stale. To detect if a queue entry was initiated by Collide,
|
|
||||||
we check for the existence of our special Collide.queueEntryFlag declaration, which minifiers won't rename since the flag
|
|
||||||
is assigned to's global object and thus exists out of Collide's own scope. */
|
|
||||||
if (opts.loop !== true && (Collide.queue(element)[1] === undefined || !/\.collideQueueEntryFlag/i.test(Collide.queue(element)[1]))) {
|
|
||||||
/* The element may have been deleted. Ensure that its data cache still exists before acting on it. */
|
|
||||||
|
|
||||||
if (eleData) {
|
|
||||||
eleData.isAnimating = false;
|
|
||||||
|
|
||||||
/* Clear the element's rootPropertyValueCache, which will become stale. */
|
|
||||||
eleData.rootPropertyValueCache = {};
|
|
||||||
|
|
||||||
/* If any 3D transform subproperty is at its default value (regardless of unit type), remove it. */
|
|
||||||
for (var j = 0, jj = CSS.Lists.transforms3D.length; j < jj; j++) {
|
|
||||||
var transformName = CSS.Lists.transforms3D[j];
|
|
||||||
var defaultValue = /^scale/.test(transformName) ? 1 : 0;
|
|
||||||
var currentValue = eleData.transformCache[transformName];
|
|
||||||
|
|
||||||
if (currentValue !== undefined && new RegExp('^\\(' + defaultValue + '[^.]').test(currentValue)) {
|
|
||||||
delete eleData.transformCache[transformName];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Flush the subproperty removals to the DOM. */
|
|
||||||
CSS.flushTransformCache(element);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*********************
|
|
||||||
Option: Complete
|
|
||||||
*********************/
|
|
||||||
|
|
||||||
/* Complete is fired once per call (not once per element) and is passed the full raw DOM element set as both its context and its first argument. */
|
|
||||||
/* Note: Callbacks aren't fired when calls are manually stopped (via Collide('stop'). */
|
|
||||||
if (!isStopped && opts.complete && !opts.loop && (i === callLength - 1)) {
|
|
||||||
/* We throw callbacks in a setTimeout so that thrown errors don't halt the execution of Collide itself. */
|
|
||||||
try {
|
|
||||||
opts.complete.call(elements, elements);
|
|
||||||
} catch (error) {
|
|
||||||
setTimeout(function() { throw error; });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**********************
|
|
||||||
Promise Resolving
|
|
||||||
**********************/
|
|
||||||
|
|
||||||
/* Note: Infinite loops don't return promises. */
|
|
||||||
if (resolve && opts.loop !== true) {
|
|
||||||
resolve(elements);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/****************************
|
|
||||||
Option: Loop (Infinite)
|
|
||||||
****************************/
|
|
||||||
|
|
||||||
if (eleData && opts.loop === true && !isStopped) {
|
|
||||||
/* If a rotateX/Y/Z property is being animated to 360 deg with loop:true, swap tween start/end values to enable
|
|
||||||
continuous iterative rotation looping. (Otherise, the element would just rotate back and forth.) */
|
|
||||||
|
|
||||||
for (var propertyName in eleData.tweensContainer) {
|
|
||||||
var tweenContainer = eleData.tweensContainer[propertyName]
|
|
||||||
|
|
||||||
if (/^rotate/.test(propertyName) && parseFloat(tweenContainer.endValue) === 360) {
|
|
||||||
tweenContainer.endValue = 0;
|
|
||||||
tweenContainer.startValue = 360;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (/^backgroundPosition/.test(propertyName) && parseFloat(tweenContainer.endValue) === 100 && tweenContainer.unitType === '%') {
|
|
||||||
tweenContainer.endValue = 0;
|
|
||||||
tweenContainer.startValue = 100;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO!!! FIXME!!!
|
|
||||||
//Collide(element, 'reverse', { loop: true, delay: opts.delay });
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/***************
|
|
||||||
Dequeueing
|
|
||||||
***************/
|
|
||||||
|
|
||||||
/* 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 animation queue. */
|
|
||||||
if (opts.queue !== false) {
|
|
||||||
Collide.dequeue(element, opts.queue);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // END: for (var i = 0, callLength = call.length; i < callLength; i++)
|
|
||||||
|
|
||||||
|
|
||||||
/***********************
|
|
||||||
Calls Array Cleanup
|
|
||||||
************************/
|
|
||||||
|
|
||||||
/* Since this call is complete, set it to false so that the rAF tick skips it. This array is later compacted via compactSparseArray().
|
|
||||||
(For performance reasons, the call is set to false instead of being deleted from the array: http://www.html5rocks.com/en/tutorials/speed/v8/) */
|
|
||||||
Collide.State.calls[callIndex] = false;
|
|
||||||
|
|
||||||
/* Iterate through the calls array to determine if this was the final in-progress animation.
|
|
||||||
If so, set a flag to end ticking and clear the calls array. */
|
|
||||||
for (var j = 0, jj = Collide.State.calls.length; j < jj; j++) {
|
|
||||||
if (Collide.State.calls[j] !== false && Collide.State.calls[j][0]) {
|
|
||||||
remainingCallsExist = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (remainingCallsExist === false) {
|
|
||||||
/* tick() will detect this flag upon its next iteration and subsequently turn itself off. */
|
|
||||||
Collide.State.isTicking = false;
|
|
||||||
|
|
||||||
/* Clear the calls array so that its length is reset. */
|
|
||||||
delete Collide.State.calls;
|
|
||||||
Collide.State.calls = [];
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,915 +0,0 @@
|
|||||||
/* Forked from VelocityJS, MIT License: https://github.com/julianshapiro/velocity | Julian Shapiro http://twitter.com/shapiro */
|
|
||||||
|
|
||||||
import {Collide} from './collide'
|
|
||||||
|
|
||||||
const data = Collide.data;
|
|
||||||
|
|
||||||
|
|
||||||
/*****************
|
|
||||||
CSS Stack
|
|
||||||
*****************/
|
|
||||||
|
|
||||||
/* The CSS object handles the validation, getting, and setting of both standard
|
|
||||||
CSS properties and CSS property hooks. */
|
|
||||||
export var CSS = {
|
|
||||||
|
|
||||||
|
|
||||||
/*************
|
|
||||||
CSS RegEx
|
|
||||||
*************/
|
|
||||||
|
|
||||||
RegEx: {
|
|
||||||
isHex: /^#([A-f\d]{3}){1,2}$/i,
|
|
||||||
|
|
||||||
/* Unwrap a property value's surrounding text, e.g. 'rgba(4, 3, 2, 1)' ==> '4, 3, 2, 1' and 'rect(4px 3px 2px 1px)' ==> '4px 3px 2px 1px'. */
|
|
||||||
valueUnwrap: /^[A-z]+\((.*)\)$/i,
|
|
||||||
|
|
||||||
wrappedValueAlreadyExtracted: /[0-9.]+ [0-9.]+ [0-9.]+( [0-9.]+)?/,
|
|
||||||
|
|
||||||
/* Split a multi-value property into an array of subvalues, e.g. 'rgba(4, 3, 2, 1) 4px 3px 2px 1px' ==> [ 'rgba(4, 3, 2, 1)', '4px', '3px', '2px', '1px' ]. */
|
|
||||||
valueSplit: /([A-z]+\(.+\))|(([A-z0-9#-.]+?)(?=\s|$))/ig
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
/************
|
|
||||||
CSS Lists
|
|
||||||
************/
|
|
||||||
|
|
||||||
Lists: {
|
|
||||||
colors: [ 'fill', 'stroke', 'stopColor', 'color', 'backgroundColor', 'borderColor', 'borderTopColor', 'borderRightColor', 'borderBottomColor', 'borderLeftColor', 'outlineColor' ],
|
|
||||||
transformsBase: [ 'translateX', 'translateY', 'scale', 'scaleX', 'scaleY', 'skewX', 'skewY', 'rotateZ' ],
|
|
||||||
transforms3D: [ 'transformPerspective', 'translateZ', 'scaleZ', 'rotateX', 'rotateY' ]
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
/************
|
|
||||||
CSS Hooks
|
|
||||||
************/
|
|
||||||
|
|
||||||
/* Hooks allow a subproperty (e.g. 'boxShadowBlur') of a compound-value CSS property
|
|
||||||
(e.g. 'boxShadow: X Y Blur Spread Color') to be animated as if it were a discrete property. */
|
|
||||||
/* Note: Beyond enabling fine-grained property animation, hooking is necessary since Collide only
|
|
||||||
tweens properties with single numeric values; unlike CSS transitions, Collide does not interpolate compound-values. */
|
|
||||||
Hooks: {
|
|
||||||
|
|
||||||
/********************
|
|
||||||
CSS Hook Registration
|
|
||||||
********************/
|
|
||||||
|
|
||||||
/* Templates are a concise way of indicating which subproperties must be individually registered for each compound-value CSS property. */
|
|
||||||
/* Each template consists of the compound-value's base name, its constituent subproperty names, and those subproperties' default values. */
|
|
||||||
templates: {
|
|
||||||
textShadow: [ 'Color X Y Blur', 'black 0px 0px 0px' ],
|
|
||||||
boxShadow: [ 'Color X Y Blur Spread', 'black 0px 0px 0px 0px' ],
|
|
||||||
clip: [ 'Top Right Bottom Left', '0px 0px 0px 0px' ],
|
|
||||||
backgroundPosition: [ 'X Y', '0% 0%' ],
|
|
||||||
transformOrigin: [ 'X Y Z', '50% 50% 0px' ],
|
|
||||||
perspectiveOrigin: [ 'X Y', '50% 50%' ]
|
|
||||||
},
|
|
||||||
|
|
||||||
/* A 'registered' hook is one that has been converted from its template form into a live,
|
|
||||||
tweenable property. It contains data to associate it with its root property. */
|
|
||||||
registered: {
|
|
||||||
/* Note: A registered hook looks like this ==> textShadowBlur: [ 'textShadow', 3 ],
|
|
||||||
which consists of the subproperty's name, the associated root property's name,
|
|
||||||
and the subproperty's position in the root's value. */
|
|
||||||
},
|
|
||||||
|
|
||||||
/* Convert the templates into individual hooks then append them to the registered object above. */
|
|
||||||
register: function() {
|
|
||||||
/* Color hooks registration: Colors are defaulted to white -- as opposed to black -- since colors that are
|
|
||||||
currently set to 'transparent' default to their respective template below when color-animated,
|
|
||||||
and white is typically a closer match to transparent than black is. An exception is made for text ('color'),
|
|
||||||
which is almost always set closer to black than white. */
|
|
||||||
for (var i = 0; i < CSS.Lists.colors.length; i++) {
|
|
||||||
var rgbComponents = (CSS.Lists.colors[i] === 'color') ? '0 0 0 1' : '255 255 255 1';
|
|
||||||
CSS.Hooks.templates[CSS.Lists.colors[i]] = [ 'Red Green Blue Alpha', rgbComponents ];
|
|
||||||
}
|
|
||||||
|
|
||||||
var rootProperty,
|
|
||||||
hookTemplate,
|
|
||||||
hookNames;
|
|
||||||
|
|
||||||
/* Hook registration. */
|
|
||||||
for (rootProperty in CSS.Hooks.templates) {
|
|
||||||
hookTemplate = CSS.Hooks.templates[rootProperty];
|
|
||||||
hookNames = hookTemplate[0].split(' ');
|
|
||||||
|
|
||||||
for (var i in hookNames) {
|
|
||||||
var fullHookName = rootProperty + hookNames[i],
|
|
||||||
hookPosition = i;
|
|
||||||
|
|
||||||
/* For each hook, register its full name (e.g. textShadowBlur) with its root property (e.g. textShadow)
|
|
||||||
and the hook's position in its template's default value string. */
|
|
||||||
CSS.Hooks.registered[fullHookName] = [ rootProperty, hookPosition ];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************
|
|
||||||
CSS Hook Injection and Extraction
|
|
||||||
*****************************/
|
|
||||||
|
|
||||||
/* Look up the root property associated with the hook (e.g. return 'textShadow' for 'textShadowBlur'). */
|
|
||||||
/* Since a hook cannot be set directly (the browser won't recognize it), style updating for hooks is routed through the hook's root property. */
|
|
||||||
getRoot: function(property) {
|
|
||||||
var hookData = CSS.Hooks.registered[property];
|
|
||||||
|
|
||||||
if (hookData) {
|
|
||||||
return hookData[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If there was no hook match, return the property name untouched. */
|
|
||||||
return property;
|
|
||||||
},
|
|
||||||
|
|
||||||
/* Convert any rootPropertyValue, null or otherwise, into a space-delimited list of hook values so that
|
|
||||||
the targeted hook can be injected or extracted at its standard position. */
|
|
||||||
cleanRootPropertyValue: function(rootProperty, rootPropertyValue) {
|
|
||||||
/* If the rootPropertyValue is wrapped with 'rgb()', 'clip()', etc., remove the wrapping to normalize the value before manipulation. */
|
|
||||||
if (CSS.RegEx.valueUnwrap.test(rootPropertyValue)) {
|
|
||||||
rootPropertyValue = rootPropertyValue.match(CSS.RegEx.valueUnwrap)[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If rootPropertyValue is a CSS null-value (from which there's inherently no hook value to extract),
|
|
||||||
default to the root's default value as defined in CSS.Hooks.templates. */
|
|
||||||
/* Note: CSS null-values include 'none', 'auto', and 'transparent'. They must be converted into their
|
|
||||||
zero-values (e.g. textShadow: 'none' ==> textShadow: '0px 0px 0px black') for hook manipulation to proceed. */
|
|
||||||
if (CSS.Values.isCSSNullValue(rootPropertyValue)) {
|
|
||||||
rootPropertyValue = CSS.Hooks.templates[rootProperty][1];
|
|
||||||
}
|
|
||||||
|
|
||||||
return rootPropertyValue;
|
|
||||||
},
|
|
||||||
|
|
||||||
/* Extracted the hook's value from its root property's value. This is used to get the starting value of an animating hook. */
|
|
||||||
extractValue: function(fullHookName, rootPropertyValue) {
|
|
||||||
var hookData = CSS.Hooks.registered[fullHookName];
|
|
||||||
|
|
||||||
if (hookData) {
|
|
||||||
var hookRoot = hookData[0],
|
|
||||||
hookPosition = hookData[1];
|
|
||||||
|
|
||||||
rootPropertyValue = CSS.Hooks.cleanRootPropertyValue(hookRoot, rootPropertyValue);
|
|
||||||
|
|
||||||
/* Split rootPropertyValue into its constituent hook values then grab the desired hook at its standard position. */
|
|
||||||
return rootPropertyValue.toString().match(CSS.RegEx.valueSplit)[hookPosition];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If the provided fullHookName isn't a registered hook, return the rootPropertyValue that was passed in. */
|
|
||||||
return rootPropertyValue;
|
|
||||||
},
|
|
||||||
|
|
||||||
/* Inject the hook's value into its root property's value. This is used to piece back together the root property
|
|
||||||
once Collide has updated one of its individually hooked values through tweening. */
|
|
||||||
injectValue: function(fullHookName, hookValue, rootPropertyValue) {
|
|
||||||
var hookData = CSS.Hooks.registered[fullHookName];
|
|
||||||
|
|
||||||
if (hookData) {
|
|
||||||
var hookRoot = hookData[0],
|
|
||||||
hookPosition = hookData[1],
|
|
||||||
rootPropertyValueParts,
|
|
||||||
rootPropertyValueUpdated;
|
|
||||||
|
|
||||||
rootPropertyValue = CSS.Hooks.cleanRootPropertyValue(hookRoot, rootPropertyValue);
|
|
||||||
|
|
||||||
/* Split rootPropertyValue into its individual hook values, replace the targeted value with hookValue,
|
|
||||||
then reconstruct the rootPropertyValue string. */
|
|
||||||
rootPropertyValueParts = rootPropertyValue.toString().match(CSS.RegEx.valueSplit);
|
|
||||||
rootPropertyValueParts[hookPosition] = hookValue;
|
|
||||||
rootPropertyValueUpdated = rootPropertyValueParts.join(' ');
|
|
||||||
|
|
||||||
return rootPropertyValueUpdated;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If the provided fullHookName isn't a registered hook, return the rootPropertyValue that was passed in. */
|
|
||||||
return rootPropertyValue;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
/*******************
|
|
||||||
CSS Normalizations
|
|
||||||
*******************/
|
|
||||||
|
|
||||||
/* Normalizations standardize CSS property manipulation by pollyfilling browser-specific implementations (e.g. opacity)
|
|
||||||
and reformatting special properties (e.g. clip, rgba) to look like standard ones. */
|
|
||||||
Normalizations: {
|
|
||||||
|
|
||||||
/* Normalizations are passed a normalization target (either the property's name, its extracted value, or its injected value),
|
|
||||||
the targeted element (which may need to be queried), and the targeted property value. */
|
|
||||||
registered: {
|
|
||||||
clip: function(type, element, propertyValue) {
|
|
||||||
switch (type) {
|
|
||||||
|
|
||||||
case 'name':
|
|
||||||
return 'clip';
|
|
||||||
|
|
||||||
/* Clip needs to be unwrapped and stripped of its commas during extraction. */
|
|
||||||
case 'extract':
|
|
||||||
var extracted;
|
|
||||||
|
|
||||||
/* If Collide also extracted this value, skip extraction. */
|
|
||||||
if (CSS.RegEx.wrappedValueAlreadyExtracted.test(propertyValue)) {
|
|
||||||
extracted = propertyValue;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
/* Remove the 'rect()' wrapper. */
|
|
||||||
extracted = propertyValue.toString().match(CSS.RegEx.valueUnwrap);
|
|
||||||
|
|
||||||
/* Strip off commas. */
|
|
||||||
extracted = extracted ? extracted[1].replace(/,(\s+)?/g, ' ') : propertyValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return extracted;
|
|
||||||
|
|
||||||
/* Clip needs to be re-wrapped during injection. */
|
|
||||||
case 'inject':
|
|
||||||
return 'rect(' + propertyValue + ')';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
blur: function(type, element, propertyValue) {
|
|
||||||
switch (type) {
|
|
||||||
|
|
||||||
case 'name':
|
|
||||||
return '-webkit-filter';
|
|
||||||
|
|
||||||
case 'extract':
|
|
||||||
var extracted = parseFloat(propertyValue);
|
|
||||||
|
|
||||||
/* If extracted is NaN, meaning the value isn't already extracted. */
|
|
||||||
if (!(extracted || extracted === 0)) {
|
|
||||||
var blurComponent = propertyValue.toString().match(/blur\(([0-9]+[A-z]+)\)/i);
|
|
||||||
|
|
||||||
/* If the filter string had a blur component, return just the blur value and unit type. */
|
|
||||||
if (blurComponent) {
|
|
||||||
extracted = blurComponent[1];
|
|
||||||
|
|
||||||
/* If the component doesn't exist, default blur to 0. */
|
|
||||||
} else {
|
|
||||||
extracted = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return extracted;
|
|
||||||
|
|
||||||
/* Blur needs to be re-wrapped during injection. */
|
|
||||||
case 'inject':
|
|
||||||
/* For the blur effect to be fully de-applied, it needs to be set to 'none' instead of 0. */
|
|
||||||
if (!parseFloat(propertyValue)) {
|
|
||||||
return 'none';
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'blur(' + propertyValue + ')';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
opacity: function(type, element, propertyValue) {
|
|
||||||
switch (type) {
|
|
||||||
case 'name':
|
|
||||||
return 'opacity';
|
|
||||||
|
|
||||||
case 'extract':
|
|
||||||
return propertyValue;
|
|
||||||
|
|
||||||
case 'inject':
|
|
||||||
return propertyValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************
|
|
||||||
CSS Batched Registrations
|
|
||||||
*****************************/
|
|
||||||
|
|
||||||
/* Note: Batched normalizations extend the CSS.Normalizations.registered object. */
|
|
||||||
register: function() {
|
|
||||||
|
|
||||||
/****************************************
|
|
||||||
CSS Batched Registration Transforms
|
|
||||||
****************************************/
|
|
||||||
|
|
||||||
/* Transforms are the subproperties contained by the CSS 'transform' property. Transforms must undergo normalization
|
|
||||||
so that they can be referenced in a properties map by their individual names. */
|
|
||||||
/* Note: When transforms are 'set', they are actually assigned to a per-element transformCache. When all transform
|
|
||||||
setting is complete complete, CSS.flushTransformCache() must be manually called to flush the values to the DOM.
|
|
||||||
Transform setting is batched in this way to improve performance: the transform style only needs to be updated
|
|
||||||
once when multiple transform subproperties are being animated simultaneously. */
|
|
||||||
|
|
||||||
for (var i = 0; i < CSS.Lists.transformsBase.length; i++) {
|
|
||||||
/* Wrap the dynamically generated normalization function in a new scope so that transformName's value is
|
|
||||||
paired with its respective function. (Otherwise, all functions would take the final for loop's transformName.) */
|
|
||||||
(function() {
|
|
||||||
var transformName = CSS.Lists.transformsBase[i];
|
|
||||||
|
|
||||||
CSS.Normalizations.registered[transformName] = function(type, element, propertyValue) {
|
|
||||||
var eleData = data(element);
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
|
|
||||||
/* The normalized property name is the parent 'transform' property -- the property that is actually set in CSS. */
|
|
||||||
case 'name':
|
|
||||||
return 'transform';
|
|
||||||
|
|
||||||
/* Transform values are cached onto a per-element transformCache object. */
|
|
||||||
case 'extract':
|
|
||||||
/* If this transform has yet to be assigned a value, return its null value. */
|
|
||||||
if (eleData === undefined || eleData.transformCache[transformName] === undefined) {
|
|
||||||
/* Scale CSS.Lists.transformsBase default to 1 whereas all other transform properties default to 0. */
|
|
||||||
return /^scale/i.test(transformName) ? 1 : 0;
|
|
||||||
}
|
|
||||||
/* When transform values are set, they are wrapped in parentheses as per the CSS spec.
|
|
||||||
Thus, when extracting their values (for tween calculations), we strip off the parentheses. */
|
|
||||||
return eleData.transformCache[transformName].replace(/[()]/g, '');
|
|
||||||
|
|
||||||
case 'inject':
|
|
||||||
var invalid = false;
|
|
||||||
|
|
||||||
/* If an individual transform property contains an unsupported unit type, the browser ignores the *entire* transform property.
|
|
||||||
Thus, protect users from themselves by skipping setting for transform values supplied with invalid unit types. */
|
|
||||||
/* Switch on the base transform type; ignore the axis by removing the last letter from the transform's name. */
|
|
||||||
switch (transformName.substr(0, transformName.length - 1)) {
|
|
||||||
/* Whitelist unit types for each transform. */
|
|
||||||
case 'translate':
|
|
||||||
invalid = !/(%|px|em|rem|vw|vh|\d)$/i.test(propertyValue);
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* Since an axis-free 'scale' property is supported as well, a little hack is used here to detect it by chopping off its last letter. */
|
|
||||||
case 'scal':
|
|
||||||
case 'scale':
|
|
||||||
invalid = !/(\d)$/i.test(propertyValue);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'skew':
|
|
||||||
invalid = !/(deg|\d)$/i.test(propertyValue);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'rotate':
|
|
||||||
invalid = !/(deg|\d)$/i.test(propertyValue);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!invalid) {
|
|
||||||
/* As per the CSS spec, wrap the value in parentheses. */
|
|
||||||
eleData.transformCache[transformName] = '(' + propertyValue + ')';
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Although the value is set on the transformCache object, return the newly-updated value for the calling code to process as normal. */
|
|
||||||
return eleData.transformCache[transformName];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
}
|
|
||||||
|
|
||||||
/************************************
|
|
||||||
CSS Batched Registration Colors
|
|
||||||
************************************/
|
|
||||||
|
|
||||||
/* Since Collide only animates a single numeric value per property, color animation is achieved by hooking the individual RGBA components of CSS color properties.
|
|
||||||
Accordingly, color values must be normalized (e.g. '#ff0000', 'red', and 'rgb(255, 0, 0)' ==> '255 0 0 1') so that their components can be injected/extracted by CSS.Hooks logic. */
|
|
||||||
for (var i = 0; i < CSS.Lists.colors.length; i++) {
|
|
||||||
/* Wrap the dynamically generated normalization function in a new scope so that colorName's value is paired with its respective function.
|
|
||||||
(Otherwise, all functions would take the final for loop's colorName.) */
|
|
||||||
(function() {
|
|
||||||
var colorName = CSS.Lists.colors[i];
|
|
||||||
|
|
||||||
/* Note: In IE<=8, which support rgb but not rgba, color properties are reverted to rgb by stripping off the alpha component. */
|
|
||||||
CSS.Normalizations.registered[colorName] = function(type, element, propertyValue) {
|
|
||||||
switch (type) {
|
|
||||||
|
|
||||||
case 'name':
|
|
||||||
return colorName;
|
|
||||||
|
|
||||||
/* Convert all color values into the rgb format. (Old IE can return hex values and color names instead of rgb/rgba.) */
|
|
||||||
case 'extract':
|
|
||||||
var extracted;
|
|
||||||
|
|
||||||
/* If the color is already in its hookable form (e.g. '255 255 255 1') due to having been previously extracted, skip extraction. */
|
|
||||||
if (CSS.RegEx.wrappedValueAlreadyExtracted.test(propertyValue)) {
|
|
||||||
extracted = propertyValue;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
var converted,
|
|
||||||
colorNames = {
|
|
||||||
black: 'rgb(0, 0, 0)',
|
|
||||||
blue: 'rgb(0, 0, 255)',
|
|
||||||
gray: 'rgb(128, 128, 128)',
|
|
||||||
green: 'rgb(0, 128, 0)',
|
|
||||||
red: 'rgb(255, 0, 0)',
|
|
||||||
white: 'rgb(255, 255, 255)'
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Convert color names to rgb. */
|
|
||||||
if (/^[A-z]+$/i.test(propertyValue)) {
|
|
||||||
if (colorNames[propertyValue] !== undefined) {
|
|
||||||
converted = colorNames[propertyValue]
|
|
||||||
|
|
||||||
} else {
|
|
||||||
/* If an unmatched color name is provided, default to black. */
|
|
||||||
converted = colorNames.black;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Convert hex values to rgb. */
|
|
||||||
} else if (CSS.RegEx.isHex.test(propertyValue)) {
|
|
||||||
converted = 'rgb(' + CSS.Values.hexToRgb(propertyValue).join(' ') + ')';
|
|
||||||
|
|
||||||
/* If the provided color doesn't match any of the accepted color formats, default to black. */
|
|
||||||
} else if (!(/^rgba?\(/i.test(propertyValue))) {
|
|
||||||
converted = colorNames.black;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remove the surrounding 'rgb/rgba()' string then replace commas with spaces and strip
|
|
||||||
repeated spaces (in case the value included spaces to begin with). */
|
|
||||||
extracted = (converted || propertyValue).toString().match(CSS.RegEx.valueUnwrap)[1].replace(/,(\s+)?/g, ' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
/* add a fourth (alpha) component if it's missing and default it to 1 (visible). */
|
|
||||||
if (extracted.split(' ').length === 3) {
|
|
||||||
extracted += ' 1';
|
|
||||||
}
|
|
||||||
|
|
||||||
return extracted;
|
|
||||||
|
|
||||||
case 'inject':
|
|
||||||
/* add a fourth (alpha) component if it's missing and default it to 1 (visible). */
|
|
||||||
if (propertyValue.split(' ').length === 3) {
|
|
||||||
propertyValue += ' 1';
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Re-insert the browser-appropriate wrapper('rgb/rgba()'), insert commas, and strip off decimal units
|
|
||||||
on all values but the fourth (R, G, and B only accept whole numbers). */
|
|
||||||
return 'rgba(' + propertyValue.replace(/\s+/g, ',').replace(/\.(\d)+(?=,)/g, '') + ')';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
/************************
|
|
||||||
CSS Property Names
|
|
||||||
************************/
|
|
||||||
|
|
||||||
Names: {
|
|
||||||
/* Camelcase a property name into its JavaScript notation (e.g. 'background-color' ==> 'backgroundColor').
|
|
||||||
Camelcasing is used to normalize property names between and across calls. */
|
|
||||||
camelCase: function(property) {
|
|
||||||
return property.replace(/-(\w)/g, function(match, subMatch) {
|
|
||||||
return subMatch.toUpperCase();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/* For SVG elements, some properties (namely, dimensional ones) are GET/SET via the element's HTML attributes (instead of via CSS styles). */
|
|
||||||
SVGAttribute: function(property) {
|
|
||||||
return new RegExp('^(width|height|x|y|cx|cy|r|rx|ry|x1|x2|y1|y2)$', 'i').test(property);
|
|
||||||
},
|
|
||||||
|
|
||||||
/* Determine whether a property should be set with a vendor prefix. */
|
|
||||||
/* If a prefixed version of the property exists, return it. Otherwise, return the original property name.
|
|
||||||
If the property is not at all supported by the browser, return a false flag. */
|
|
||||||
prefixCheck: function(property) {
|
|
||||||
/* If this property has already been checked, return the cached value. */
|
|
||||||
if (CSS.Names.prefixMatches[property]) {
|
|
||||||
return [ CSS.Names.prefixMatches[property], true ];
|
|
||||||
|
|
||||||
} else {
|
|
||||||
for (var i = 0, vendorsLength = vendorPrefixes.length; i < vendorsLength; i++) {
|
|
||||||
var propertyPrefixed;
|
|
||||||
|
|
||||||
if (i === 0) {
|
|
||||||
propertyPrefixed = property;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
/* Capitalize the first letter of the property to conform to JavaScript vendor prefix notation (e.g. webkitFilter). */
|
|
||||||
propertyPrefixed = vendorPrefixes[i] + property.replace(/^\w/, function(match) { return match.toUpperCase(); });
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if the browser supports this property as prefixed. */
|
|
||||||
if (typeof Collide.State.prefixElement.style[propertyPrefixed] === 'string') {
|
|
||||||
/* Cache the match. */
|
|
||||||
CSS.Names.prefixMatches[property] = propertyPrefixed;
|
|
||||||
|
|
||||||
return [ propertyPrefixed, true ];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If the browser doesn't support this property in any form, include a false flag so that the caller can decide how to proceed. */
|
|
||||||
return [ property, false ];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/* cached property name prefixes */
|
|
||||||
prefixMatches: {}
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
/************************
|
|
||||||
CSS Property Values
|
|
||||||
************************/
|
|
||||||
|
|
||||||
Values: {
|
|
||||||
/* Hex to RGB conversion. Copyright Tim Down: http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb */
|
|
||||||
hexToRgb: function(hex) {
|
|
||||||
var shortformRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i,
|
|
||||||
longformRegex = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i,
|
|
||||||
rgbParts;
|
|
||||||
|
|
||||||
hex = hex.replace(shortformRegex, function(m, r, g, b) {
|
|
||||||
return r + r + g + g + b + b;
|
|
||||||
});
|
|
||||||
|
|
||||||
rgbParts = longformRegex.exec(hex);
|
|
||||||
|
|
||||||
return rgbParts ? [ parseInt(rgbParts[1], 16), parseInt(rgbParts[2], 16), parseInt(rgbParts[3], 16) ] : [ 0, 0, 0 ];
|
|
||||||
},
|
|
||||||
|
|
||||||
isCSSNullValue: function(value) {
|
|
||||||
/* The browser defaults CSS values that have not been set to either 0 or one of several possible null-value strings.
|
|
||||||
Thus, we check for both falsiness and these special strings. */
|
|
||||||
/* Null-value checking is performed to default the special strings to 0 (for the sake of tweening) or their hook
|
|
||||||
templates as defined as CSS.Hooks (for the sake of hook injection/extraction). */
|
|
||||||
/* Note: Chrome returns 'rgba(0, 0, 0, 0)' for an undefined color whereas IE returns 'transparent'. */
|
|
||||||
return (value == 0 || /^(none|auto|transparent|(rgba\(0, ?0, ?0, ?0\)))$/i.test(value));
|
|
||||||
},
|
|
||||||
|
|
||||||
/* Retrieve a property's default unit type. Used for assigning a unit type when one is not supplied by the user. */
|
|
||||||
getUnitType: function(property) {
|
|
||||||
if (/^(rotate|skew)/i.test(property)) {
|
|
||||||
return 'deg';
|
|
||||||
|
|
||||||
} else if (/(^(scale|scaleX|scaleY|scaleZ|alpha|flexGrow|flexHeight|zIndex|fontWeight)$)|((opacity|red|green|blue|alpha)$)/i.test(property)) {
|
|
||||||
/* The above properties are unitless. */
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Default to px for all other properties. */
|
|
||||||
return 'px';
|
|
||||||
},
|
|
||||||
|
|
||||||
/* HTML elements default to an associated display type when they're not set to display:none. */
|
|
||||||
/* Note: This function is used for correctly setting the non-'none' display value in certain Collide redirects, such as fadeIn/Out. */
|
|
||||||
getDisplayType: function(element) {
|
|
||||||
var tagName = element && element.tagName && element.tagName.toString().toLowerCase();
|
|
||||||
|
|
||||||
if (/^(b|big|i|small|tt|abbr|acronym|cite|code|dfn|em|kbd|strong|samp|var|a|bdo|br|img|map|object|q|script|span|sub|sup|button|input|label|select|textarea)$/.test(tagName)) {
|
|
||||||
return 'inline';
|
|
||||||
|
|
||||||
} else if (/^(li)$/.test(tagName)) {
|
|
||||||
return 'list-item';
|
|
||||||
|
|
||||||
} else if (/^(tr)$/.test(tagName)) {
|
|
||||||
return 'table-row';
|
|
||||||
|
|
||||||
} else if (/^(table)$/.test(tagName)) {
|
|
||||||
return 'table';
|
|
||||||
|
|
||||||
} else if (/^(tbody)$/.test(tagName)) {
|
|
||||||
return 'table-row-group';
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Default to 'block' when no match is found. */
|
|
||||||
return 'block';
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
/****************************
|
|
||||||
CSS Style Getting & Setting
|
|
||||||
****************************/
|
|
||||||
|
|
||||||
/* The singular getPropertyValue, which routes the logic for all normalizations, hooks, and standard CSS properties. */
|
|
||||||
getPropertyValue: function(element, property, rootPropertyValue, forceStyleLookup) {
|
|
||||||
|
|
||||||
/* Get an element's computed property value. */
|
|
||||||
/* Note: Retrieving the value of a CSS property cannot simply be performed by checking an element's
|
|
||||||
style attribute (which only reflects user-defined values). Instead, the browser must be queried for a property's
|
|
||||||
*computed* value. You can read more about getComputedStyle here: https://developer.mozilla.org/en/docs/Web/API/window.getComputedStyle */
|
|
||||||
function computePropertyValue (element, property) {
|
|
||||||
/* When box-sizing isn't set to border-box, height and width style values are incorrectly computed when an
|
|
||||||
element's scrollbars are visible (which expands the element's dimensions). Thus, we defer to the more accurate
|
|
||||||
offsetHeight/Width property, which includes the total dimensions for interior, border, padding, and scrollbar.
|
|
||||||
We subtract border and padding to get the sum of interior + scrollbar. */
|
|
||||||
var computedValue = 0;
|
|
||||||
|
|
||||||
/* Browsers do not return height and width values for elements that are set to display:'none'. Thus, we temporarily
|
|
||||||
toggle display to the element type's default value. */
|
|
||||||
var toggleDisplay = false;
|
|
||||||
|
|
||||||
if (/^(width|height)$/.test(property) && CSS.getPropertyValue(element, 'display') === 0) {
|
|
||||||
toggleDisplay = true;
|
|
||||||
CSS.setPropertyValue(element, 'display', CSS.Values.getDisplayType(element));
|
|
||||||
}
|
|
||||||
|
|
||||||
function revertDisplay () {
|
|
||||||
if (toggleDisplay) {
|
|
||||||
CSS.setPropertyValue(element, 'display', 'none');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!forceStyleLookup) {
|
|
||||||
if (property === 'height' && CSS.getPropertyValue(element, 'boxSizing').toString().toLowerCase() !== 'border-box') {
|
|
||||||
var contentBoxHeight = element.offsetHeight - (parseFloat(CSS.getPropertyValue(element, 'borderTopWidth')) || 0) - (parseFloat(CSS.getPropertyValue(element, 'borderBottomWidth')) || 0) - (parseFloat(CSS.getPropertyValue(element, 'paddingTop')) || 0) - (parseFloat(CSS.getPropertyValue(element, 'paddingBottom')) || 0);
|
|
||||||
revertDisplay();
|
|
||||||
|
|
||||||
return contentBoxHeight;
|
|
||||||
|
|
||||||
} else if (property === 'width' && CSS.getPropertyValue(element, 'boxSizing').toString().toLowerCase() !== 'border-box') {
|
|
||||||
var contentBoxWidth = element.offsetWidth - (parseFloat(CSS.getPropertyValue(element, 'borderLeftWidth')) || 0) - (parseFloat(CSS.getPropertyValue(element, 'borderRightWidth')) || 0) - (parseFloat(CSS.getPropertyValue(element, 'paddingLeft')) || 0) - (parseFloat(CSS.getPropertyValue(element, 'paddingRight')) || 0);
|
|
||||||
revertDisplay();
|
|
||||||
|
|
||||||
return contentBoxWidth;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var computedStyle;
|
|
||||||
var eleData = data(element);
|
|
||||||
|
|
||||||
/* For elements that Collide hasn't been called on directly (e.g. when Collide queries the DOM on behalf
|
|
||||||
of a parent of an element its animating), perform a direct getComputedStyle lookup since the object isn't cached. */
|
|
||||||
if (eleData === undefined) {
|
|
||||||
computedStyle = window.getComputedStyle(element, null); /* GET */
|
|
||||||
|
|
||||||
/* If the computedStyle object has yet to be cached, do so now. */
|
|
||||||
} else if (!eleData.computedStyle) {
|
|
||||||
computedStyle = eleData.computedStyle = window.getComputedStyle(element, null); /* GET */
|
|
||||||
|
|
||||||
/* If computedStyle is cached, use it. */
|
|
||||||
} else {
|
|
||||||
computedStyle = eleData.computedStyle;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* IE and Firefox do not return a value for the generic borderColor -- they only return individual values for each border side's color.
|
|
||||||
Also, in all browsers, when border colors aren't all the same, a compound value is returned that isn't setup to parse.
|
|
||||||
So, as a polyfill for querying individual border side colors, we just return the top border's color and animate all borders from that value. */
|
|
||||||
if (property === 'borderColor') {
|
|
||||||
property = 'borderTopColor';
|
|
||||||
}
|
|
||||||
|
|
||||||
computedValue = computedStyle[property];
|
|
||||||
|
|
||||||
/* Fall back to the property's style value (if defined) when computedValue returns nothing,
|
|
||||||
which can happen when the element hasn't been painted. */
|
|
||||||
if (computedValue === '' || computedValue === null) {
|
|
||||||
computedValue = element.style[property];
|
|
||||||
}
|
|
||||||
|
|
||||||
revertDisplay();
|
|
||||||
|
|
||||||
/* For top, right, bottom, and left (TRBL) values that are set to 'auto' on elements of 'fixed' or 'absolute' position,
|
|
||||||
defer to jQuery for converting 'auto' to a numeric value. (For elements with a 'static' or 'relative' position, 'auto' has the same
|
|
||||||
effect as being set to 0, so no conversion is necessary.) */
|
|
||||||
/* An example of why numeric conversion is necessary: When an element with 'position:absolute' has an untouched 'left'
|
|
||||||
property, which reverts to 'auto', left's value is 0 relative to its parent element, but is often non-zero relative
|
|
||||||
to its *containing* (not parent) element, which is the nearest 'position:relative' ancestor or the viewport (and always the viewport in the case of 'position:fixed'). */
|
|
||||||
if (computedValue === 'auto' && /^(top|right|bottom|left)$/i.test(property)) {
|
|
||||||
var position = computePropertyValue(element, 'position'); /* GET */
|
|
||||||
|
|
||||||
/* For absolute positioning, CSS.position(element) only returns values for top and left;
|
|
||||||
right and bottom will have their 'auto' value reverted to 0. */
|
|
||||||
if (position === 'fixed' || (position === 'absolute' && /top|left/i.test(property))) {
|
|
||||||
computedValue = CSS.position(element)[property] + 'px'; /* GET */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return computedValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var propertyValue;
|
|
||||||
|
|
||||||
/* If this is a hooked property (e.g. 'clipLeft' instead of the root property of 'clip'),
|
|
||||||
extract the hook's value from a normalized rootPropertyValue using CSS.Hooks.extractValue(). */
|
|
||||||
if (CSS.Hooks.registered[property]) {
|
|
||||||
var hook = property,
|
|
||||||
hookRoot = CSS.Hooks.getRoot(hook);
|
|
||||||
|
|
||||||
/* If a cached rootPropertyValue wasn't passed in (which Collide always attempts to do in order to avoid requerying the DOM),
|
|
||||||
query the DOM for the root property's value. */
|
|
||||||
if (rootPropertyValue === undefined) {
|
|
||||||
/* Since the browser is now being directly queried, use the official post-prefixing property name for this lookup. */
|
|
||||||
rootPropertyValue = CSS.getPropertyValue(element, CSS.Names.prefixCheck(hookRoot)[0]); /* GET */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If this root has a normalization registered, peform the associated normalization extraction. */
|
|
||||||
if (CSS.Normalizations.registered[hookRoot]) {
|
|
||||||
rootPropertyValue = CSS.Normalizations.registered[hookRoot]('extract', element, rootPropertyValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Extract the hook's value. */
|
|
||||||
propertyValue = CSS.Hooks.extractValue(hook, rootPropertyValue);
|
|
||||||
|
|
||||||
/* If this is a normalized property (e.g. 'opacity' becomes 'filter' in <=IE8) or 'translateX' becomes 'transform'),
|
|
||||||
normalize the property's name and value, and handle the special case of transforms. */
|
|
||||||
/* Note: Normalizing a property is mutually exclusive from hooking a property since hook-extracted values are strictly
|
|
||||||
numerical and therefore do not require normalization extraction. */
|
|
||||||
} else if (CSS.Normalizations.registered[property]) {
|
|
||||||
var normalizedPropertyName,
|
|
||||||
normalizedPropertyValue;
|
|
||||||
|
|
||||||
normalizedPropertyName = CSS.Normalizations.registered[property]('name', element);
|
|
||||||
|
|
||||||
/* Transform values are calculated via normalization extraction (see below), which checks against the element's transformCache.
|
|
||||||
At no point do transform GETs ever actually query the DOM; initial stylesheet values are never processed.
|
|
||||||
This is because parsing 3D transform matrices is not always accurate and would bloat our codebase;
|
|
||||||
thus, normalization extraction defaults initial transform values to their zero-values (e.g. 1 for scaleX and 0 for translateX). */
|
|
||||||
if (normalizedPropertyName !== 'transform') {
|
|
||||||
normalizedPropertyValue = computePropertyValue(element, CSS.Names.prefixCheck(normalizedPropertyName)[0]); /* GET */
|
|
||||||
|
|
||||||
/* If the value is a CSS null-value and this property has a hook template, use that zero-value template so that hooks can be extracted from it. */
|
|
||||||
if (CSS.Values.isCSSNullValue(normalizedPropertyValue) && CSS.Hooks.templates[property]) {
|
|
||||||
normalizedPropertyValue = CSS.Hooks.templates[property][1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
propertyValue = CSS.Normalizations.registered[property]('extract', element, normalizedPropertyValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If a (numeric) value wasn't produced via hook extraction or normalization, query the DOM. */
|
|
||||||
if (!/^[\d-]/.test(propertyValue)) {
|
|
||||||
/* For SVG elements, dimensional properties (which SVGAttribute() detects) are tweened via
|
|
||||||
their HTML attribute values instead of their CSS style values. */
|
|
||||||
var eleData = data(element);
|
|
||||||
if (eleData && eleData.isSVG && CSS.Names.SVGAttribute(property)) {
|
|
||||||
/* Since the height/width attribute values must be set manually, they don't reflect computed values.
|
|
||||||
Thus, we use use getBBox() to ensure we always get values for elements with undefined height/width attributes. */
|
|
||||||
if (/^(height|width)$/i.test(property)) {
|
|
||||||
/* Firefox throws an error if .getBBox() is called on an SVG that isn't attached to the DOM. */
|
|
||||||
try {
|
|
||||||
propertyValue = element.getBBox()[property];
|
|
||||||
} catch (error) {
|
|
||||||
propertyValue = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Otherwise, access the attribute value directly. */
|
|
||||||
} else {
|
|
||||||
propertyValue = element.getAttribute(property);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
propertyValue = computePropertyValue(element, CSS.Names.prefixCheck(property)[0]); /* GET */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Since property lookups are for animation purposes (which entails computing the numeric delta between start and end values),
|
|
||||||
convert CSS null-values to an integer of value 0. */
|
|
||||||
if (CSS.Values.isCSSNullValue(propertyValue)) {
|
|
||||||
propertyValue = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Collide.debug >= 2) console.log('Get ' + property + ': ' + propertyValue);
|
|
||||||
|
|
||||||
return propertyValue;
|
|
||||||
},
|
|
||||||
|
|
||||||
/* The singular setPropertyValue, which routes the logic for all normalizations, hooks, and standard CSS properties. */
|
|
||||||
setPropertyValue: function(element, property, propertyValue, rootPropertyValue, scrollData) {
|
|
||||||
var propertyName = property;
|
|
||||||
|
|
||||||
/* In order to be subjected to call options and element queueing, scroll animation is routed through Collie as if it were a standard CSS property. */
|
|
||||||
if (property === 'scroll') {
|
|
||||||
/* If a container option is present, scroll the container instead of the browser window. */
|
|
||||||
if (scrollData.container) {
|
|
||||||
scrollData.container['scroll' + scrollData.direction] = propertyValue;
|
|
||||||
|
|
||||||
/* Otherwise, Collide defaults to scrolling the browser window. */
|
|
||||||
} else {
|
|
||||||
if (scrollData.direction === 'Left') {
|
|
||||||
window.scrollTo(propertyValue, scrollData.alternateValue);
|
|
||||||
} else {
|
|
||||||
window.scrollTo(scrollData.alternateValue, propertyValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
var eleData = data(element);
|
|
||||||
|
|
||||||
/* Transforms (translateX, rotateZ, etc.) are applied to a per-element transformCache object, which is manually flushed via flushTransformCache().
|
|
||||||
Thus, for now, we merely cache transforms being SET. */
|
|
||||||
if (CSS.Normalizations.registered[property] && CSS.Normalizations.registered[property]('name', element) === 'transform') {
|
|
||||||
/* Perform a normalization injection. */
|
|
||||||
/* Note: The normalization logic handles the transformCache updating. */
|
|
||||||
CSS.Normalizations.registered[property]('inject', element, propertyValue);
|
|
||||||
|
|
||||||
propertyName = 'transform';
|
|
||||||
propertyValue = eleData.transformCache[property];
|
|
||||||
|
|
||||||
} else {
|
|
||||||
/* Inject hooks. */
|
|
||||||
if (CSS.Hooks.registered[property]) {
|
|
||||||
var hookName = property,
|
|
||||||
hookRoot = CSS.Hooks.getRoot(property);
|
|
||||||
|
|
||||||
/* If a cached rootPropertyValue was not provided, query the DOM for the hookRoot's current value. */
|
|
||||||
rootPropertyValue = rootPropertyValue || CSS.getPropertyValue(element, hookRoot); /* GET */
|
|
||||||
|
|
||||||
propertyValue = CSS.Hooks.injectValue(hookName, propertyValue, rootPropertyValue);
|
|
||||||
property = hookRoot;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Normalize names and values. */
|
|
||||||
if (CSS.Normalizations.registered[property]) {
|
|
||||||
propertyValue = CSS.Normalizations.registered[property]('inject', element, propertyValue);
|
|
||||||
property = CSS.Normalizations.registered[property]('name', element);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Assign the appropriate vendor prefix before performing an official style update. */
|
|
||||||
propertyName = CSS.Names.prefixCheck(property)[0];
|
|
||||||
|
|
||||||
/* SVG elements have their dimensional properties (width, height, x, y, cx, etc.) applied directly as attributes instead of as styles. */
|
|
||||||
|
|
||||||
if (eleData && eleData.isSVG && CSS.Names.SVGAttribute(property)) {
|
|
||||||
/* Note: For SVG attributes, vendor-prefixed property names are never used. */
|
|
||||||
/* Note: Not all CSS properties can be animated via attributes, but the browser won't throw an error for unsupported properties. */
|
|
||||||
element.setAttribute(property, propertyValue);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
element.style[propertyName] = propertyValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Collide.debug >= 2) console.log('Set ' + property + ' (' + propertyName + '): ' + propertyValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return the normalized property name and value in case the caller wants to know how these values were modified before being applied to the DOM. */
|
|
||||||
return [ propertyName, propertyValue ];
|
|
||||||
},
|
|
||||||
|
|
||||||
/* To increase performance by batching transform updates into a single SET, transforms are not directly applied to an element until flushTransformCache() is called. */
|
|
||||||
/* Note: Collide applies transform properties in the same order that they are chronogically introduced to the element's CSS styles. */
|
|
||||||
flushTransformCache: function(element) {
|
|
||||||
var transformString = '';
|
|
||||||
var transformCache = data(element).transformCache;
|
|
||||||
|
|
||||||
var transformValue,
|
|
||||||
perspective;
|
|
||||||
|
|
||||||
/* Transform properties are stored as members of the transformCache object. Concatenate all the members into a string. */
|
|
||||||
for (var transformName in transformCache) {
|
|
||||||
transformValue = transformCache[transformName];
|
|
||||||
|
|
||||||
/* Transform's perspective subproperty must be set first in order to take effect. Store it temporarily. */
|
|
||||||
if (transformName === 'transformPerspective') {
|
|
||||||
perspective = transformValue;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
transformString += transformName + transformValue + ' ';
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If present, set the perspective subproperty first. */
|
|
||||||
if (perspective) {
|
|
||||||
transformString = 'perspective' + perspective + ' ' + transformString;
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
const vendorPrefixes = [ '', 'Webkit', 'ms' ];
|
|
||||||
|
|
||||||
/* Register hooks and normalizations. */
|
|
||||||
CSS.Hooks.register();
|
|
||||||
CSS.Normalizations.register();
|
|
@ -1,316 +0,0 @@
|
|||||||
/* Forked from VelocityJS, MIT License: https://github.com/julianshapiro/velocity | Julian Shapiro http://twitter.com/shapiro */
|
|
||||||
|
|
||||||
import * as util from 'ionic/util/util'
|
|
||||||
import {Collide} from './collide'
|
|
||||||
|
|
||||||
|
|
||||||
/**************
|
|
||||||
Easing
|
|
||||||
**************/
|
|
||||||
|
|
||||||
/* Step easing generator. */
|
|
||||||
function generateStep (steps) {
|
|
||||||
return function (p) {
|
|
||||||
return Math.round(p * steps) * (1 / steps);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Bezier curve function generator. Copyright Gaetan Renaudeau. MIT License: http://en.wikipedia.org/wiki/MIT_License */
|
|
||||||
function generateBezier (mX1, mY1, mX2, mY2) {
|
|
||||||
var NEWTON_ITERATIONS = 4,
|
|
||||||
NEWTON_MIN_SLOPE = 0.001,
|
|
||||||
SUBDIVISION_PRECISION = 0.0000001,
|
|
||||||
SUBDIVISION_MAX_ITERATIONS = 10,
|
|
||||||
kSplineTableSize = 11,
|
|
||||||
kSampleStepSize = 1.0 / (kSplineTableSize - 1.0),
|
|
||||||
float32ArraySupported = 'Float32Array' in window;
|
|
||||||
|
|
||||||
/* Must contain four arguments. */
|
|
||||||
if (arguments.length !== 4) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Arguments must be numbers. */
|
|
||||||
for (var i = 0; i < 4; ++i) {
|
|
||||||
if (typeof arguments[i] !== 'number' || isNaN(arguments[i]) || !isFinite(arguments[i])) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* X values must be in the [0, 1] range. */
|
|
||||||
mX1 = Math.min(mX1, 1);
|
|
||||||
mX2 = Math.min(mX2, 1);
|
|
||||||
mX1 = Math.max(mX1, 0);
|
|
||||||
mX2 = Math.max(mX2, 0);
|
|
||||||
|
|
||||||
var mSampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize);
|
|
||||||
|
|
||||||
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; }
|
|
||||||
|
|
||||||
function calcBezier (aT, aA1, aA2) {
|
|
||||||
return ((A(aA1, aA2)*aT + B(aA1, aA2))*aT + C(aA1))*aT;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSlope (aT, aA1, aA2) {
|
|
||||||
return 3.0 * A(aA1, aA2)*aT*aT + 2.0 * B(aA1, aA2) * aT + C(aA1);
|
|
||||||
}
|
|
||||||
|
|
||||||
function newtonRaphsonIterate (aX, aGuessT) {
|
|
||||||
for (var i = 0; i < NEWTON_ITERATIONS; ++i) {
|
|
||||||
var currentSlope = getSlope(aGuessT, mX1, mX2);
|
|
||||||
|
|
||||||
if (currentSlope === 0.0) return aGuessT;
|
|
||||||
|
|
||||||
var currentX = calcBezier(aGuessT, mX1, mX2) - aX;
|
|
||||||
aGuessT -= currentX / currentSlope;
|
|
||||||
}
|
|
||||||
|
|
||||||
return aGuessT;
|
|
||||||
}
|
|
||||||
|
|
||||||
function calcSampleValues () {
|
|
||||||
for (var i = 0; i < kSplineTableSize; ++i) {
|
|
||||||
mSampleValues[i] = calcBezier(i * kSampleStepSize, mX1, mX2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function binarySubdivide (aX, aA, aB) {
|
|
||||||
var currentX, currentT, i = 0;
|
|
||||||
|
|
||||||
do {
|
|
||||||
currentT = aA + (aB - aA) / 2.0;
|
|
||||||
currentX = calcBezier(currentT, mX1, mX2) - aX;
|
|
||||||
if (currentX > 0.0) {
|
|
||||||
aB = currentT;
|
|
||||||
} else {
|
|
||||||
aA = currentT;
|
|
||||||
}
|
|
||||||
} while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS);
|
|
||||||
|
|
||||||
return currentT;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTForX (aX) {
|
|
||||||
var intervalStart = 0.0,
|
|
||||||
currentSample = 1,
|
|
||||||
lastSample = kSplineTableSize - 1;
|
|
||||||
|
|
||||||
for (; currentSample != lastSample && mSampleValues[currentSample] <= aX; ++currentSample) {
|
|
||||||
intervalStart += kSampleStepSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
--currentSample;
|
|
||||||
|
|
||||||
var dist = (aX - mSampleValues[currentSample]) / (mSampleValues[currentSample+1] - mSampleValues[currentSample]),
|
|
||||||
guessForT = intervalStart + dist * kSampleStepSize,
|
|
||||||
initialSlope = getSlope(guessForT, mX1, mX2);
|
|
||||||
|
|
||||||
if (initialSlope >= NEWTON_MIN_SLOPE) {
|
|
||||||
return newtonRaphsonIterate(aX, guessForT);
|
|
||||||
} else if (initialSlope == 0.0) {
|
|
||||||
return guessForT;
|
|
||||||
} else {
|
|
||||||
return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var _precomputed = false;
|
|
||||||
|
|
||||||
function precompute() {
|
|
||||||
_precomputed = true;
|
|
||||||
if (mX1 != mY1 || mX2 != mY2) calcSampleValues();
|
|
||||||
}
|
|
||||||
|
|
||||||
var f = function (aX) {
|
|
||||||
if (!_precomputed) precompute();
|
|
||||||
if (mX1 === mY1 && mX2 === mY2) return aX;
|
|
||||||
if (aX === 0) return 0;
|
|
||||||
if (aX === 1) return 1;
|
|
||||||
|
|
||||||
return calcBezier(getTForX(aX), mY1, mY2);
|
|
||||||
};
|
|
||||||
|
|
||||||
f.getControlPoints = function() { return [{ x: mX1, y: mY1 }, { x: mX2, y: mY2 }]; };
|
|
||||||
|
|
||||||
var str = 'generateBezier(' + [mX1, mY1, mX2, mY2] + ')';
|
|
||||||
f.toString = function () { return str; };
|
|
||||||
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Runge-Kutta spring physics function generator. Adapted from Framer.js, copyright Koen Bok. MIT License: http://en.wikipedia.org/wiki/MIT_License */
|
|
||||||
/* Given a tension, friction, and duration, a simulation at 60FPS will first run without a defined duration in order to calculate the full path. A second pass
|
|
||||||
then adjusts the time delta -- using the relation between actual time and duration -- to calculate the path for the duration-constrained animation. */
|
|
||||||
var generateSpringRK4 = (function () {
|
|
||||||
function springAccelerationForState (state) {
|
|
||||||
return (-state.tension * state.x) - (state.friction * state.v);
|
|
||||||
}
|
|
||||||
|
|
||||||
function springEvaluateStateWithDerivative (initialState, dt, derivative) {
|
|
||||||
var state = {
|
|
||||||
x: initialState.x + derivative.dx * dt,
|
|
||||||
v: initialState.v + derivative.dv * dt,
|
|
||||||
tension: initialState.tension,
|
|
||||||
friction: initialState.friction
|
|
||||||
};
|
|
||||||
|
|
||||||
return { dx: state.v, dv: springAccelerationForState(state) };
|
|
||||||
}
|
|
||||||
|
|
||||||
function springIntegrateState (state, dt) {
|
|
||||||
var a = {
|
|
||||||
dx: state.v,
|
|
||||||
dv: springAccelerationForState(state)
|
|
||||||
},
|
|
||||||
b = springEvaluateStateWithDerivative(state, dt * 0.5, a),
|
|
||||||
c = springEvaluateStateWithDerivative(state, dt * 0.5, b),
|
|
||||||
d = springEvaluateStateWithDerivative(state, dt, c),
|
|
||||||
dxdt = 1.0 / 6.0 * (a.dx + 2.0 * (b.dx + c.dx) + d.dx),
|
|
||||||
dvdt = 1.0 / 6.0 * (a.dv + 2.0 * (b.dv + c.dv) + d.dv);
|
|
||||||
|
|
||||||
state.x = state.x + dxdt * dt;
|
|
||||||
state.v = state.v + dvdt * dt;
|
|
||||||
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
return function springRK4Factory (tension, friction, duration) {
|
|
||||||
|
|
||||||
var initState = {
|
|
||||||
x: -1,
|
|
||||||
v: 0,
|
|
||||||
tension: null,
|
|
||||||
friction: null
|
|
||||||
},
|
|
||||||
path = [0],
|
|
||||||
time_lapsed = 0,
|
|
||||||
tolerance = 1 / 10000,
|
|
||||||
DT = 16 / 1000,
|
|
||||||
have_duration, dt, last_state;
|
|
||||||
|
|
||||||
tension = parseFloat(tension) || 500;
|
|
||||||
friction = parseFloat(friction) || 20;
|
|
||||||
duration = duration || null;
|
|
||||||
|
|
||||||
initState.tension = tension;
|
|
||||||
initState.friction = friction;
|
|
||||||
|
|
||||||
have_duration = duration !== null;
|
|
||||||
|
|
||||||
/* Calculate the actual time it takes for this animation to complete with the provided conditions. */
|
|
||||||
if (have_duration) {
|
|
||||||
/* Run the simulation without a duration. */
|
|
||||||
time_lapsed = springRK4Factory(tension, friction);
|
|
||||||
/* Compute the adjusted time delta. */
|
|
||||||
dt = time_lapsed / duration * DT;
|
|
||||||
} else {
|
|
||||||
dt = DT;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
/* Next/step function .*/
|
|
||||||
last_state = springIntegrateState(last_state || initState, dt);
|
|
||||||
/* Store the position. */
|
|
||||||
path.push(1 + last_state.x);
|
|
||||||
time_lapsed += 16;
|
|
||||||
/* If the change threshold is reached, break. */
|
|
||||||
if (!(Math.abs(last_state.x) > tolerance && Math.abs(last_state.v) > tolerance)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If duration is not defined, return the actual time required for completing this animation. Otherwise, return a closure that holds the
|
|
||||||
computed path and returns a snapshot of the position according to a given percentComplete. */
|
|
||||||
return !have_duration ? time_lapsed : function(percentComplete) { return path[ (percentComplete * (path.length - 1)) | 0 ]; };
|
|
||||||
};
|
|
||||||
}());
|
|
||||||
|
|
||||||
|
|
||||||
/* default easings. */
|
|
||||||
Collide.Easings = {
|
|
||||||
linear: function(p) { return p; },
|
|
||||||
swing: function(p) { return 0.5 - Math.cos( p * Math.PI ) / 2 },
|
|
||||||
spring: function(p) { return 1 - (Math.cos(p * 4.5 * Math.PI) * Math.exp(-p * 6)); }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/* CSS3 and Robert Penner easings. */
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
let penner = [
|
|
||||||
[ 'ease', [ 0.25, 0.1, 0.25, 1 ] ],
|
|
||||||
[ 'ease-in', [ 0.42, 0.0, 1.00, 1 ] ],
|
|
||||||
[ 'ease-out', [ 0.00, 0.0, 0.58, 1 ] ],
|
|
||||||
[ 'ease-in-out', [ 0.42, 0.0, 0.58, 1 ] ],
|
|
||||||
[ '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 ] ],
|
|
||||||
[ '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 ] ],
|
|
||||||
[ '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 ] ],
|
|
||||||
[ 'easeInQuart', [ 0.895, 0.03, 0.685, 0.22 ] ],
|
|
||||||
[ 'easeOutQuart', [ 0.165, 0.84, 0.44, 1 ] ],
|
|
||||||
[ 'easeInOutQuart', [ 0.77, 0, 0.175, 1 ] ],
|
|
||||||
[ 'easeInQuint', [ 0.755, 0.05, 0.855, 0.06 ] ],
|
|
||||||
[ 'easeOutQuint', [ 0.23, 1, 0.32, 1 ] ],
|
|
||||||
[ 'easeInOutQuint', [ 0.86, 0, 0.07, 1 ] ],
|
|
||||||
[ 'easeInExpo', [ 0.95, 0.05, 0.795, 0.035 ] ],
|
|
||||||
[ 'easeOutExpo', [ 0.19, 1, 0.22, 1 ] ],
|
|
||||||
[ 'easeInOutExpo', [ 1, 0, 0, 1 ] ],
|
|
||||||
[ '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 ] ]
|
|
||||||
];
|
|
||||||
|
|
||||||
for (var i = 0, l = penner.length; i < l; i++) {
|
|
||||||
addEasing(penner[i][0], penner[i][1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
export function addEasing(name, values) {
|
|
||||||
Collide.Easings[name] = generateBezier.apply(null, values);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/* Determine the appropriate easing type given an easing input. */
|
|
||||||
export function getEasing(value, duration) {
|
|
||||||
let easing = value;
|
|
||||||
|
|
||||||
/* The easing option can either be a string that references a pre-registered easing,
|
|
||||||
or it can be a two-/four-item array of integers to be converted into a bezier/spring function. */
|
|
||||||
if (util.isString(value)) {
|
|
||||||
/* Ensure that the easing has been assigned to standard easings object. */
|
|
||||||
if (!Collide.Easings[value]) {
|
|
||||||
easing = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (util.isArray(value) && value.length === 1) {
|
|
||||||
easing = generateStep.apply(null, value);
|
|
||||||
|
|
||||||
} else if (util.isArray(value) && value.length === 2) {
|
|
||||||
/* springRK4 must be passed the animation's duration. */
|
|
||||||
/* Note: If the springRK4 array contains non-numbers, generateSpringRK4() returns an easing
|
|
||||||
function generated with default tension and friction values. */
|
|
||||||
easing = generateSpringRK4.apply(null, value.concat([ duration ]));
|
|
||||||
|
|
||||||
} else if (util.isArray(value) && value.length === 4) {
|
|
||||||
/* Note: If the bezier array contains non-numbers, generateBezier() returns false. */
|
|
||||||
easing = generateBezier.apply(null, value);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
easing = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Revert to the fall back to 'swing' */
|
|
||||||
if (easing === false) {
|
|
||||||
easing = 'swing';
|
|
||||||
}
|
|
||||||
|
|
||||||
return easing;
|
|
||||||
};
|
|
@ -1,655 +0,0 @@
|
|||||||
/* Forked from VelocityJS, MIT License: https://github.com/julianshapiro/velocity | Julian Shapiro http://twitter.com/shapiro */
|
|
||||||
|
|
||||||
import * as util from 'ionic/util/util'
|
|
||||||
import {Collide} from './collide'
|
|
||||||
import {CSS} from './css'
|
|
||||||
import {getEasing} from './easing'
|
|
||||||
import {calculateUnitRatios} from './calculate-unit-ratios'
|
|
||||||
|
|
||||||
|
|
||||||
const data = Collide.data;
|
|
||||||
|
|
||||||
/*********************
|
|
||||||
Element Processing
|
|
||||||
*********************/
|
|
||||||
|
|
||||||
/* Element processing consists of three parts -- data processing that cannot go stale and data processing that *can* go stale (i.e. third-party style modifications):
|
|
||||||
1) Pre-Queueing: Element-wide variables, including the element's data storage, are instantiated. Call options are prepared. If triggered, the Stop action is executed.
|
|
||||||
2) Queueing: The logic that runs once this call has reached its point of execution in the element's Collide.queue() stack. Most logic is placed here to avoid risking it becoming stale.
|
|
||||||
3) Pushing: Consolidation of the tween data followed by its push onto the global in-progress calls container.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export function processElement(action, animation, elementIndex, clearCache) {
|
|
||||||
var elements = animation._ele;
|
|
||||||
var elementsLength = elements.length;
|
|
||||||
var element = elements[elementIndex];
|
|
||||||
var eleData;
|
|
||||||
|
|
||||||
var opts = animation._options;
|
|
||||||
var propertiesMap = animation._properties;
|
|
||||||
var callUnitConversionData = animation._unitConversion;
|
|
||||||
var call = animation._call;
|
|
||||||
var resolve = animation._resolve;
|
|
||||||
|
|
||||||
|
|
||||||
/*************************
|
|
||||||
Part I: Pre-Queueing
|
|
||||||
*************************/
|
|
||||||
|
|
||||||
/***************************
|
|
||||||
Element-Wide Variables
|
|
||||||
***************************/
|
|
||||||
|
|
||||||
/* A container for the processed data associated with each property in the propertyMap.
|
|
||||||
(Each property in the map produces its own 'tween'.) */
|
|
||||||
var tweensContainer = {};
|
|
||||||
var elementUnitConversionData = null;
|
|
||||||
|
|
||||||
|
|
||||||
/******************
|
|
||||||
Element Init
|
|
||||||
******************/
|
|
||||||
|
|
||||||
if (data(element) === undefined) {
|
|
||||||
eleData = Collide.initData(element);
|
|
||||||
} else {
|
|
||||||
eleData = data(element);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (animation._addStartClasses.length) eleData.startAddCls = animation._addStartClasses.slice();
|
|
||||||
if (animation._removeStartClasses.length) eleData.startRmvCls = animation._removeStartClasses.slice();
|
|
||||||
if (animation._addEndClasses.length) eleData.endAddCls = animation._addEndClasses.slice();
|
|
||||||
if (animation._removeEndClasses.length) eleData.endRmvCls = animation._removeEndClasses.slice();
|
|
||||||
|
|
||||||
|
|
||||||
/******************
|
|
||||||
Option: Delay
|
|
||||||
******************/
|
|
||||||
|
|
||||||
/* Since queue:false doesn't respect the item's existing queue, we avoid injecting its delay here (it's set later on). */
|
|
||||||
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.
|
|
||||||
// See completeCall() for further details.
|
|
||||||
Collide.collideQueueEntryFlag = true;
|
|
||||||
|
|
||||||
// The ensuing queue item (which is assigned to the 'next' argument that Collide.queue() automatically passes in) will be triggered after a setTimeout delay.
|
|
||||||
// The setTimeout is stored so that it can be subjected to clearTimeout() if this animation is prematurely stopped via Collide's 'stop' command.
|
|
||||||
data(element).delayTimer = {
|
|
||||||
setTimeout: setTimeout(next, parseFloat(opts.delay)),
|
|
||||||
next: next
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*********************
|
|
||||||
Option: Duration
|
|
||||||
*********************/
|
|
||||||
|
|
||||||
opts.duration = parseFloat(opts.duration) || 1;
|
|
||||||
|
|
||||||
|
|
||||||
/*******************
|
|
||||||
Option: Easing
|
|
||||||
*******************/
|
|
||||||
|
|
||||||
opts.easing = getEasing(opts.easing, opts.duration);
|
|
||||||
|
|
||||||
|
|
||||||
/**********************
|
|
||||||
Option: Callbacks
|
|
||||||
**********************/
|
|
||||||
|
|
||||||
/* Callbacks must functions. Otherwise, default to null. */
|
|
||||||
if (opts.begin && typeof opts.begin !== 'function') {
|
|
||||||
opts.begin = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts.progress && typeof opts.progress !== 'function') {
|
|
||||||
opts.progress = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts.complete && typeof opts.complete !== 'function') {
|
|
||||||
opts.complete = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*********************************
|
|
||||||
Option: Display & Visibility
|
|
||||||
*********************************/
|
|
||||||
|
|
||||||
/* Refer to Collide's documentation (CollideJS.org/#displayAndVisibility) for a description of the display and visibility options' behavior. */
|
|
||||||
/* Note: We strictly check for undefined instead of falsiness because display accepts an empty string value. */
|
|
||||||
if (opts.display !== undefined && opts.display !== null) {
|
|
||||||
opts.display = opts.display.toString().toLowerCase();
|
|
||||||
|
|
||||||
/* Users can pass in a special 'auto' value to instruct Collide to set the element to its default display value. */
|
|
||||||
if (opts.display === 'auto') {
|
|
||||||
opts.display = CSS.getDisplayType(element);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts.visibility !== undefined && opts.visibility !== null) {
|
|
||||||
opts.visibility = opts.visibility.toString().toLowerCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/***********************
|
|
||||||
Part II: Queueing
|
|
||||||
***********************/
|
|
||||||
|
|
||||||
/* When a set of elements is targeted by a Collide call, the set is broken up and each element has the current Collide call individually queued onto it.
|
|
||||||
In this way, each element's existing queue is respected; some elements may already be animating and accordingly should not have this current Collide call triggered immediately. */
|
|
||||||
/* In each queue, tween data is processed for each animating property then pushed onto the call-wide calls array. When the last element in the set has had its tweens processed,
|
|
||||||
the call array is pushed to Collide.State.calls for live processing by the requestAnimationFrame tick. */
|
|
||||||
function buildQueue(next) {
|
|
||||||
|
|
||||||
/*******************
|
|
||||||
Option: Begin
|
|
||||||
*******************/
|
|
||||||
|
|
||||||
/* The begin callback is fired once per call -- not once per elemenet -- and is passed the full raw DOM element set as both its context and its first argument. */
|
|
||||||
if (opts.begin && elementIndex === 0) {
|
|
||||||
/* We throw callbacks in a setTimeout so that thrown errors don't halt the execution of Collide itself. */
|
|
||||||
try {
|
|
||||||
opts.begin.call(elements, elements);
|
|
||||||
} catch (error) {
|
|
||||||
setTimeout(function() { throw error; });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*****************************************
|
|
||||||
Tween Data Construction (for Scroll)
|
|
||||||
*****************************************/
|
|
||||||
|
|
||||||
/* Note: In order to be subjected to chaining and animation options, scroll's tweening is routed through Collide as if it were a standard CSS property animation. */
|
|
||||||
if (action === 'scroll') {
|
|
||||||
/* The scroll action uniquely takes an optional 'offset' option -- specified in pixels -- that offsets the targeted scroll position. */
|
|
||||||
var scrollDirection = (/^x$/i.test(opts.axis) ? 'Left' : 'Top'),
|
|
||||||
scrollOffset = parseFloat(opts.offset) || 0,
|
|
||||||
scrollPositionCurrent,
|
|
||||||
scrollPositionCurrentAlternate,
|
|
||||||
scrollPositionEnd;
|
|
||||||
|
|
||||||
/* 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 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 */
|
|
||||||
|
|
||||||
/* 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 raw DOM element was passed in, default to null so that this option is ignored. */
|
|
||||||
opts.container = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
/* 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 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. */
|
|
||||||
tweensContainer = {
|
|
||||||
scroll: {
|
|
||||||
rootPropertyValue: false,
|
|
||||||
startValue: scrollPositionCurrent,
|
|
||||||
currentValue: scrollPositionCurrent,
|
|
||||||
endValue: scrollPositionEnd,
|
|
||||||
unitType: '',
|
|
||||||
easing: opts.easing,
|
|
||||||
scrollData: {
|
|
||||||
container: opts.container,
|
|
||||||
direction: scrollDirection,
|
|
||||||
alternateValue: scrollPositionCurrentAlternate
|
|
||||||
}
|
|
||||||
},
|
|
||||||
element: element
|
|
||||||
};
|
|
||||||
|
|
||||||
if (Collide.debug) console.log("tweensContainer (scroll): ", tweensContainer.scroll, element);
|
|
||||||
|
|
||||||
|
|
||||||
} else if (action === 'start') {
|
|
||||||
|
|
||||||
/****************************
|
|
||||||
Start: Value Transferring
|
|
||||||
****************************/
|
|
||||||
|
|
||||||
/* If this queue entry follows a previous Collide-initiated queue entry *and* if this entry was created
|
|
||||||
while the element was in the process of being animated by Collide, then this current call is safe to use
|
|
||||||
the end values from the prior call as its start values. Collide attempts to perform this value transfer
|
|
||||||
process whenever possible in order to avoid requerying the DOM. */
|
|
||||||
/* If values aren't transferred from a prior call and start values were not forcefed by the user (more on this below),
|
|
||||||
then the DOM is queried for the element's current values as a last resort. */
|
|
||||||
/* Note: Conversely, animation reversal (and looping) *always* perform inter-call value transfers; they never requery the DOM. */
|
|
||||||
var lastTweensContainer;
|
|
||||||
|
|
||||||
/* The per-element isAnimating flag is used to indicate whether it's safe (i.e. the data isn't stale)
|
|
||||||
to transfer over end values to use as start values. If it's set to true and there is a previous
|
|
||||||
Collide call to pull values from, do so. */
|
|
||||||
eleData = data(element);
|
|
||||||
if (clearCache) {
|
|
||||||
eleData.tweensContainer = undefined;
|
|
||||||
} else if (eleData.tweensContainer && eleData.isAnimating === true) {
|
|
||||||
lastTweensContainer = eleData.tweensContainer;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/********************************
|
|
||||||
Start: Tween Data Calculation
|
|
||||||
********************************/
|
|
||||||
|
|
||||||
/* This function parses property data and defaults endValue, easing, and startValue as appropriate. */
|
|
||||||
/* 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. */
|
|
||||||
function parsePropertyValue(valueData, skipResolvingEasing) {
|
|
||||||
var endValue = valueData[0];
|
|
||||||
var easing = skipResolvingEasing ? valueData[1] : getEasing(valueData[1] || opts.easing, opts.duration);
|
|
||||||
var startValue = valueData[2];
|
|
||||||
|
|
||||||
/* Default to the call's easing if a per-property easing type was not defined. */
|
|
||||||
if (!skipResolvingEasing) {
|
|
||||||
easing = easing || opts.easing;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If functions were passed in as values, pass the function the current element as its context,
|
|
||||||
plus the element's index and the element set's size as arguments. Then, assign the returned value. */
|
|
||||||
if (typeof endValue === 'function') {
|
|
||||||
endValue = endValue.call(element, elementIndex, elementsLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof startValue === 'function') {
|
|
||||||
startValue = startValue.call(element, elementIndex, elementsLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Allow startValue to be left as undefined to indicate to the ensuing code that its value was not forcefed. */
|
|
||||||
return [ endValue || 0, easing, startValue ];
|
|
||||||
|
|
||||||
} // END: parsePropertyValue()
|
|
||||||
|
|
||||||
|
|
||||||
/* Cycle through each property in the map, looking for shorthand color properties (e.g. 'color' as opposed to 'colorRed'). Inject the corresponding
|
|
||||||
colorRed, colorGreen, and colorBlue RGB component tweens into the propertiesMap (which Collide understands) and remove the shorthand property. */
|
|
||||||
for (var property in propertiesMap) {
|
|
||||||
|
|
||||||
/* Find shorthand color properties that have been passed a hex string. */
|
|
||||||
if (RegExp('^' + CSS.Lists.colors.join('$|^') + '$').test(property)) {
|
|
||||||
/* Parse the value data for each shorthand. */
|
|
||||||
var valueData = parsePropertyValue(propertiesMap[property], true),
|
|
||||||
endValue = valueData[0],
|
|
||||||
easing = valueData[1],
|
|
||||||
startValue = valueData[2];
|
|
||||||
|
|
||||||
if (CSS.RegEx.isHex.test(endValue)) {
|
|
||||||
/* Convert the hex strings into their RGB component arrays. */
|
|
||||||
var colorComponents = [ 'Red', 'Green', 'Blue' ],
|
|
||||||
endValueRGB = CSS.Values.hexToRgb(endValue),
|
|
||||||
startValueRGB = startValue ? CSS.Values.hexToRgb(startValue) : undefined;
|
|
||||||
|
|
||||||
/* Inject the RGB component tweens into propertiesMap. */
|
|
||||||
for (var i = 0; i < colorComponents.length; i++) {
|
|
||||||
var dataArray = [ endValueRGB[i] ];
|
|
||||||
|
|
||||||
if (easing) {
|
|
||||||
dataArray.push(easing);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (startValueRGB !== undefined) {
|
|
||||||
dataArray.push(startValueRGB[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
propertiesMap[property + colorComponents[i]] = dataArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remove the intermediary shorthand property entry now that we've processed it. */
|
|
||||||
delete propertiesMap[property];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Create a tween out of each property, and append its associated data to tweensContainer. */
|
|
||||||
for (var property in propertiesMap) {
|
|
||||||
|
|
||||||
/*********************************************
|
|
||||||
parsePropertyValue(), Start Value Sourcing
|
|
||||||
*********************************************/
|
|
||||||
|
|
||||||
/* Parse out endValue, easing, and startValue from the property's data. */
|
|
||||||
var valueData = parsePropertyValue(propertiesMap[property]),
|
|
||||||
endValue = valueData[0],
|
|
||||||
easing = valueData[1],
|
|
||||||
startValue = valueData[2];
|
|
||||||
|
|
||||||
/* Now that the original property name's format has been used for the parsePropertyValue() lookup above,
|
|
||||||
we force the property to its camelCase styling to normalize it for manipulation. */
|
|
||||||
property = CSS.Names.camelCase(property);
|
|
||||||
|
|
||||||
/* In case this property is a hook, there are circumstances where we will intend to work on the hook's root property and not the hooked subproperty. */
|
|
||||||
var rootProperty = CSS.Hooks.getRoot(property),
|
|
||||||
rootPropertyValue = false;
|
|
||||||
|
|
||||||
/* Other than for the dummy tween property, properties that are not supported by the browser (and do not have an associated normalization) will
|
|
||||||
inherently produce no style changes when set, so they are skipped in order to decrease animation tick overhead.
|
|
||||||
Property support is determined via prefixCheck(), which returns a false flag when no supported is detected. */
|
|
||||||
/* 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 (!eleData.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.');
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If the display option is being set to a non-'none' (e.g. 'block') and opacity (filter on IE<=8) is being
|
|
||||||
animated to an endValue of non-zero, the user's intention is to fade in from invisible, thus we forcefeed opacity
|
|
||||||
a startValue of 0 if its startValue hasn't already been sourced by value transferring or prior forcefeeding. */
|
|
||||||
if (((opts.display !== undefined && opts.display !== null && opts.display !== 'none') || (opts.visibility !== undefined && opts.visibility !== 'hidden')) && /opacity|filter/.test(property) && !startValue && endValue !== 0) {
|
|
||||||
startValue = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* If values have been transferred from the previous Collide call, extract the endValue and rootPropertyValue
|
|
||||||
for all of the current call's properties that were *also* animated in the previous call. */
|
|
||||||
/* Note: Value transferring can optionally be disabled by the user via the _cacheValues option. */
|
|
||||||
if (opts._cacheValues && lastTweensContainer && lastTweensContainer[property]) {
|
|
||||||
|
|
||||||
if (startValue === undefined) {
|
|
||||||
startValue = lastTweensContainer[property].endValue + lastTweensContainer[property].unitType;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The previous call's rootPropertyValue is extracted from the element's data cache since that's the
|
|
||||||
instance of rootPropertyValue that gets freshly updated by the tweening process, whereas the rootPropertyValue
|
|
||||||
attached to the incoming lastTweensContainer is equal to the root property's value prior to any tweening. */
|
|
||||||
rootPropertyValue = eleData.rootPropertyValueCache[rootProperty];
|
|
||||||
|
|
||||||
/* If values were not transferred from a previous Collide call, query the DOM as needed. */
|
|
||||||
} else {
|
|
||||||
/* Handle hooked properties. */
|
|
||||||
if (CSS.Hooks.registered[property]) {
|
|
||||||
if (startValue === undefined) {
|
|
||||||
rootPropertyValue = CSS.getPropertyValue(element, rootProperty); /* GET */
|
|
||||||
/* Note: The following getPropertyValue() call does not actually trigger a DOM query;
|
|
||||||
getPropertyValue() will extract the hook from rootPropertyValue. */
|
|
||||||
startValue = CSS.getPropertyValue(element, property, rootPropertyValue);
|
|
||||||
|
|
||||||
/* If startValue is already defined via forcefeeding, do not query the DOM for the root property's value;
|
|
||||||
just grab rootProperty's zero-value template from CSS.Hooks. This overwrites the element's actual
|
|
||||||
root property value (if one is set), but this is acceptable since the primary reason users forcefeed is
|
|
||||||
to avoid DOM queries, and thus we likewise avoid querying the DOM for the root property's value. */
|
|
||||||
} else {
|
|
||||||
/* Grab this hook's zero-value template, e.g. '0px 0px 0px black'. */
|
|
||||||
rootPropertyValue = CSS.Hooks.templates[rootProperty][1];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Handle non-hooked properties that haven't already been defined via forcefeeding. */
|
|
||||||
} else if (startValue === undefined) {
|
|
||||||
startValue = CSS.getPropertyValue(element, property); /* GET */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**********************************************
|
|
||||||
parsePropertyValue(), Value Data Extraction
|
|
||||||
**********************************************/
|
|
||||||
|
|
||||||
var separatedValue,
|
|
||||||
endValueUnitType,
|
|
||||||
startValueUnitType,
|
|
||||||
operator = false;
|
|
||||||
|
|
||||||
/* Separates a property value into its numeric value and its unit util. */
|
|
||||||
function separateValue (property, value) {
|
|
||||||
var unitType,
|
|
||||||
numericValue;
|
|
||||||
|
|
||||||
numericValue = (value || '0')
|
|
||||||
.toString()
|
|
||||||
.toLowerCase()
|
|
||||||
/* Match the unit type at the end of the value. */
|
|
||||||
.replace(/[%A-z]+$/, function(match) {
|
|
||||||
/* Grab the unit util. */
|
|
||||||
unitType = match;
|
|
||||||
|
|
||||||
/* Strip the unit type off of value. */
|
|
||||||
return '';
|
|
||||||
});
|
|
||||||
|
|
||||||
/* If no unit type was supplied, assign one that is appropriate for this property (e.g. 'deg' for rotateZ or 'px' for width). */
|
|
||||||
if (!unitType) {
|
|
||||||
unitType = CSS.Values.getUnitType(property);
|
|
||||||
}
|
|
||||||
|
|
||||||
return [ numericValue, unitType ];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Separate startValue. */
|
|
||||||
separatedValue = separateValue(property, startValue);
|
|
||||||
startValue = separatedValue[0];
|
|
||||||
startValueUnitType = separatedValue[1];
|
|
||||||
|
|
||||||
/* Separate endValue, and extract a value operator (e.g. '+=', '-=') if one exists. */
|
|
||||||
separatedValue = separateValue(property, endValue);
|
|
||||||
endValue = separatedValue[0].replace(/^([+-\/*])=/, function(match, subMatch) {
|
|
||||||
operator = subMatch;
|
|
||||||
|
|
||||||
/* Strip the operator off of the value. */
|
|
||||||
return '';
|
|
||||||
});
|
|
||||||
endValueUnitType = separatedValue[1];
|
|
||||||
|
|
||||||
/* Parse float values from endValue and startValue. Default to 0 if NaN is returned. */
|
|
||||||
startValue = parseFloat(startValue) || 0;
|
|
||||||
endValue = parseFloat(endValue) || 0;
|
|
||||||
|
|
||||||
|
|
||||||
/***************************************
|
|
||||||
parsePropertyValue, Property-Specific Value Conversion
|
|
||||||
***************************************/
|
|
||||||
|
|
||||||
/* Custom support for properties that don't actually accept the % unit type, but where pollyfilling is trivial and relatively foolproof. */
|
|
||||||
if (endValueUnitType === '%') {
|
|
||||||
/* A %-value fontSize/lineHeight is relative to the parent's fontSize (as opposed to the parent's dimensions),
|
|
||||||
which is identical to the em unit's behavior, so we piggyback off of that. */
|
|
||||||
if (/^(fontSize|lineHeight)$/.test(property)) {
|
|
||||||
/* Convert % into an em decimal value. */
|
|
||||||
endValue = endValue / 100;
|
|
||||||
endValueUnitType = 'em';
|
|
||||||
|
|
||||||
/* For scaleX and scaleY, convert the value into its decimal format and strip off the unit util. */
|
|
||||||
} else if (/^scale/.test(property)) {
|
|
||||||
endValue = endValue / 100;
|
|
||||||
endValueUnitType = '';
|
|
||||||
|
|
||||||
/* For RGB components, take the defined percentage of 255 and strip off the unit util. */
|
|
||||||
} else if (/(Red|Green|Blue)$/i.test(property)) {
|
|
||||||
endValue = (endValue / 100) * 255;
|
|
||||||
endValueUnitType = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/****************************************
|
|
||||||
parsePropertyValue(), Unit Conversion
|
|
||||||
*****************************************/
|
|
||||||
|
|
||||||
/* The * and / operators, which are not passed in with an associated unit, inherently use startValue's unit. Skip value and unit conversion. */
|
|
||||||
if (/[\/*]/.test(operator)) {
|
|
||||||
endValueUnitType = startValueUnitType;
|
|
||||||
|
|
||||||
/* If startValue and endValue differ in unit type, convert startValue into the same unit type as endValue so that if endValueUnitType
|
|
||||||
is a relative unit (%, em, rem), the values set during tweening will continue to be accurately relative even if the metrics they depend
|
|
||||||
on are dynamically changing during the course of the animation. Conversely, if we always normalized into px and used px for setting values, the px ratio
|
|
||||||
would become stale if the original unit being animated toward was relative and the underlying metrics change during the animation. */
|
|
||||||
/* Since 0 is 0 in any unit type, no conversion is necessary when startValue is 0 -- we just start at 0 with endValueUnitutil. */
|
|
||||||
} else if ((startValueUnitType !== endValueUnitType) && startValue !== 0) {
|
|
||||||
/* Unit conversion is also skipped when endValue is 0, but *startValueUnitType* must be used for tween values to remain accurate. */
|
|
||||||
/* Note: Skipping unit conversion here means that if endValueUnitType was originally a relative unit, the animation won't relatively
|
|
||||||
match the underlying metrics if they change, but this is acceptable since we're animating toward invisibility instead of toward visibility,
|
|
||||||
which remains past the point of the animation's completion. */
|
|
||||||
if (endValue === 0) {
|
|
||||||
endValueUnitType = startValueUnitType;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
/* By this point, we cannot avoid unit conversion (it's undesirable since it causes layout thrashing).
|
|
||||||
If we haven't already, we trigger calculateUnitRatios(), which runs once per element per call. */
|
|
||||||
elementUnitConversionData = elementUnitConversionData || calculateUnitRatios(element, callUnitConversionData);
|
|
||||||
|
|
||||||
/* The following RegEx matches CSS properties that have their % values measured relative to the x-axis. */
|
|
||||||
/* Note: W3C spec mandates that all of margin and padding's properties (even top and bottom) are %-relative to the *width* of the parent element. */
|
|
||||||
var axis = (/margin|padding|left|right|width|text|word|letter/i.test(property) || /X$/.test(property) || property === 'x') ? 'x' : 'y';
|
|
||||||
|
|
||||||
/* In order to avoid generating n^2 bespoke conversion functions, unit conversion is a two-step process:
|
|
||||||
1) Convert startValue into pixels. 2) Convert this new pixel value into endValue's unit util. */
|
|
||||||
switch (startValueUnitType) {
|
|
||||||
case '%':
|
|
||||||
/* Note: translateX and translateY are the only properties that are %-relative to an element's own dimensions -- not its parent's dimensions.
|
|
||||||
Collide does not include a special conversion process to account for this behavior. Therefore, animating translateX/Y from a % value
|
|
||||||
to a non-% value will produce an incorrect start value. Fortunately, this sort of cross-unit conversion is rarely done by users in practice. */
|
|
||||||
startValue *= (axis === 'x' ? elementUnitConversionData.percentToPxWidth : elementUnitConversionData.percentToPxHeight);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'px':
|
|
||||||
/* px acts as our midpoint in the unit conversion process; do nothing. */
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
startValue *= elementUnitConversionData[startValueUnitType + 'ToPx'];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Invert the px ratios to convert into to the target unit. */
|
|
||||||
switch (endValueUnitType) {
|
|
||||||
case '%':
|
|
||||||
startValue *= 1 / (axis === 'x' ? elementUnitConversionData.percentToPxWidth : elementUnitConversionData.percentToPxHeight);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'px':
|
|
||||||
/* startValue is already in px, do nothing; we're done. */
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
startValue *= 1 / elementUnitConversionData[endValueUnitType + 'ToPx'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/****************************************
|
|
||||||
parsePropertyValue(), Relative Values
|
|
||||||
****************************************/
|
|
||||||
|
|
||||||
/* Operator logic must be performed last since it requires unit-normalized start and end values. */
|
|
||||||
/* Note: Relative *percent values* do not behave how most people think; while one would expect '+=50%'
|
|
||||||
to increase the property 1.5x its current value, it in fact increases the percent units in absolute terms:
|
|
||||||
50 points is added on top of the current % value. */
|
|
||||||
switch (operator) {
|
|
||||||
case '+':
|
|
||||||
endValue = startValue + endValue;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '-':
|
|
||||||
endValue = startValue - endValue;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '*':
|
|
||||||
endValue = startValue * endValue;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '/':
|
|
||||||
endValue = startValue / endValue;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
var currentValue = startValue;
|
|
||||||
|
|
||||||
/*********************************************
|
|
||||||
parsePropertyValue(), tweensContainer Push
|
|
||||||
*********************************************/
|
|
||||||
|
|
||||||
/* Construct the per-property tween object, and push it to the element's tweensContainer. */
|
|
||||||
tweensContainer[property] = {
|
|
||||||
rootPropertyValue: rootPropertyValue,
|
|
||||||
startValue: startValue,
|
|
||||||
currentValue: currentValue,
|
|
||||||
endValue: endValue,
|
|
||||||
unitType: endValueUnitType,
|
|
||||||
easing: easing
|
|
||||||
};
|
|
||||||
|
|
||||||
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. */
|
|
||||||
tweensContainer.element = element;
|
|
||||||
|
|
||||||
} // END: parsePropertyValue()
|
|
||||||
|
|
||||||
|
|
||||||
/*****************
|
|
||||||
Call Push
|
|
||||||
*****************/
|
|
||||||
|
|
||||||
/* Note: tweensContainer can be empty if all of the properties in this call's property map were skipped due to not
|
|
||||||
being supported by the browser. The element property is used for checking that the tweensContainer has been appended to. */
|
|
||||||
if (tweensContainer.element) {
|
|
||||||
|
|
||||||
/* The call array houses the tweensContainers for each element being animated in the current call. */
|
|
||||||
call.push(tweensContainer);
|
|
||||||
|
|
||||||
/* Store the tweensContainer and options if we're working on the default effects queue, so that they can be used by the reverse command. */
|
|
||||||
if (opts.queue === '') {
|
|
||||||
eleData.tweensContainer = tweensContainer;
|
|
||||||
eleData.opts = opts;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // END: buildQueue
|
|
||||||
|
|
||||||
|
|
||||||
/* When the queue option is set to false, the call skips the element's queue and fires immediately. */
|
|
||||||
if (opts.queue === false) {
|
|
||||||
/* Since this buildQueue call doesn't respect the element's existing queue (which is where a delay option would have been appended),
|
|
||||||
we manually inject the delay property here with an explicit setTimeout. */
|
|
||||||
if (opts.delay) {
|
|
||||||
setTimeout(buildQueue, opts.delay);
|
|
||||||
} else {
|
|
||||||
buildQueue();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Otherwise, the call undergoes element queueing as normal. */
|
|
||||||
} else {
|
|
||||||
Collide.queue(element, opts.queue, function(next, clearQueue) {
|
|
||||||
/* If the clearQueue flag was passed in by the stop command, resolve this call's promise. (Promises can only be resolved once,
|
|
||||||
so it's fine if this is repeatedly triggered for each element in the associated call.) */
|
|
||||||
if (clearQueue === true) {
|
|
||||||
/* Do not continue with animation queueing. */
|
|
||||||
resolve(elements);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This flag indicates to the upcoming completeCall() function that this queue entry was initiated by Collide.
|
|
||||||
See completeCall() for further details. */
|
|
||||||
Collide.collideQueueEntryFlag = true;
|
|
||||||
|
|
||||||
buildQueue(next);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,301 +0,0 @@
|
|||||||
/* Forked from VelocityJS, MIT License: https://github.com/julianshapiro/velocity | Julian Shapiro http://twitter.com/shapiro */
|
|
||||||
|
|
||||||
import {dom} from 'ionic/util'
|
|
||||||
import {Collide} from './collide'
|
|
||||||
import {CSS} from './css'
|
|
||||||
import {completeCall} from './complete-call'
|
|
||||||
|
|
||||||
|
|
||||||
/************
|
|
||||||
Tick
|
|
||||||
************/
|
|
||||||
|
|
||||||
export function startTick() {
|
|
||||||
if (!Collide.State.isTicking && Collide.State.calls && Collide.State.calls.length) {
|
|
||||||
Collide.State.isTicking = true;
|
|
||||||
tick();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Note: All calls to Collide are pushed to the Collide.State.calls array, which is fully iterated through upon each tick. */
|
|
||||||
function tick(timestamp) {
|
|
||||||
/* An empty timestamp argument indicates that this is the first tick occurence since ticking was turned on.
|
|
||||||
We leverage this metadata to fully ignore the first tick pass since RAF's initial pass is fired whenever
|
|
||||||
the browser's next tick sync time occurs, which results in the first elements subjected to Collide
|
|
||||||
calls being animated out of sync with any elements animated immediately thereafter. In short, we ignore
|
|
||||||
the first RAF tick pass so that elements being immediately consecutively animated -- instead of simultaneously animated
|
|
||||||
by the same Collide call -- are properly batched into the same initial RAF tick and consequently remain in sync thereafter. */
|
|
||||||
|
|
||||||
if (timestamp) {
|
|
||||||
/* We ignore RAF's high resolution timestamp since it can be significantly offset when the browser is
|
|
||||||
under high stress; we opt for choppiness over allowing the browser to drop huge chunks of frames. */
|
|
||||||
var timeCurrent = (new Date).getTime();
|
|
||||||
|
|
||||||
|
|
||||||
/********************
|
|
||||||
Call Iteration
|
|
||||||
********************/
|
|
||||||
|
|
||||||
var callsLength = Collide.State.calls.length;
|
|
||||||
|
|
||||||
/* To speed up iterating over this array, it is compacted (falsey items -- calls that have completed -- are removed)
|
|
||||||
when its length has ballooned to a point that can impact tick performance. This only becomes necessary when animation
|
|
||||||
has been continuous with many elements over a long period of time; whenever all active calls are completed, completeCall() clears Collide.State.calls. */
|
|
||||||
if (callsLength > 10000) {
|
|
||||||
Collide.State.calls = compactSparseArray(Collide.State.calls);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Iterate through each active call. */
|
|
||||||
for (var i = 0; i < callsLength; i++) {
|
|
||||||
|
|
||||||
/* When a Collide call is completed, its Collide.State.calls entry is set to false. Continue on to the next call. */
|
|
||||||
if (!Collide.State.calls[i]) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/************************
|
|
||||||
Call-Wide Variables
|
|
||||||
************************/
|
|
||||||
|
|
||||||
var callContainer = Collide.State.calls[i],
|
|
||||||
call = callContainer[0],
|
|
||||||
opts = callContainer[2],
|
|
||||||
timeStart = callContainer[3],
|
|
||||||
firstTick = !!timeStart,
|
|
||||||
tweenDummyValue = null;
|
|
||||||
|
|
||||||
if (!call) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If timeStart is undefined, then this is the first time that this call has been processed by tick().
|
|
||||||
We assign timeStart now so that its value is as close to the real animation start time as possible.
|
|
||||||
(Conversely, had timeStart been defined when this call was added to Collide.State.calls, the delay
|
|
||||||
between that time and now would cause the first few frames of the tween to be skipped since
|
|
||||||
percentComplete is calculated relative to timeStart.) */
|
|
||||||
/* Further, subtract 16ms (the approximate resolution of RAF) from the current time value so that the
|
|
||||||
first tick iteration isn't wasted by animating at 0% tween completion, which would produce the
|
|
||||||
same style value as the element's current value. */
|
|
||||||
if (!timeStart) {
|
|
||||||
timeStart = Collide.State.calls[i][3] = timeCurrent - 16;
|
|
||||||
}
|
|
||||||
|
|
||||||
var percentComplete;
|
|
||||||
if (opts.percentComplete !== undefined) {
|
|
||||||
percentComplete = Math.max(Math.min(parseFloat(opts.percentComplete), 1), 0);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
/* The tween's completion percentage is relative to the tween's start time, not the tween's start value
|
|
||||||
(which would result in unpredictable tween durations since JavaScript's timers are not particularly accurate).
|
|
||||||
Accordingly, we ensure that percentComplete does not exceed 1. */
|
|
||||||
percentComplete = Math.min((timeCurrent - timeStart) / opts.duration, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**********************
|
|
||||||
Element Iteration
|
|
||||||
**********************/
|
|
||||||
|
|
||||||
/* For every call, iterate through each of the elements in its set. */
|
|
||||||
for (var j = 0, jj = call.length; j < jj; j++) {
|
|
||||||
var tweensContainer = call[j];
|
|
||||||
var element = tweensContainer.element;
|
|
||||||
var eleData = Collide.data(element);
|
|
||||||
|
|
||||||
/* Check to see if this element has been deleted midway through the animation by checking for the
|
|
||||||
continued existence of its data cache. If it's gone, skip animating this element. */
|
|
||||||
if (!eleData) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (eleData.startAddCls) {
|
|
||||||
for (var k = 0; k < eleData.startAddCls.length; k++) {
|
|
||||||
element.classList.add(eleData.startAddCls[k]);
|
|
||||||
}
|
|
||||||
eleData.startAddCls = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (eleData.startRmvCls) {
|
|
||||||
for (var k = 0; k < eleData.startRmvCls.length; k++) {
|
|
||||||
element.classList.remove(eleData.startRmvCls[k]);
|
|
||||||
}
|
|
||||||
eleData.startRmvCls = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var transformPropertyExists = false;
|
|
||||||
|
|
||||||
|
|
||||||
/**********************************
|
|
||||||
Display & Visibility Toggling
|
|
||||||
**********************************/
|
|
||||||
|
|
||||||
/* If the display option is set to non-'none', set it upfront so that the element can become visible before tweening begins.
|
|
||||||
(Otherwise, display's 'none' value is set in completeCall() once the animation has completed.) */
|
|
||||||
if (opts.display !== undefined && opts.display !== null && opts.display !== 'none') {
|
|
||||||
if (opts.display === 'flex') {
|
|
||||||
|
|
||||||
for (var f = 0; f < flexValues.length; f++) {
|
|
||||||
CSS.setPropertyValue(element, 'display', flexValues[f]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CSS.setPropertyValue(element, 'display', opts.display);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Same goes with the visibility option, but its 'none' equivalent is 'hidden'. */
|
|
||||||
if (opts.visibility !== undefined && opts.visibility !== 'hidden') {
|
|
||||||
CSS.setPropertyValue(element, 'visibility', opts.visibility);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/************************
|
|
||||||
Property Iteration
|
|
||||||
************************/
|
|
||||||
|
|
||||||
/* For every element, iterate through each property. */
|
|
||||||
for (var property in tweensContainer) {
|
|
||||||
|
|
||||||
/* Note: In addition to property tween data, tweensContainer contains a reference to its associated element. */
|
|
||||||
if (property !== 'element') {
|
|
||||||
var tween = tweensContainer[property];
|
|
||||||
|
|
||||||
var currentValue;
|
|
||||||
|
|
||||||
/* Easing can either be a pre-genereated function or a string that references a pre-registered easing
|
|
||||||
on the Collide.Easings object. In either case, return the appropriate easing *function*. */
|
|
||||||
var easing = (typeof tween.easing === 'string' ? Collide.Easings[tween.easing] : tween.easing);
|
|
||||||
|
|
||||||
/******************************
|
|
||||||
Current Value Calculation
|
|
||||||
******************************/
|
|
||||||
|
|
||||||
/* If this is the last tick pass (if we've reached 100% completion for this tween),
|
|
||||||
ensure that currentValue is explicitly set to its target endValue so that it's not subjected to any rounding. */
|
|
||||||
if (percentComplete === 1) {
|
|
||||||
currentValue = tween.endValue;
|
|
||||||
|
|
||||||
/* Otherwise, calculate currentValue based on the current delta from startValue. */
|
|
||||||
} else {
|
|
||||||
var tweenDelta = tween.endValue - tween.startValue;
|
|
||||||
currentValue = tween.startValue + (tweenDelta * easing(percentComplete, opts, tweenDelta));
|
|
||||||
|
|
||||||
/* If no value change is occurring, don't proceed with DOM updating. */
|
|
||||||
if (!firstTick && (currentValue === tween.currentValue)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tween.currentValue = currentValue;
|
|
||||||
|
|
||||||
/* If we're tweening a fake 'tween' property in order to log transition values, update the one-per-call variable so that
|
|
||||||
it can be passed into the progress callback. */
|
|
||||||
if (property === 'tween') {
|
|
||||||
tweenDummyValue = currentValue;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
/******************
|
|
||||||
Hooks: Part I
|
|
||||||
******************/
|
|
||||||
|
|
||||||
/* For hooked properties, the newly-updated rootPropertyValueCache is cached onto the element so that it can be used
|
|
||||||
for subsequent hooks in this call that are associated with the same root property. If we didn't cache the updated
|
|
||||||
rootPropertyValue, each subsequent update to the root property in this tick pass would reset the previous hook's
|
|
||||||
updates to rootPropertyValue prior to injection. A nice performance byproduct of rootPropertyValue caching is that
|
|
||||||
subsequently chained animations using the same hookRoot but a different hook can use this cached rootPropertyValue. */
|
|
||||||
if (CSS.Hooks.registered[property]) {
|
|
||||||
var hookRoot = CSS.Hooks.getRoot(property),
|
|
||||||
rootPropertyValueCache = eleData.rootPropertyValueCache[hookRoot];
|
|
||||||
|
|
||||||
if (rootPropertyValueCache) {
|
|
||||||
tween.rootPropertyValue = rootPropertyValueCache;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*****************
|
|
||||||
DOM Update
|
|
||||||
*****************/
|
|
||||||
|
|
||||||
/* setPropertyValue() returns an array of the property name and property value post any normalization that may have been performed. */
|
|
||||||
/* Note: To solve an IE<=8 positioning bug, the unit type is dropped when setting a property value of 0. */
|
|
||||||
var adjustedSetData = CSS.setPropertyValue(element, /* SET */
|
|
||||||
property,
|
|
||||||
tween.currentValue + (parseFloat(currentValue) === 0 ? '' : tween.unitType),
|
|
||||||
tween.rootPropertyValue,
|
|
||||||
tween.scrollData);
|
|
||||||
|
|
||||||
|
|
||||||
/*******************
|
|
||||||
Hooks: Part II
|
|
||||||
*******************/
|
|
||||||
|
|
||||||
/* Now that we have the hook's updated rootPropertyValue (the post-processed value provided by adjustedSetData), cache it onto the element. */
|
|
||||||
if (CSS.Hooks.registered[property]) {
|
|
||||||
/* Since adjustedSetData contains normalized data ready for DOM updating, the rootPropertyValue needs to be re-extracted from its normalized form. ?? */
|
|
||||||
if (CSS.Normalizations.registered[hookRoot]) {
|
|
||||||
eleData.rootPropertyValueCache[hookRoot] = CSS.Normalizations.registered[hookRoot]('extract', null, adjustedSetData[1]);
|
|
||||||
} else {
|
|
||||||
eleData.rootPropertyValueCache[hookRoot] = adjustedSetData[1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/***************
|
|
||||||
Transforms
|
|
||||||
***************/
|
|
||||||
|
|
||||||
/* Flag whether a transform property is being animated so that flushTransformCache() can be triggered once this tick pass is complete. */
|
|
||||||
if (adjustedSetData[0] === 'transform') {
|
|
||||||
transformPropertyExists = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
} // END: if (property !== 'element')
|
|
||||||
|
|
||||||
} // END: for (var property in tweensContainer)
|
|
||||||
|
|
||||||
if (transformPropertyExists) {
|
|
||||||
CSS.flushTransformCache(element);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // END: for (var j = 0, jj = call.length; j < jj; j++)
|
|
||||||
|
|
||||||
|
|
||||||
/* The non-'none' display value is only applied to an element once -- when its associated call is first ticked through.
|
|
||||||
Accordingly, it's set to false so that it isn't re-processed by this call in the next tick. */
|
|
||||||
if (opts.display !== undefined && opts.display !== 'none') {
|
|
||||||
Collide.State.calls[i][2].display = false;
|
|
||||||
}
|
|
||||||
if (opts.visibility !== undefined && opts.visibility !== 'hidden') {
|
|
||||||
Collide.State.calls[i][2].visibility = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Pass the elements and the timing data (percentComplete, msRemaining, timeStart, tweenDummyValue) into the progress callback. */
|
|
||||||
if (opts.progress) {
|
|
||||||
opts.progress.call(callContainer[1],
|
|
||||||
callContainer[1],
|
|
||||||
percentComplete,
|
|
||||||
Math.max(0, (timeStart + opts.duration) - timeCurrent),
|
|
||||||
timeStart,
|
|
||||||
tweenDummyValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If this call has finished tweening, pass its index to completeCall() to handle call cleanup. */
|
|
||||||
if (percentComplete === 1 || opts.percentComplete !== undefined) {
|
|
||||||
completeCall(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // END: for (var i = 0; i < callsLength; i++)
|
|
||||||
|
|
||||||
} // END: if (timestamp)
|
|
||||||
|
|
||||||
/* Note: completeCall() sets the isTicking flag to false when the last call on Collide.State.calls has completed. */
|
|
||||||
if (Collide.State.isTicking) {
|
|
||||||
dom.raf(tick);
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
const flexValues = [ '-webkit-box', '-moz-box', '-ms-flexbox', '-webkit-flex' ];
|
|
@ -15,132 +15,45 @@ let scale = 0.6;
|
|||||||
})
|
})
|
||||||
class IonicApp {
|
class IonicApp {
|
||||||
|
|
||||||
fadeOut() {
|
constructor() {
|
||||||
console.debug('fadeOut');
|
this.animation = new Animation();
|
||||||
|
|
||||||
var animation = new Animation();
|
this.animation
|
||||||
|
.duration(1000)
|
||||||
animation.duration(1000);
|
.easing('ease-in-out');
|
||||||
animation.easing('linear');
|
|
||||||
|
|
||||||
var row1 = new Animation();
|
|
||||||
row1.elements( document.querySelectorAll('.square') )
|
|
||||||
.to('opacity', opacity)
|
|
||||||
.to('translateX', translateX)
|
|
||||||
.to('translateY', translateX)
|
|
||||||
.to('rotateZ', rotateZ)
|
|
||||||
.to('scale', scale);
|
|
||||||
|
|
||||||
animation.addChild(row1);
|
|
||||||
|
|
||||||
|
|
||||||
var row2 = new Animation();
|
var row1 = new Animation( document.querySelectorAll('.square') );
|
||||||
row2.elements( document.querySelectorAll('.square2') );
|
row1
|
||||||
|
.from('opacity', 1)
|
||||||
|
.to('opacity', 0)
|
||||||
|
.beforePlay.addClass('added-before-play')
|
||||||
|
.afterFinish.addClass('added-after-finish')
|
||||||
|
|
||||||
row2.to('opacity', opacity);
|
var row2 = new Animation( document.querySelectorAll('.square2') );
|
||||||
row2.to('translateX', '-100px');
|
row2
|
||||||
row2.to('translateY', '-100px');
|
.to('transform', 'rotate(90deg) scale(0.5)')
|
||||||
row2.to('rotateZ', '-180deg');
|
.beforePlay.addClass('added-before-play')
|
||||||
row2.to('scale', 0.4);
|
.afterFinish.addClass('added-after-finish')
|
||||||
|
|
||||||
animation.addChild(row2);
|
this.animation.children(row1, row2);
|
||||||
|
|
||||||
let q = animation.start();
|
|
||||||
|
|
||||||
q.then(()=> {
|
|
||||||
console.log('fade out complete')
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
play() {
|
||||||
fadeIn() {
|
console.debug('play');
|
||||||
console.debug('fadeIn');
|
this.animation.play();;
|
||||||
|
|
||||||
var animation = new Animation();
|
|
||||||
|
|
||||||
animation.duration(1000);
|
|
||||||
animation.easing('linear');
|
|
||||||
|
|
||||||
|
|
||||||
var row1 = new Animation();
|
|
||||||
row1.elements( document.querySelectorAll('.square') );
|
|
||||||
|
|
||||||
row1.to('opacity', 1);
|
|
||||||
row1.to('translateX', 0);
|
|
||||||
row1.to('translateY', 0);
|
|
||||||
row1.to('rotateZ', 0);
|
|
||||||
row1.to('scale', 1);
|
|
||||||
|
|
||||||
animation.addChild(row1);
|
|
||||||
|
|
||||||
|
|
||||||
var row2 = new Animation();
|
|
||||||
row2.elements( document.querySelectorAll('.square2') );
|
|
||||||
|
|
||||||
row2.to('opacity', 1);
|
|
||||||
row2.to('translateX', 0);
|
|
||||||
row2.to('translateY', 0);
|
|
||||||
row2.to('rotateZ', 0);
|
|
||||||
row2.to('scale', 1);
|
|
||||||
|
|
||||||
animation.addChild(row2);
|
|
||||||
|
|
||||||
let q = animation.start();
|
|
||||||
|
|
||||||
q.then(()=> {
|
|
||||||
console.log('fade in complete')
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stop() {
|
pause() {
|
||||||
this.animation.stop();
|
console.debug('pause');
|
||||||
|
|
||||||
|
this.animation.pause();
|
||||||
}
|
}
|
||||||
|
|
||||||
percent(ev) {
|
progress(ev) {
|
||||||
let percentComplete = parseFloat(ev.srcElement.value) / 100;
|
let value = ev.srcElement.value;
|
||||||
|
this.animation.progress(value);
|
||||||
if (!this.percentAnimation) {
|
|
||||||
this.percentAnimation = new Animation();
|
|
||||||
// this.percentAnimation.name = 'top';
|
|
||||||
|
|
||||||
var row1 = new Animation();
|
|
||||||
row1.elements( document.querySelectorAll('.square') )
|
|
||||||
.to('opacity', opacity)
|
|
||||||
.to('translateX', translateX)
|
|
||||||
.to('translateY', translateX)
|
|
||||||
.to('rotateZ', rotateZ)
|
|
||||||
.to('scale', scale);
|
|
||||||
|
|
||||||
this.percentAnimation.addChild(row1);
|
|
||||||
|
|
||||||
var row2 = new Animation();
|
|
||||||
row2.elements( document.querySelectorAll('.square2') );
|
|
||||||
|
|
||||||
row2.to('opacity', opacity);
|
|
||||||
row2.to('translateX', '-100px');
|
|
||||||
row2.to('translateY', '-100px');
|
|
||||||
row2.to('rotateZ', '-180deg');
|
|
||||||
row2.to('scale', 0.4);
|
|
||||||
|
|
||||||
this.percentAnimation.addChild(row2);
|
|
||||||
|
|
||||||
this.percentAnimation.ready();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.percentAnimation.percent(percentComplete);
|
|
||||||
}
|
|
||||||
|
|
||||||
velocityStart() {
|
|
||||||
var elements = document.querySelectorAll('.square');
|
|
||||||
Velocity(elements, {
|
|
||||||
opacity: 0,
|
|
||||||
translateX: '100px',
|
|
||||||
rotateZ: '90deg'
|
|
||||||
}, 2000);
|
|
||||||
|
|
||||||
// setTimeout(function() {
|
|
||||||
// Velocity(elements, "stop");
|
|
||||||
// }, 1000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Collide Tests</title>
|
<title>Animation Tests</title>
|
||||||
|
<script src="./web-animations-js/web-animations-next.dev.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
@ -21,20 +22,13 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<input type="range" (input)="percent($event)" value="0" min="0" max="100" style="width:200px">
|
<input type="range" (input)="progress($event)" value="0" min="0" step="0.001" max="1" style="width:200px">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<button class="button button-primary" (click)="fadeOut($event)">Out</button>
|
<button class="button button-primary" (click)="play($event)">Play</button>
|
||||||
<button class="button button-primary" (click)="fadeIn($event)">In</button>
|
<button class="button button-primary" (click)="pause($event)">Pause</button>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
||||||
<!-- <p>
|
|
||||||
<button class="button button-primary" (click)="velocityStart($event)">Velocity Start</button>
|
|
||||||
</p> -->
|
|
||||||
|
|
||||||
<script src="http://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.2/velocity.js"></script>
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -124,7 +124,7 @@ export class NavBase {
|
|||||||
leavingItem.state = ACTIVELY_LEAVING_STATE;
|
leavingItem.state = ACTIVELY_LEAVING_STATE;
|
||||||
|
|
||||||
// start the transition
|
// start the transition
|
||||||
transAnimation.start().then(() => {
|
transAnimation.play().then(() => {
|
||||||
|
|
||||||
// transition has completed, update each item's state
|
// transition has completed, update each item's state
|
||||||
enteringItem.state = ACTIVE_STATE;
|
enteringItem.state = ACTIVE_STATE;
|
||||||
|
@ -41,7 +41,7 @@ export * from 'ionic/engine/engine'
|
|||||||
export * from 'ionic/engine/cordova/cordova'
|
export * from 'ionic/engine/cordova/cordova'
|
||||||
export * from 'ionic/engine/electron/electron'
|
export * from 'ionic/engine/electron/electron'
|
||||||
|
|
||||||
export * from 'ionic/collide/animation'
|
export * from 'ionic/animations/animation'
|
||||||
export * from 'ionic/transitions/transition'
|
export * from 'ionic/transitions/transition'
|
||||||
export * from 'ionic/transitions/none-transition'
|
export * from 'ionic/transitions/none-transition'
|
||||||
export * from 'ionic/transitions/ios-transition'
|
export * from 'ionic/transitions/ios-transition'
|
||||||
|
@ -1,17 +1,21 @@
|
|||||||
import {Animation} from '../collide/animation';
|
import {Animation} from '../animations/animation';
|
||||||
import {addEasing} from '../collide/easing';
|
|
||||||
import {rafPromise} from '../util/dom'
|
import {rafPromise} from '../util/dom'
|
||||||
import {Transition} from './transition'
|
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 OPACITY = 'opacity';
|
||||||
|
const TRANSFORM = 'transform';
|
||||||
|
|
||||||
|
const CENTER = 'none';
|
||||||
|
const OFF_RIGHT = 'translate3d(100%,0px,0px)';
|
||||||
|
const OFF_LEFT = 'translate3d(-33%,0px,0px)';
|
||||||
const OFF_OPACITY = 0.8;
|
const OFF_OPACITY = 0.8;
|
||||||
|
|
||||||
const TRANSLATE_X = 'translateX';
|
const SHOW_TOOLBAR_CSS = 'show-toolbar';
|
||||||
const OPACITY = 'opacity';
|
const SHOW_NAV_ITEM_CSS = 'show-nav-item';
|
||||||
|
|
||||||
|
|
||||||
class IOSTransition extends Animation {
|
class IOSTransition extends Animation {
|
||||||
@ -21,7 +25,6 @@ class IOSTransition extends Animation {
|
|||||||
|
|
||||||
// global duration and easing for all child animations
|
// global duration and easing for all child animations
|
||||||
this.duration(DURATION);
|
this.duration(DURATION);
|
||||||
this.easing('ios');
|
|
||||||
|
|
||||||
// get the entering and leaving items
|
// get the entering and leaving items
|
||||||
this.enteringItem = navCtrl.getStagedEnteringItem();
|
this.enteringItem = navCtrl.getStagedEnteringItem();
|
||||||
@ -50,72 +53,73 @@ class IOSTransition extends Animation {
|
|||||||
// entering item moves to center
|
// entering item moves to center
|
||||||
// before starting, set enteringItem to display: block
|
// before starting, set enteringItem to display: block
|
||||||
enteringContent
|
enteringContent
|
||||||
.addStartClass('show-nav-item')
|
.beforePlay.addClass(SHOW_NAV_ITEM_CSS)
|
||||||
.to(TRANSLATE_X, 0)
|
.to(TRANSFORM, CENTER)
|
||||||
.to(OPACITY, 1);
|
.to(OPACITY, 1);
|
||||||
|
|
||||||
enteringTitle
|
enteringTitle
|
||||||
.to(TRANSLATE_X, 0)
|
.to(TRANSFORM, CENTER)
|
||||||
.to(OPACITY, 1);
|
.to(OPACITY, 1);
|
||||||
|
|
||||||
enteringToolbars
|
enteringToolbars
|
||||||
.addStartClass('show-toolbar');
|
.beforePlay.addClass(SHOW_TOOLBAR_CSS);
|
||||||
|
|
||||||
// leaving view moves off screen
|
// leaving view moves off screen
|
||||||
// when completed, set leavingItem to display: none
|
// when completed, set leavingItem to display: none
|
||||||
leavingContent
|
leavingContent
|
||||||
.removeEndClass('show-nav-item')
|
.afterFinish.removeClass(SHOW_NAV_ITEM_CSS)
|
||||||
.from(TRANSLATE_X, 0)
|
.from(TRANSFORM, CENTER)
|
||||||
.from(OPACITY, 1);
|
.from(OPACITY, 1);
|
||||||
|
|
||||||
leavingToolbars
|
leavingToolbars
|
||||||
.removeEndClass('show-toolbar');
|
.afterFinish.removeClass(SHOW_TOOLBAR_CSS);
|
||||||
|
|
||||||
leavingTitle
|
leavingTitle
|
||||||
.from(TRANSLATE_X, 0)
|
.from(TRANSFORM, CENTER)
|
||||||
.from(OPACITY, 1);
|
.from(OPACITY, 1);
|
||||||
|
|
||||||
// set properties depending on direction
|
// set properties depending on direction
|
||||||
if (opts.direction === 'back') {
|
if (opts.direction === 'back') {
|
||||||
// back direction
|
// back direction
|
||||||
enteringContent
|
enteringContent
|
||||||
.from(TRANSLATE_X, OFF_LEFT)
|
.from(TRANSFORM, OFF_LEFT)
|
||||||
.from(OPACITY, OFF_OPACITY)
|
.from(OPACITY, OFF_OPACITY)
|
||||||
.to(OPACITY, 1);
|
.to(OPACITY, 1);
|
||||||
|
|
||||||
enteringTitle
|
enteringTitle
|
||||||
.from(TRANSLATE_X, OFF_LEFT)
|
.from(TRANSFORM, OFF_LEFT)
|
||||||
.from(OPACITY, 0)
|
.from(OPACITY, 0)
|
||||||
.to(OPACITY, 1);
|
.to(OPACITY, 1);
|
||||||
|
|
||||||
leavingContent
|
leavingContent
|
||||||
.to(TRANSLATE_X, OFF_RIGHT)
|
.to(TRANSFORM, OFF_RIGHT)
|
||||||
.to(OPACITY, 1);
|
.to(OPACITY, 1);
|
||||||
|
|
||||||
leavingTitle
|
leavingTitle
|
||||||
.to(TRANSLATE_X, OFF_RIGHT)
|
.to(TRANSFORM, OFF_RIGHT)
|
||||||
.to(OPACITY, 1);
|
.to(OPACITY, 1);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// forward direction
|
// forward direction
|
||||||
enteringContent
|
enteringContent
|
||||||
.from(TRANSLATE_X, OFF_RIGHT)
|
.from(TRANSFORM, OFF_RIGHT)
|
||||||
.from(OPACITY, 1);
|
.from(OPACITY, 1);
|
||||||
|
|
||||||
enteringTitle
|
enteringTitle
|
||||||
.from(TRANSLATE_X, OFF_RIGHT);
|
.from(TRANSFORM, OFF_RIGHT);
|
||||||
|
|
||||||
leavingContent
|
leavingContent
|
||||||
.to(TRANSLATE_X, OFF_LEFT)
|
.to(TRANSFORM, OFF_LEFT)
|
||||||
.to(OPACITY, OFF_OPACITY);
|
.to(OPACITY, OFF_OPACITY);
|
||||||
|
|
||||||
leavingTitle
|
leavingTitle
|
||||||
.to(TRANSLATE_X, OFF_LEFT)
|
.to(TRANSFORM, OFF_LEFT)
|
||||||
.to(OPACITY, 0);
|
.to(OPACITY, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// set child animations
|
// set child animations
|
||||||
this.setChildren([enteringContent, enteringToolbars, enteringTitle, leavingContent, leavingToolbars, leavingTitle]);
|
this.children(enteringContent, enteringToolbars, enteringTitle, leavingContent, leavingToolbars, leavingTitle);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stage() {
|
stage() {
|
||||||
@ -124,6 +128,4 @@ class IOSTransition extends Animation {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addEasing('ios', EASING_FN);
|
|
||||||
|
|
||||||
Transition.register('ios', IOSTransition);
|
Transition.register('ios', IOSTransition);
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
import {Transition} from './transition'
|
import {Transition} from './transition'
|
||||||
|
|
||||||
|
|
||||||
|
const SHOW_TOOLBAR_CSS = 'show-toolbar';
|
||||||
|
const SHOW_NAV_ITEM_CSS = 'show-nav-item';
|
||||||
|
|
||||||
|
|
||||||
class NoneTransition {
|
class NoneTransition {
|
||||||
|
|
||||||
constructor(navCtrl) {
|
constructor(navCtrl) {
|
||||||
@ -10,26 +14,24 @@ class NoneTransition {
|
|||||||
|
|
||||||
// show entering contet
|
// show entering contet
|
||||||
let enteringContent = enteringItem.getContent();
|
let enteringContent = enteringItem.getContent();
|
||||||
enteringContent.classList.add('show-nav-item');
|
enteringContent.classList.add(SHOW_NAV_ITEM_CSS);
|
||||||
enteringContent.style.transform = 'translateX(0%)';
|
|
||||||
|
|
||||||
// show entering headers
|
// show entering headers
|
||||||
let enteringToolbars = enteringItem.getToolbars();
|
let enteringToolbars = enteringItem.getToolbars();
|
||||||
for (let i = 0; i < enteringToolbars.length; i++) {
|
for (let i = 0; i < enteringToolbars.length; i++) {
|
||||||
enteringToolbars[i].classList.add('show-toolbar');
|
enteringToolbars[i].classList.add(SHOW_TOOLBAR_CSS);
|
||||||
enteringToolbars[i].style.transform = 'translateX(0%)';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// hide the leaving item
|
// hide the leaving item
|
||||||
if (leavingItem) {
|
if (leavingItem) {
|
||||||
let leavingContent = leavingItem.getContent();
|
let leavingContent = leavingItem.getContent();
|
||||||
if (leavingContent) {
|
if (leavingContent) {
|
||||||
leavingContent.classList.remove('show-nav-item');
|
leavingContent.classList.remove(SHOW_NAV_ITEM_CSS);
|
||||||
}
|
}
|
||||||
|
|
||||||
let leavingToolbars = leavingItem.getToolbars();
|
let leavingToolbars = leavingItem.getToolbars();
|
||||||
for (let i = 0; i < leavingToolbars.length; i++) {
|
for (let i = 0; i < leavingToolbars.length; i++) {
|
||||||
leavingToolbars[i].classList.remove('show-toolbar');
|
leavingToolbars[i].classList.remove(SHOW_TOOLBAR_CSS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -39,7 +41,7 @@ class NoneTransition {
|
|||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
start() {
|
play() {
|
||||||
// immediately resolve
|
// immediately resolve
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,11 @@
|
|||||||
Loading...
|
Loading...
|
||||||
</ion-app>
|
</ion-app>
|
||||||
|
|
||||||
|
<!-- scripts auto-added by angular build process -->
|
||||||
$SCRIPTS$
|
$SCRIPTS$
|
||||||
|
|
||||||
|
<!-- web animations polyfill for non-chrome browsers -->
|
||||||
|
<script src="/polyfills/web-animations.min.js"></script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
Reference in New Issue
Block a user