collide updates

This commit is contained in:
Adam Bradley
2015-04-26 11:59:44 -05:00
parent e05a87518b
commit c6b6a07d06
12 changed files with 179 additions and 150 deletions

View File

@ -1,9 +1,11 @@
/* Forked from Velocity.js, MIT License. Julian Shapiro http://twitter.com/shapiro */
/* Forked from VelocityJS: https://github.com/julianshapiro/velocity | MIT License. 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;
@ -587,128 +589,6 @@ export function animationProcess(action, elements, elementsIndex, options, prope
}
/***************************
parsePropertyValue(), 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. */
function calculateUnitRatios() {
/**************************************************************
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 x = 0; x < overflows.length; x++) {
Collide.CSS.setPropertyValue(dummy, cssPropNames[x], '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 x = 0; x < overflows.length; x++) {
Collide.CSS.setPropertyValue(dummy, cssPropNames[x], 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. */
callUnitConversionData.remToPx = parseFloat(CSS.getPropertyValue(document.body, 'fontSize')) || 16; /* GET */
}
/* 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;
} // END: calculateUnitRatios()
/****************************************
parsePropertyValue(), Unit Conversion
*****************************************/
@ -733,7 +613,7 @@ export function animationProcess(action, elements, elementsIndex, options, prope
} 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();
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. */

View File

@ -1,4 +1,4 @@
/* Forked from Collide.js, MIT License. Julian Shapiro http://twitter.com/shapiro */
/* Forked from VelocityJS: https://github.com/julianshapiro/velocity | MIT License. Julian Shapiro http://twitter.com/shapiro */
import {Collide} from './collide'
import {animationProcess} from './animation-process'
@ -50,7 +50,7 @@ export function animationStart(elements, options, propertiesMap) {
if (elements && elements.length) {
for (var i = 0, l = elements.length; i < l; i++) {
if (elements[i] && elements[i].parentElement) {
if (elements[i]) {
promises.push(
animationProcess('start', elements, i, options, propertiesMap, callUnitConversionData, call)

View File

@ -1,4 +1,4 @@
/* Forked from Collide.js, MIT License. Julian Shapiro http://twitter.com/shapiro */
/* Forked from VelocityJS: https://github.com/julianshapiro/velocity | MIT License. Julian Shapiro http://twitter.com/shapiro */
import {Collide} from './collide'

View File

@ -39,6 +39,8 @@ export class Animation {
percent(ratio) {
this._options.percentComplete = parseFloat(ratio);
animationStart(this._elements, this._options, this._properties);
startTick();
}

View File

@ -0,0 +1,127 @@
/* Forked from VelocityJS: https://github.com/julianshapiro/velocity | MIT License. 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 x = 0; x < overflows.length; x++) {
Collide.CSS.setPropertyValue(dummy, cssPropNames[x], '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 x = 0; x < overflows.length; x++) {
Collide.CSS.setPropertyValue(dummy, cssPropNames[x], 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;

View File

@ -1,4 +1,4 @@
/* Forked from Collide.js, MIT License. Julian Shapiro http://twitter.com/shapiro */
/* Forked from VelocityJS: https://github.com/julianshapiro/velocity | MIT License. Julian Shapiro http://twitter.com/shapiro */
import {dom} from 'ionic/util'

View File

@ -1,4 +1,4 @@
/* Forked from Velocity.js, MIT License. Julian Shapiro http://twitter.com/shapiro */
/* Forked from VelocityJS: https://github.com/julianshapiro/velocity | MIT License. Julian Shapiro http://twitter.com/shapiro */
import {Collide} from './collide'
import {CSS} from './css'
@ -9,7 +9,7 @@ import {CSS} from './css'
***************************/
/* Note: Unlike tick(), which processes all active calls at once, call completion is handled on a per-call basis. */
export function completeCall (callIndex, isStopped) {
export function completeCall(callIndex, isStopped) {
/* Ensure the call exists. */
if (!Collide.State.calls[callIndex]) {
@ -88,7 +88,7 @@ export function completeCall (callIndex, isStopped) {
try {
opts.complete.call(elements, elements);
} catch (error) {
setTimeout(function() { throw error; }, 1);
setTimeout(function() { throw error; });
}
}

View File

@ -1,4 +1,4 @@
/* Forked from Collide.js, MIT License. Julian Shapiro http://twitter.com/shapiro */
/* Forked from VelocityJS: https://github.com/julianshapiro/velocity | MIT License. Julian Shapiro http://twitter.com/shapiro */
import {Collide} from './collide'

View File

@ -1,4 +1,4 @@
/* Forked from Collide.js, MIT License. Julian Shapiro http://twitter.com/shapiro */
/* Forked from VelocityJS: https://github.com/julianshapiro/velocity | MIT License. Julian Shapiro http://twitter.com/shapiro */
import * as util from 'ionic/util/util'
import {Collide} from './collide'

View File

@ -1,4 +1,4 @@
/* Forked from Collide.js, MIT License. Julian Shapiro http://twitter.com/shapiro */
/* Forked from VelocityJS: https://github.com/julianshapiro/velocity | MIT License. Julian Shapiro http://twitter.com/shapiro */
import {dom} from 'ionic/util'
import {Collide} from './collide'
@ -29,6 +29,8 @@ function tick(timestamp) {
var percentCompleteStop = false;
if (timestamp) {
console.debug('tick, calls', Collide.State.calls.length)
/* 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();
@ -82,7 +84,6 @@ function tick(timestamp) {
/* 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. */
debugger
var percentComplete;
if (opts.percentComplete !== undefined) {
percentCompleteStop = true;
@ -276,7 +277,7 @@ function tick(timestamp) {
} // 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 || percentCompleteStop) {
if (Collide.State.isTicking) {
dom.raf(tick);
}

View File

@ -8,7 +8,7 @@
<div class="red square" style="position:absolute; width:100px; height:100px; background:red; top: 0; left: 0;"></div>
<div class="green square" style="position:absolute; width:100px; height:100px; background:green; top: 0; left: 100px;"></div>
<div class="blue square" style="position:absolute; width:100px; height:100px; background:blue; top: 0; left: 200px;"></div>
<!-- <div class="blue square" style="position:absolute; width:100px; height:100px; background:blue; top: 0; left: 200px;"></div> -->
</div>
@ -17,7 +17,8 @@
</p>
<p>
<button class="button button-primary" (click)="start($event)">Collide Start</button>
<button class="button button-primary" (click)="fadeOut($event)">Collide Fade Out</button>
<button class="button button-primary" (click)="fadeIn($event)">Collide Fade In</button>
</p>

View File

@ -10,22 +10,40 @@ import {Animation} from 'ionic/ionic';
class IonicApp {
constructor() {
this.animation = new Animation();
this.animation.elements( document.querySelectorAll('.square') );
this.animation.debug(2);
this.animation.duration(500);
this.animation.easing('swing');
this.animation.property('opacity', 0);
}
start() {
let q = this.animation.start();
fadeOut() {
let animation = new Animation();
animation.elements( document.querySelectorAll('.square') );
animation.debug(2);
animation.duration(500);
animation.easing('swing');
animation.property('opacity', 0);
let q = animation.start();
q.then(()=> {
console.log('animation complete')
console.log('fade out complete')
});
}
fadeIn() {
let animation = new Animation();
animation.elements( document.querySelectorAll('.square') );
animation.debug(2);
animation.duration(500);
animation.easing('swing');
animation.property('opacity', 1);
let q = animation.start();
q.then(()=> {
console.log('fade in complete')
});
}
@ -41,7 +59,7 @@ class IonicApp {
}
velocityStart() {
Velocity(document.querySelectorAll('.square'), { opacity: 0 }, 500);
window.Velocity(document.querySelectorAll('.square'), { opacity: 0 }, 500);
}
}