Comparing version 0.2.3-beta0 to 0.3.0-alpha.0
@@ -101,38 +101,3 @@ # Contributing to Famo.us | ||
## Issues | ||
Famo.us is composed of several repositories, and it helps if bugs are filed in | ||
the issues section of the right repository. To help you check if your issue has | ||
already been filed or to help you file it in the right place in case it hasn't | ||
we've created the following guidelines: | ||
Below is a list of links to the issues page for all the famous framework | ||
repositories. | ||
* [famous][famous-issues] | ||
* [core][core-issues] | ||
* [events][events-issues] | ||
* [inputs][inputs-issues] | ||
* [math][math-issues] | ||
* [modifiers][modifiers-issues] | ||
* [physics][physics-issues] | ||
* [surfaces][surfaces-issues] | ||
* [transitions][transitions-issues] | ||
* [utilities][utilities-issues] | ||
* [views][views-issues] | ||
* [widgets][widgets-issues] | ||
[famous-issues]: https://github.com/famous/famous/issues | ||
[core-issues]: https://github.com/famous/core/issues | ||
[events-issues]: https://github.com/famous/events/issues | ||
[inputs-issues]: https://github.com/famous/inputs/issues | ||
[math-issues]: https://github.com/famous/math/issues | ||
[modifiers-issues]: https://github.com/famous/modifiers/issues | ||
[physics-issues]: https://github.com/famous/physics/issues | ||
[surfaces-issues]: https://github.com/famous/surfaces/issues | ||
[transitions-issues]: https://github.com/famous/transitions/issues | ||
[utilities-issues]: https://github.com/famous/utilities/issues | ||
[views-issues]: https://github.com/famous/views/issues | ||
[widgets-issues]: https://github.com/famous/widgets/issues | ||
@@ -139,0 +104,0 @@ [famous]: https://github.com/famous/famous |
@@ -16,3 +16,3 @@ /* This Source Code Form is subject to the terms of the Mozilla Public | ||
var _originZeroZero = [0, 0]; | ||
var _zeroZero = [0, 0]; | ||
@@ -48,4 +48,4 @@ function _getElementSize(element) { | ||
opacity: 1, | ||
origin: _originZeroZero, | ||
align: null, | ||
origin: _zeroZero, | ||
align: _zeroZero, | ||
size: this._size | ||
@@ -52,0 +52,0 @@ }; |
@@ -286,3 +286,3 @@ /* This Source Code Form is subject to the terms of the Mozilla Public | ||
this._matrix = matrix; | ||
var aaMatrix = this.size ? Transform.thenMove(matrix, [-this._size[0]*origin[0], -this._size[1]*origin[1], 0]) : matrix; | ||
var aaMatrix = this._size ? Transform.thenMove(matrix, [-this._size[0]*origin[0], -this._size[1]*origin[1], 0]) : matrix; | ||
_setMatrix(target, aaMatrix); | ||
@@ -289,0 +289,0 @@ this._transformDirty = false; |
@@ -28,3 +28,3 @@ /* This Source Code Form is subject to the terms of the Mozilla Public | ||
* @method get | ||
* @param {Number} id entity reigstration id | ||
* @param {Number} id entity registration id | ||
* @return {Surface} entity in the global index | ||
@@ -41,4 +41,4 @@ */ | ||
* @method set | ||
* @param {Number} id entity reigstration id | ||
* @return {Surface} entity to add to the global index | ||
* @param {Number} id entity registration id | ||
* @param {Surface} entity to add to the global index | ||
*/ | ||
@@ -68,3 +68,3 @@ function set(id, entity) { | ||
* @method unregister | ||
* @param {Number} id entity reigstration id | ||
* @param {Number} id entity registration id | ||
*/ | ||
@@ -71,0 +71,0 @@ function unregister(id) { |
module.exports = { | ||
Context: require('./Context'), | ||
ElementOutput: require('./ElementOutput'), | ||
ElementAllocator: require('./ElementAllocator'), | ||
ElementOutput: require('./ElementOutput'), | ||
Engine: require('./Engine'), | ||
Entity: require('./Entity'), | ||
EventEmitter: require('./EventEmitter'), | ||
Group: require('./Group'), | ||
EventHandler: require('./EventHandler'), | ||
Group: require('./Group'), | ||
Modifier: require('./Modifier'), | ||
@@ -14,7 +14,7 @@ OptionsManager: require('./OptionsManager'), | ||
Scene: require('./Scene'), | ||
Surface: require('./Surface'), | ||
SpecParser: require('./SpecParser'), | ||
Transform: require('./Transform'), | ||
Surface: require('./Surface'), | ||
View: require('./View'), | ||
ViewSequence: require('./ViewSequence') | ||
}; |
@@ -37,2 +37,3 @@ /* This Source Code Form is subject to the terms of the Mozilla Public | ||
this._sizeGetter = null; | ||
this._proportionGetter = null; | ||
@@ -48,2 +49,3 @@ /* TODO: remove this when deprecation complete */ | ||
size: null, | ||
proportions: null, | ||
target: null | ||
@@ -58,2 +60,3 @@ }; | ||
if (options.size) this.sizeFrom(options.size); | ||
if (options.proportions) this.proportionsFrom(options.proportions); | ||
} | ||
@@ -155,2 +158,20 @@ } | ||
/** | ||
* Set function, object, or numerical array to provide proportions, as [percent of width, percent of height]. | ||
* | ||
* @method proportionsFrom | ||
* | ||
* @param {Object} proportions provider object | ||
* @return {Modifier} this | ||
*/ | ||
Modifier.prototype.proportionsFrom = function proportionsFrom(proportions) { | ||
if (proportions instanceof Function) this._proportionGetter = proportions; | ||
else if (proportions instanceof Object && proportions.get) this._proportionGetter = proportions.get.bind(proportions); | ||
else { | ||
this._proportionGetter = null; | ||
this._output.proportions = proportions; | ||
} | ||
return this; | ||
}; | ||
/** | ||
@@ -274,2 +295,24 @@ * Deprecated: Prefer transformFrom with static Transform, or use a TransitionableTransform. | ||
/** | ||
* Deprecated: Prefer proportionsFrom with static origin array, or use a Transitionable with those proportions. | ||
* @deprecated | ||
* @method setProportions | ||
* @param {Array.Number} proportions two element array of [percent of width, percent of height] | ||
* @param {Transitionable} transition Valid transitionable object | ||
* @param {Function} callback callback to call after transition completes | ||
* @return {Modifier} this | ||
*/ | ||
Modifier.prototype.setProportions = function setProportions(proportions, transition, callback) { | ||
if (proportions && (transition || this._legacyStates.proportions)) { | ||
if (!this._legacyStates.proportions) { | ||
this._legacyStates.proportions = new Transitionable(this._output.proportions || [0, 0]); | ||
} | ||
if (!this._proportionGetter) this.proportionsFrom(this._legacyStates.proportions); | ||
this._legacyStates.proportions.set(proportions, transition, callback); | ||
return this; | ||
} | ||
else return this.proportionsFrom(proportions); | ||
}; | ||
/** | ||
* Deprecated: Prefer to stop transform in your provider object. | ||
@@ -285,2 +328,3 @@ * @deprecated | ||
if (this._legacyStates.size) this._legacyStates.size.halt(); | ||
if (this._legacyStates.proportions) this._legacyStates.proportions.halt(); | ||
this._transformGetter = null; | ||
@@ -291,2 +335,3 @@ this._opacityGetter = null; | ||
this._sizeGetter = null; | ||
this._proportionGetter = null; | ||
}; | ||
@@ -354,2 +399,12 @@ | ||
/** | ||
* Deprecated: Prefer to use your provided proportions or output of your proportions provider. | ||
* @deprecated | ||
* @method getProportions | ||
* @return {Object} proportions provider object | ||
*/ | ||
Modifier.prototype.getProportions = function getProportions() { | ||
return this._proportionGetter ? this._proportionGetter() : this._output.proportions; | ||
}; | ||
// call providers on tick to receive render spec elements to apply | ||
@@ -362,2 +417,3 @@ function _update() { | ||
if (this._sizeGetter) this._output.size = this._sizeGetter(); | ||
if (this._proportionGetter) this._output.proportions = this._proportionGetter(); | ||
} | ||
@@ -364,0 +420,0 @@ |
@@ -83,3 +83,3 @@ /* This Source Code Form is subject to the terms of the Mozilla Public | ||
var _originZeroZero = [0, 0]; | ||
var _zeroZero = [0, 0]; | ||
@@ -103,3 +103,3 @@ // From the provided renderSpec tree, recursively compose opacities, | ||
transform = parentContext.transform; | ||
align = parentContext.align || parentContext.origin; | ||
align = parentContext.align || _zeroZero; | ||
if (parentContext.size && align && (align[0] || align[1])) { | ||
@@ -112,4 +112,4 @@ var alignAdjust = [align[0] * parentContext.size[0], align[1] * parentContext.size[1], 0]; | ||
opacity: parentContext.opacity, | ||
origin: parentContext.origin || _originZeroZero, | ||
align: parentContext.align || parentContext.origin || _originZeroZero, | ||
origin: parentContext.origin || _zeroZero, | ||
align: parentContext.align || _zeroZero, | ||
size: parentContext.size | ||
@@ -142,13 +142,22 @@ }; | ||
if (spec.align) align = spec.align; | ||
if (spec.size) { | ||
var parentSize = parentContext.size; | ||
size = [ | ||
spec.size[0] !== undefined ? spec.size[0] : parentSize[0], | ||
spec.size[1] !== undefined ? spec.size[1] : parentSize[1] | ||
]; | ||
if (spec.size || spec.proportions) { | ||
var parentSize = size; | ||
size = [size[0], size[1]]; | ||
if (spec.size) { | ||
if (spec.size[0] !== undefined) size[0] = spec.size[0]; | ||
if (spec.size[1] !== undefined) size[1] = spec.size[1]; | ||
} | ||
if (spec.proportions) { | ||
if (spec.proportions[0] !== undefined) size[0] = size[0] * spec.proportions[0]; | ||
if (spec.proportions[1] !== undefined) size[1] = size[1] * spec.proportions[1]; | ||
} | ||
if (parentSize) { | ||
if (!align) align = origin; | ||
if (align && (align[0] || align[1])) transform = Transform.thenMove(transform, _vecInContext([align[0] * parentSize[0], align[1] * parentSize[1], 0], sizeContext)); | ||
if (origin && (origin[0] || origin[1])) transform = Transform.moveThen([-origin[0] * size[0], -origin[1] * size[1], 0], transform); | ||
} | ||
nextSizeContext = parentContext.transform; | ||
@@ -155,0 +164,0 @@ origin = null; |
@@ -33,2 +33,3 @@ /* This Source Code Form is subject to the terms of the Mozilla Public | ||
this.properties = {}; | ||
this.attributes = {}; | ||
this.content = ''; | ||
@@ -40,4 +41,6 @@ this.classList = []; | ||
this._stylesDirty = true; | ||
this._attributesDirty = true; | ||
this._sizeDirty = true; | ||
this._contentDirty = true; | ||
this._trueSizeCheck = true; | ||
@@ -56,2 +59,28 @@ this._dirtyClasses = []; | ||
/** | ||
* Set HTML attributes on this Surface. Note that this will cause | ||
* dirtying and thus re-rendering, even if values do not change. | ||
* | ||
* @method setAttributes | ||
* @param {Object} attributes property dictionary of "key" => "value" | ||
*/ | ||
Surface.prototype.setAttributes = function setAttributes(attributes) { | ||
for (var n in attributes) { | ||
if (n === 'style') throw new Error('Cannot set styles via "setAttributes" as it will break Famo.us. Use "setProperties" instead.'); | ||
this.attributes[n] = attributes[n]; | ||
} | ||
this._attributesDirty = true; | ||
}; | ||
/** | ||
* Get HTML attributes on this Surface. | ||
* | ||
* @method getAttributes | ||
* | ||
* @return {Object} Dictionary of this Surface's attributes. | ||
*/ | ||
Surface.prototype.getAttributes = function getAttributes() { | ||
return this.attributes; | ||
}; | ||
/** | ||
* Set CSS-style properties on this Surface. Note that this will cause | ||
@@ -61,2 +90,3 @@ * dirtying and thus re-rendering, even if values do not change. | ||
* @method setProperties | ||
* @chainable | ||
* @param {Object} properties property dictionary of "key" => "value" | ||
@@ -69,2 +99,3 @@ */ | ||
this._stylesDirty = true; | ||
return this; | ||
}; | ||
@@ -89,2 +120,3 @@ | ||
* @method addClass | ||
* @chainable | ||
* @param {string} className name of class to add | ||
@@ -97,2 +129,3 @@ */ | ||
} | ||
return this; | ||
}; | ||
@@ -106,2 +139,3 @@ | ||
* @method removeClass | ||
* @chainable | ||
* @param {string} className name of class to remove | ||
@@ -115,2 +149,3 @@ */ | ||
} | ||
return this; | ||
}; | ||
@@ -133,2 +168,3 @@ | ||
} | ||
return this; | ||
}; | ||
@@ -139,2 +175,3 @@ | ||
* @method setClasses | ||
* @chainable | ||
* @param {Array.string} classList | ||
@@ -151,2 +188,3 @@ */ | ||
for (i = 0; i < classList.length; i++) this.addClass(classList[i]); | ||
return this; | ||
}; | ||
@@ -169,2 +207,3 @@ | ||
* @method setContent | ||
* @chainable | ||
* @param {string|Document Fragment} content HTML content | ||
@@ -177,2 +216,3 @@ */ | ||
} | ||
return this; | ||
}; | ||
@@ -195,2 +235,3 @@ | ||
* @method setOptions | ||
* @chainable | ||
* @param {Object} [options] overrides for default options. See constructor. | ||
@@ -202,3 +243,5 @@ */ | ||
if (options.properties) this.setProperties(options.properties); | ||
if (options.attributes) this.setAttributes(options.attributes); | ||
if (options.content) this.setContent(options.content); | ||
return this; | ||
}; | ||
@@ -228,2 +271,18 @@ | ||
// Apply values of all Famous-managed attributes to the document element. | ||
// These will be deployed to the document on call to #setup(). | ||
function _applyAttributes(target) { | ||
for (var n in this.attributes) { | ||
target.setAttribute(n, this.attributes[n]); | ||
} | ||
} | ||
// Clear all Famous-managed attributes from the document element. | ||
// These will be deployed to the document on call to #setup(). | ||
function _cleanupAttributes(target) { | ||
for (var n in this.attributes) { | ||
target.removeAttribute(n); | ||
} | ||
} | ||
function _xyNotEquals(a, b) { | ||
@@ -255,7 +314,10 @@ return (a && b) ? (a[0] !== b[0] || a[1] !== b[1]) : a !== b; | ||
this.attach(target); | ||
this._opacity = null; | ||
this._currentTarget = target; | ||
this._stylesDirty = true; | ||
this._classesDirty = true; | ||
this._attributesDirty = true; | ||
this._sizeDirty = true; | ||
this._contentDirty = true; | ||
this._originDirty = true; | ||
this._transformDirty = true; | ||
@@ -283,2 +345,3 @@ }; | ||
this._classesDirty = false; | ||
this._trueSizeCheck = true; | ||
} | ||
@@ -289,4 +352,11 @@ | ||
this._stylesDirty = false; | ||
this._trueSizeCheck = true; | ||
} | ||
if (this._attributesDirty) { | ||
_applyAttributes.call(this, target); | ||
this._attributesDirty = false; | ||
this._trueSizeCheck = true; | ||
} | ||
if (this.size) { | ||
@@ -296,5 +366,26 @@ var origSize = context.size; | ||
if (size[0] === undefined) size[0] = origSize[0]; | ||
else if (size[0] === true) size[0] = target.clientWidth; | ||
if (size[1] === undefined) size[1] = origSize[1]; | ||
else if (size[1] === true) size[1] = target.clientHeight; | ||
if (size[0] === true || size[1] === true) { | ||
if (size[0] === true && (this._trueSizeCheck || this._size[0] === 0)) { | ||
var width = target.clientWidth; | ||
if (this._size && this._size[0] !== width) { | ||
this._size[0] = width; | ||
this._sizeDirty = true; | ||
} | ||
size[0] = width; | ||
} else { | ||
if (this._size) size[0] = this._size[0]; | ||
} | ||
if (size[1] === true && (this._trueSizeCheck || this._size[1] === 0)) { | ||
var height = target.clientHeight; | ||
if (this._size && this._size[1] !== height) { | ||
this._size[1] = height; | ||
this._sizeDirty = true; | ||
} | ||
size[1] = height; | ||
} else { | ||
if (this._size) size[1] = this._size[1]; | ||
} | ||
this._trueSizeCheck = false; | ||
} | ||
} | ||
@@ -314,2 +405,3 @@ | ||
} | ||
this._eventOutput.emit('resize'); | ||
this._sizeDirty = false; | ||
@@ -322,2 +414,3 @@ } | ||
this._contentDirty = false; | ||
this._trueSizeCheck = true; | ||
} | ||
@@ -343,6 +436,7 @@ | ||
target.style.display = 'none'; | ||
target.style.opacity = ''; | ||
target.style.width = ''; | ||
target.style.height = ''; | ||
this._size = null; | ||
_cleanupStyles.call(this, target); | ||
_cleanupAttributes.call(this, target); | ||
var classList = this.getClassList(); | ||
@@ -409,2 +503,3 @@ _cleanupClasses.call(this, target); | ||
* @method setSize | ||
* @chainable | ||
* @param {Array.Number} size as [width, height] | ||
@@ -415,4 +510,5 @@ */ | ||
this._sizeDirty = true; | ||
return this; | ||
}; | ||
module.exports = Surface; |
@@ -43,2 +43,4 @@ /* This Source Code Form is subject to the terms of the Mozilla Public | ||
if (options.trackSize !== undefined) this._.trackSize = options.trackSize; | ||
this._previousNode = null; | ||
@@ -55,2 +57,5 @@ this._nextNode = null; | ||
this.lastNode = null; | ||
this.cumulativeSizes = [[0, 0]]; | ||
this.sizeDirty = true; | ||
this.trackSize = false; | ||
}; | ||
@@ -70,2 +75,30 @@ | ||
// Get sequence size from backing up to index | ||
// TODO: remove from viewSequence with proper abstraction | ||
ViewSequence.Backing.prototype.getSize = function getSize(index) { | ||
return this.cumulativeSizes[index]; | ||
}; | ||
// Calculates cumulative size | ||
// TODO: remove from viewSequence with proper abstraction | ||
ViewSequence.Backing.prototype.calculateSize = function calculateSize(index) { | ||
index = index || this.array.length; | ||
var size = [0, 0]; | ||
for (var i = 0; i < index; i++) { | ||
var nodeSize = this.array[i].getSize(); | ||
if (!nodeSize) return undefined; | ||
if (size[0] !== undefined) { | ||
if (nodeSize[0] === undefined) size[0] = undefined; | ||
else size[0] += nodeSize[0]; | ||
} | ||
if (size[1] !== undefined) { | ||
if (nodeSize[1] === undefined) size[1] = undefined; | ||
else size[1] += nodeSize[1]; | ||
} | ||
this.cumulativeSizes[i + 1] = size.slice(); | ||
} | ||
this.sizeDirty = false; | ||
return size; | ||
}; | ||
// After splicing into the backing store, restore the indexes of each node correctly. | ||
@@ -112,2 +145,3 @@ ViewSequence.Backing.prototype.reindex = function reindex(start, removeCount, insertCount) { | ||
} | ||
if (this._.trackSize) this._.sizeDirty = true; | ||
}; | ||
@@ -190,2 +224,3 @@ | ||
this._.firstIndex -= arguments.length; | ||
if (this._.trackSize) this._.sizeDirty = true; | ||
}; | ||
@@ -201,2 +236,3 @@ | ||
this._.array.push.apply(this._.array, arguments); | ||
if (this._.trackSize) this._.sizeDirty = true; | ||
}; | ||
@@ -253,2 +289,3 @@ | ||
else if (other.index === this._.firstIndex + this._.array.length - 1) this._.lastNode = other; | ||
if (this._.trackSize) this._.sizeDirty = true; | ||
}; | ||
@@ -285,2 +322,3 @@ | ||
ViewSequence.prototype.render = function render() { | ||
if (this._.trackSize && this._.sizeDirty) this._.calculateSize(); | ||
var target = this.get(); | ||
@@ -287,0 +325,0 @@ return target ? target.render.apply(target, arguments) : null; |
10
index.js
module.exports = { | ||
core: require('./core'), | ||
inputs: require('./inputs'), | ||
events: require('./events'), | ||
math: require('./math'), | ||
physics: require('./physics'), | ||
modifiers: require('./modifiers'), | ||
physics: require('./physics'), | ||
surfaces: require('./surfaces'), | ||
inputs: require('./inputs'), | ||
transitions: require('./transitions'), | ||
surfaces: require('./surfaces'), | ||
views: require('./views'), | ||
widgets: require('./widgets'), | ||
utilities: require('./utilities') | ||
utilities: require('./utilities'), | ||
widgets: require('./widgets') | ||
}; |
@@ -18,50 +18,52 @@ /* This Source Code Form is subject to the terms of the Mozilla Public | ||
*/ | ||
if (!window.CustomEvent) return; | ||
var clickThreshold = 300; | ||
var clickWindow = 500; | ||
var potentialClicks = {}; | ||
var recentlyDispatched = {}; | ||
var _now = Date.now; | ||
(function() { | ||
if (!window.CustomEvent) return; | ||
var clickThreshold = 300; | ||
var clickWindow = 500; | ||
var potentialClicks = {}; | ||
var recentlyDispatched = {}; | ||
var _now = Date.now; | ||
window.addEventListener('touchstart', function(event) { | ||
var timestamp = _now(); | ||
for (var i = 0; i < event.changedTouches.length; i++) { | ||
var touch = event.changedTouches[i]; | ||
potentialClicks[touch.identifier] = timestamp; | ||
} | ||
}); | ||
window.addEventListener('touchstart', function(event) { | ||
var timestamp = _now(); | ||
for (var i = 0; i < event.changedTouches.length; i++) { | ||
var touch = event.changedTouches[i]; | ||
potentialClicks[touch.identifier] = timestamp; | ||
} | ||
}); | ||
window.addEventListener('touchmove', function(event) { | ||
for (var i = 0; i < event.changedTouches.length; i++) { | ||
var touch = event.changedTouches[i]; | ||
delete potentialClicks[touch.identifier]; | ||
} | ||
}); | ||
window.addEventListener('touchmove', function(event) { | ||
for (var i = 0; i < event.changedTouches.length; i++) { | ||
var touch = event.changedTouches[i]; | ||
delete potentialClicks[touch.identifier]; | ||
} | ||
}); | ||
window.addEventListener('touchend', function(event) { | ||
var currTime = _now(); | ||
for (var i = 0; i < event.changedTouches.length; i++) { | ||
var touch = event.changedTouches[i]; | ||
var startTime = potentialClicks[touch.identifier]; | ||
if (startTime && currTime - startTime < clickThreshold) { | ||
var clickEvt = new window.CustomEvent('click', { | ||
'bubbles': true, | ||
'detail': touch | ||
}); | ||
recentlyDispatched[currTime] = event; | ||
event.target.dispatchEvent(clickEvt); | ||
} | ||
delete potentialClicks[touch.identifier]; | ||
} | ||
}); | ||
window.addEventListener('touchend', function(event) { | ||
var currTime = _now(); | ||
for (var i = 0; i < event.changedTouches.length; i++) { | ||
var touch = event.changedTouches[i]; | ||
var startTime = potentialClicks[touch.identifier]; | ||
if (startTime && currTime - startTime < clickThreshold) { | ||
var clickEvt = new window.CustomEvent('click', { | ||
'bubbles': true, | ||
'detail': touch | ||
}); | ||
recentlyDispatched[currTime] = event; | ||
event.target.dispatchEvent(clickEvt); | ||
} | ||
delete potentialClicks[touch.identifier]; | ||
} | ||
}); | ||
window.addEventListener('click', function(event) { | ||
var currTime = _now(); | ||
for (var i in recentlyDispatched) { | ||
var previousEvent = recentlyDispatched[i]; | ||
if (currTime - i < clickWindow) { | ||
if (event instanceof window.MouseEvent && event.target === previousEvent.target) event.stopPropagation(); | ||
} | ||
else delete recentlyDispatched[i]; | ||
} | ||
}, true); | ||
window.addEventListener('click', function(event) { | ||
var currTime = _now(); | ||
for (var i in recentlyDispatched) { | ||
var previousEvent = recentlyDispatched[i]; | ||
if (currTime - i < clickWindow) { | ||
if (event instanceof window.MouseEvent && event.target === previousEvent.target) event.stopPropagation(); | ||
} | ||
else delete recentlyDispatched[i]; | ||
} | ||
}, true); | ||
})(); |
@@ -9,3 +9,2 @@ /* This Source Code Form is subject to the terms of the Mozilla Public | ||
*/ | ||
var EventHandler = require('../core/EventHandler'); | ||
@@ -12,0 +11,0 @@ |
@@ -11,5 +11,5 @@ module.exports = { | ||
ScrollSync: require('./ScrollSync'), | ||
TouchSync: require('./TouchSync'), | ||
TouchTracker: require('./TouchTracker'), | ||
TouchSync: require('./TouchSync'), | ||
TwoFingerSync: require('./TwoFingerSync') | ||
}; |
@@ -9,4 +9,4 @@ /* This Source Code Form is subject to the terms of the Mozilla Public | ||
*/ | ||
var EventHandler = require('../core/EventHandler'); | ||
var OptionsManager = require('../core/OptionsManager'); | ||
@@ -29,2 +29,4 @@ /** | ||
this.options = Object.create(MouseSync.DEFAULT_OPTIONS); | ||
this._optionsManager = new OptionsManager(this.options); | ||
if (options) this.setOptions(options); | ||
@@ -66,3 +68,4 @@ | ||
scale: 1, | ||
propogate: true // events piped to document on mouseleave | ||
propogate: true, // events piped to document on mouseleave | ||
preventDefault: true | ||
}; | ||
@@ -80,3 +83,3 @@ | ||
var velocity; | ||
event.preventDefault(); // prevent drag | ||
if (this.options.preventDefault) event.preventDefault(); // prevent drag | ||
@@ -220,8 +223,5 @@ var x = event.clientX; | ||
MouseSync.prototype.setOptions = function setOptions(options) { | ||
if (options.direction !== undefined) this.options.direction = options.direction; | ||
if (options.rails !== undefined) this.options.rails = options.rails; | ||
if (options.scale !== undefined) this.options.scale = options.scale; | ||
if (options.propogate !== undefined) this.options.propogate = options.propogate; | ||
return this._optionsManager.setOptions(options); | ||
}; | ||
module.exports = MouseSync; |
@@ -9,4 +9,4 @@ /* This Source Code Form is subject to the terms of the Mozilla Public | ||
*/ | ||
var TwoFingerSync = require('./TwoFingerSync'); | ||
var OptionsManager = require('../core/OptionsManager'); | ||
@@ -28,2 +28,3 @@ /** | ||
this.options = Object.create(PinchSync.DEFAULT_OPTIONS); | ||
this._optionsManager = new OptionsManager(this.options); | ||
if (options) this.setOptions(options); | ||
@@ -94,5 +95,5 @@ | ||
PinchSync.prototype.setOptions = function setOptions(options) { | ||
if (options.scale !== undefined) this.options.scale = options.scale; | ||
return this._optionsManager.setOptions(options); | ||
}; | ||
module.exports = PinchSync; |
@@ -9,4 +9,4 @@ /* This Source Code Form is subject to the terms of the Mozilla Public | ||
*/ | ||
var TwoFingerSync = require('./TwoFingerSync'); | ||
var OptionsManager = require('../core/OptionsManager'); | ||
@@ -28,2 +28,3 @@ /** | ||
this.options = Object.create(RotateSync.DEFAULT_OPTIONS); | ||
this._optionsManager = new OptionsManager(this.options); | ||
if (options) this.setOptions(options); | ||
@@ -95,5 +96,5 @@ | ||
RotateSync.prototype.setOptions = function setOptions(options) { | ||
if (options.scale !== undefined) this.options.scale = options.scale; | ||
return this._optionsManager.setOptions(options); | ||
}; | ||
module.exports = RotateSync; |
@@ -9,4 +9,4 @@ /* This Source Code Form is subject to the terms of the Mozilla Public | ||
*/ | ||
var TwoFingerSync = require('./TwoFingerSync'); | ||
var OptionsManager = require('../core/OptionsManager'); | ||
@@ -28,2 +28,3 @@ /** | ||
this.options = Object.create(ScaleSync.DEFAULT_OPTIONS); | ||
this._optionsManager = new OptionsManager(this.options); | ||
if (options) this.setOptions(options); | ||
@@ -102,5 +103,5 @@ | ||
ScaleSync.prototype.setOptions = function setOptions(options) { | ||
if (options.scale !== undefined) this.options.scale = options.scale; | ||
return this._optionsManager.setOptions(options); | ||
}; | ||
module.exports = ScaleSync; |
@@ -9,5 +9,5 @@ /* This Source Code Form is subject to the terms of the Mozilla Public | ||
*/ | ||
var EventHandler = require('../core/EventHandler'); | ||
var Engine = require('../core/Engine'); | ||
var OptionsManager = require('../core/OptionsManager'); | ||
@@ -36,2 +36,3 @@ /** | ||
this.options = Object.create(ScrollSync.DEFAULT_OPTIONS); | ||
this._optionsManager = new OptionsManager(this.options); | ||
if (options) this.setOptions(options); | ||
@@ -67,3 +68,4 @@ | ||
stallTime: 50, | ||
lineHeight: 40 | ||
lineHeight: 40, | ||
preventDefault: true | ||
}; | ||
@@ -96,3 +98,3 @@ | ||
function _handleMove(event) { | ||
event.preventDefault(); | ||
if (this.options.preventDefault) event.preventDefault(); | ||
@@ -194,9 +196,5 @@ if (!this._inProgress) { | ||
ScrollSync.prototype.setOptions = function setOptions(options) { | ||
if (options.direction !== undefined) this.options.direction = options.direction; | ||
if (options.minimumEndSpeed !== undefined) this.options.minimumEndSpeed = options.minimumEndSpeed; | ||
if (options.rails !== undefined) this.options.rails = options.rails; | ||
if (options.scale !== undefined) this.options.scale = options.scale; | ||
if (options.stallTime !== undefined) this.options.stallTime = options.stallTime; | ||
return this._optionsManager.setOptions(options); | ||
}; | ||
module.exports = ScrollSync; |
@@ -9,5 +9,5 @@ /* This Source Code Form is subject to the terms of the Mozilla Public | ||
*/ | ||
var TouchTracker = require('./TouchTracker'); | ||
var EventHandler = require('../core/EventHandler'); | ||
var OptionsManager = require('../core/OptionsManager'); | ||
@@ -30,2 +30,3 @@ /** | ||
this.options = Object.create(TouchSync.DEFAULT_OPTIONS); | ||
this._optionsManager = new OptionsManager(this.options); | ||
if (options) this.setOptions(options); | ||
@@ -167,5 +168,3 @@ | ||
TouchSync.prototype.setOptions = function setOptions(options) { | ||
if (options.direction !== undefined) this.options.direction = options.direction; | ||
if (options.rails !== undefined) this.options.rails = options.rails; | ||
if (options.scale !== undefined) this.options.scale = options.scale; | ||
return this._optionsManager.setOptions(options); | ||
}; | ||
@@ -172,0 +171,0 @@ |
@@ -9,3 +9,2 @@ /* This Source Code Form is subject to the terms of the Mozilla Public | ||
*/ | ||
var EventHandler = require('../core/EventHandler'); | ||
@@ -12,0 +11,0 @@ |
@@ -9,3 +9,2 @@ /* This Source Code Form is subject to the terms of the Mozilla Public | ||
*/ | ||
var EventHandler = require('../core/EventHandler'); | ||
@@ -12,0 +11,0 @@ |
@@ -5,4 +5,4 @@ module.exports = { | ||
Random: require('./Random'), | ||
Vector: require('./Vector'), | ||
Utilities: require('./Utilities') | ||
Utilities: require('./Utilities'), | ||
Vector: require('./Vector') | ||
}; |
@@ -32,2 +32,3 @@ /* This Source Code Form is subject to the terms of the Mozilla Public | ||
* @param {Array.Number} [options.size] size to apply to descendants | ||
* @param {Array.Number} [options.propportions] proportions to apply to descendants | ||
*/ | ||
@@ -40,2 +41,3 @@ function StateModifier(options) { | ||
this._sizeState = new Transitionable([0, 0]); | ||
this._proportionsState = new Transitionable([0, 0]); | ||
@@ -47,3 +49,4 @@ this._modifier = new Modifier({ | ||
align: null, | ||
size: null | ||
size: null, | ||
proportions: null | ||
}); | ||
@@ -54,2 +57,3 @@ | ||
this._hasSize = false; | ||
this._hasProportions = false; | ||
@@ -62,2 +66,3 @@ if (options) { | ||
if (options.size) this.setSize(options.size); | ||
if (options.proportions) this.setProportions(options.proportions); | ||
} | ||
@@ -73,3 +78,5 @@ } | ||
* @param {Transform} transform Transform to transition to. | ||
* @param {Transitionable} [transition] Valid transitionable object | ||
* @param {Transitionable} transition object of type {duration: number, curve: | ||
* f[0,1] -> [0,1] or name}. If transition is omitted, change will be | ||
* instantaneous. | ||
* @param {Function} [callback] callback to call after transition completes | ||
@@ -90,3 +97,5 @@ * @return {StateModifier} this | ||
* @param {Number} opacity Opacity value to transition to. | ||
* @param {Transitionable} transition Valid transitionable object | ||
* @param {Transitionable} transition object of type {duration: number, curve: | ||
* f[0,1] -> [0,1] or name}. If transition is omitted, change will be | ||
* instantaneous. | ||
* @param {Function} callback callback to call after transition completes | ||
@@ -107,3 +116,5 @@ * @return {StateModifier} this | ||
* @param {Array.Number} origin two element array with values between 0 and 1. | ||
* @param {Transitionable} transition Valid transitionable object | ||
* @param {Transitionable} transition object of type {duration: number, curve: | ||
* f[0,1] -> [0,1] or name}. If transition is omitted, change will be | ||
* instantaneous. | ||
* @param {Function} callback callback to call after transition completes | ||
@@ -135,3 +146,5 @@ * @return {StateModifier} this | ||
* @param {Array.Number} align two element array with values between 0 and 1. | ||
* @param {Transitionable} transition Valid transitionable object | ||
* @param {Transitionable} transition object of type {duration: number, curve: | ||
* f[0,1] -> [0,1] or name}. If transition is omitted, change will be | ||
* instantaneous. | ||
* @param {Function} callback callback to call after transition completes | ||
@@ -162,4 +175,6 @@ * @return {StateModifier} this | ||
* | ||
* @param {Array.Number} size two element array with values between 0 and 1. | ||
* @param {Transitionable} transition Valid transitionable object | ||
* @param {Array.Number} size two element array of [width, height] | ||
* @param {Transitionable} transition object of type {duration: number, curve: | ||
* f[0,1] -> [0,1] or name}. If transition is omitted, change will be | ||
* instantaneous. | ||
* @param {Function} callback callback to call after transition completes | ||
@@ -185,2 +200,29 @@ * @return {StateModifier} this | ||
/** | ||
* Set the proportions of this modifier, either statically or | ||
* through a provided Transitionable. | ||
* | ||
* @method setProportions | ||
* | ||
* @param {Array.Number} proportions two element array with values between 0 and 1. | ||
* @param {Transitionable} transition Valid transitionable object | ||
* @param {Function} callback callback to call after transition completes | ||
* @return {StateModifier} this | ||
*/ | ||
StateModifier.prototype.setProportions = function setSize(proportions, transition, callback) { | ||
if (proportions === null) { | ||
if (this._hasProportions) { | ||
this._modifier.proportionsFrom(null); | ||
this._hasProportions = false; | ||
} | ||
return this; | ||
} | ||
else if (!this._hasProportions) { | ||
this._hasProportions = true; | ||
this._modifier.proportionsFrom(this._proportionsState); | ||
} | ||
this._proportionsState.set(proportions, transition, callback); | ||
return this; | ||
}; | ||
/** | ||
* Stop the transition. | ||
@@ -196,2 +238,3 @@ * | ||
this._sizeState.halt(); | ||
this._proportionsState.halt(); | ||
}; | ||
@@ -260,2 +303,12 @@ | ||
/** | ||
* Get the current state of the propportions component. | ||
* | ||
* @method getProportions | ||
* @return {Object} size provider object | ||
*/ | ||
StateModifier.prototype.getProportions = function getProportions() { | ||
return this._hasProportions ? this._proportionsState.get() : null; | ||
}; | ||
/** | ||
* Return render spec for this StateModifier, applying to the provided | ||
@@ -262,0 +315,0 @@ * target component. This is similar to render() for Surfaces. |
{ | ||
"name": "famous", | ||
"version": "0.2.3-beta0", | ||
"version": "0.3.0-alpha.0", | ||
"description": "A JavaScript framework for everyone who wants to build beautiful experiences on any device.", | ||
@@ -5,0 +5,0 @@ "main": "./index.js", |
@@ -15,8 +15,9 @@ /* This Source Code Form is subject to the terms of the Mozilla Public | ||
var Matrix = require('../../math/Matrix'); | ||
var Integrator = require('../integrators/SymplecticEuler'); | ||
/** | ||
* A unit controlled by the physics engine which extends the zero-dimensional | ||
* Particle to include geometry. In addition to maintaining the state | ||
* of a Particle its state includes orientation, angular velocity | ||
* and angular momentum and responds to torque forces. | ||
* Particle to include geometry. In addition to maintaining the state | ||
* of a Particle its state includes orientation, angular velocity | ||
* and angular momentum and responds to torque forces. | ||
* | ||
@@ -41,7 +42,6 @@ * @class Body | ||
this.angularVelocity.w = 0; //quaternify the angular velocity | ||
this.setMomentsOfInertia(); | ||
this.angularVelocity.w = 0; //quaternify the angular velocity | ||
//registers | ||
// registers | ||
this.pWorld = new Vector(); //placeholder for world space position | ||
@@ -51,9 +51,5 @@ } | ||
Body.DEFAULT_OPTIONS = Particle.DEFAULT_OPTIONS; | ||
Body.DEFAULT_OPTIONS.orientation = [0,0,0,1]; | ||
Body.DEFAULT_OPTIONS.angularVelocity = [0,0,0]; | ||
Body.DEFAULT_OPTIONS.orientation = [0, 0, 0, 1]; | ||
Body.DEFAULT_OPTIONS.angularVelocity = [0, 0, 0]; | ||
Body.AXES = Particle.AXES; | ||
Body.SLEEP_TOLERANCE = Particle.SLEEP_TOLERANCE; | ||
Body.INTEGRATOR = Particle.INTEGRATOR; | ||
Body.prototype = Object.create(Particle.prototype); | ||
@@ -71,3 +67,3 @@ Body.prototype.constructor = Body; | ||
* Setter for moment of inertia, which is necessary to give proper | ||
* angular inertia depending on the geometry of the body. | ||
* angular inertia depending on the geometry of the body. | ||
* | ||
@@ -92,3 +88,3 @@ * @method setMomentsOfInertia | ||
* Determine world coordinates from the local coordinate system. Useful | ||
* if the Body has rotated in space. | ||
* if the Body has rotated in space. | ||
* | ||
@@ -116,3 +112,3 @@ * @method toWorldCoordinates | ||
* Extends Particle.reset to reset orientation, angular velocity | ||
* and angular momentum. | ||
* and angular momentum. | ||
* | ||
@@ -166,3 +162,3 @@ * @method reset | ||
* Extends Particle.applyForce with an optional argument | ||
* to apply the force at an off-centered location, resulting in a torque. | ||
* to apply the force at an off-centered location, resulting in a torque. | ||
* | ||
@@ -191,3 +187,3 @@ * @method applyForce | ||
* Extends Particle.getTransform to include a rotational component | ||
* derived from the particle's orientation. | ||
* derived from the particle's orientation. | ||
* | ||
@@ -206,3 +202,3 @@ * @method getTransform | ||
* Extends Particle._integrate to also update the rotational states | ||
* of the body. | ||
* of the body. | ||
* | ||
@@ -227,3 +223,3 @@ * @method getTransform | ||
Body.prototype.integrateAngularMomentum = function integrateAngularMomentum(dt) { | ||
Body.INTEGRATOR.integrateAngularMomentum(this, dt); | ||
Integrator.integrateAngularMomentum(this, dt); | ||
}; | ||
@@ -238,5 +234,5 @@ | ||
Body.prototype.integrateOrientation = function integrateOrientation(dt) { | ||
Body.INTEGRATOR.integrateOrientation(this, dt); | ||
Integrator.integrateOrientation(this, dt); | ||
}; | ||
module.exports = Body; |
@@ -18,19 +18,17 @@ /* This Source Code Form is subject to the terms of the Mozilla Public | ||
* position and velocity states that are updated by the Physics Engine. | ||
* Ultimately, a particle is a _special type of modifier, and can be added to | ||
* the Famous render tree like any other modifier. | ||
* Ultimately, a particle is a special type of modifier, and can be added to | ||
* the Famo.us Scene Graph like any other modifier. | ||
* | ||
* @constructor | ||
* @class Particle | ||
* @uses EventHandler | ||
* @uses Modifier | ||
* @extensionfor Body | ||
* @param {Options} [options] An object of configurable options. | ||
* @param {Array} [options.position] The position of the particle. | ||
* @param {Array} [options.velocity] The velocity of the particle. | ||
* @param {Number} [options.mass] The mass of the particle. | ||
* @param {Hexadecimal} [options.axis] The axis a particle can move along. Can be bitwise ORed e.g., Particle.AXES.X, Particle.AXES.X | Particle.AXES.Y | ||
* | ||
* @param [options] {Options} An object of configurable options. | ||
* @param [options.position] {Array} The position of the particle. | ||
* @param [options.velocity] {Array} The velocity of the particle. | ||
* @param [options.mass] {Number} The mass of the particle. | ||
*/ | ||
function Particle(options) { | ||
options = options || {}; | ||
var defaults = Particle.DEFAULT_OPTIONS; | ||
@@ -40,11 +38,9 @@ // registers | ||
this.velocity = new Vector(); | ||
this.force = new Vector(); | ||
this.force = new Vector(); | ||
var defaults = Particle.DEFAULT_OPTIONS; | ||
// state variables | ||
this._engine = null; | ||
this._isSleeping = true; | ||
this._eventOutput = null; | ||
// set vectors | ||
this.setPosition(options.position || defaults.position); | ||
this.setVelocity(options.velocity || defaults.velocity); | ||
this.force.set(options.force || [0,0,0]); | ||
// set scalars | ||
@@ -55,13 +51,8 @@ this.mass = (options.mass !== undefined) | ||
this.axis = (options.axis !== undefined) | ||
? options.axis | ||
: defaults.axis; | ||
this.inverseMass = 1 / this.mass; | ||
// state variables | ||
this._isSleeping = false; | ||
this._engine = null; | ||
this._eventOutput = null; | ||
this._positionGetter = null; | ||
// set vectors | ||
this.setPosition(options.position || defaults.position); | ||
this.setVelocity(options.velocity || defaults.velocity); | ||
this.force.set(options.force || [0,0,0]); | ||
@@ -72,4 +63,8 @@ this.transform = Transform.identity.slice(); | ||
this._spec = { | ||
transform : this.transform, | ||
target : null | ||
size : [true, true], | ||
target : { | ||
transform : this.transform, | ||
origin : [0.5, 0.5], | ||
target : null | ||
} | ||
}; | ||
@@ -79,50 +74,37 @@ } | ||
Particle.DEFAULT_OPTIONS = { | ||
position : [0,0,0], | ||
velocity : [0,0,0], | ||
mass : 1, | ||
axis : undefined | ||
position : [0, 0, 0], | ||
velocity : [0, 0, 0], | ||
mass : 1 | ||
}; | ||
//Catalogue of outputted events | ||
var _events = { | ||
start : 'start', | ||
update : 'update', | ||
end : 'end' | ||
}; | ||
// Cached timing function | ||
var now = Date.now; | ||
/** | ||
* Kinetic energy threshold needed to update the body | ||
* | ||
* @property SLEEP_TOLERANCE | ||
* @type Number | ||
* @attribute isBody | ||
* @type Boolean | ||
* @static | ||
* @default 1e-7 | ||
*/ | ||
Particle.SLEEP_TOLERANCE = 1e-7; | ||
Particle.prototype.isBody = false; | ||
/** | ||
* Axes by which a body can translate | ||
* Determines if particle is active | ||
* | ||
* @property AXES | ||
* @type Hexadecimal | ||
* @static | ||
* @default 1e-7 | ||
* @method isActive | ||
* @return {Boolean} | ||
*/ | ||
Particle.AXES = { | ||
X : 0x00, // hexadecimal for 0 | ||
Y : 0x01, // hexadecimal for 1 | ||
Z : 0x02 // hexadecimal for 2 | ||
Particle.prototype.isActive = function isActive() { | ||
return !this._isSleeping; | ||
}; | ||
// Integrator for updating the particle's state | ||
// TODO: make this a singleton | ||
Particle.INTEGRATOR = new Integrator(); | ||
//Catalogue of outputted events | ||
var _events = { | ||
start : 'start', | ||
update : 'update', | ||
end : 'end' | ||
}; | ||
// Cached timing function | ||
var now = (function() { | ||
return Date.now; | ||
})(); | ||
/** | ||
* Stops the particle from updating | ||
* | ||
* @method sleep | ||
@@ -138,2 +120,3 @@ */ | ||
* Starts the particle update | ||
* | ||
* @method wake | ||
@@ -146,13 +129,8 @@ */ | ||
this._prevTime = now(); | ||
if (this._engine) this._engine.wake(); | ||
}; | ||
/** | ||
* @attribute isBody | ||
* @type Boolean | ||
* @static | ||
*/ | ||
Particle.prototype.isBody = false; | ||
/** | ||
* Basic setter for position | ||
* | ||
* @method setPosition | ||
@@ -167,2 +145,3 @@ * @param position {Array|Vector} | ||
* 1-dimensional setter for position | ||
* | ||
* @method setPosition1D | ||
@@ -177,2 +156,3 @@ * @param x {Number} | ||
* Basic getter function for position | ||
* | ||
* @method getPosition | ||
@@ -182,7 +162,3 @@ * @return position {Array} | ||
Particle.prototype.getPosition = function getPosition() { | ||
if (this._positionGetter instanceof Function) | ||
this.setPosition(this._positionGetter()); | ||
this._engine.step(); | ||
return this.position.get(); | ||
@@ -193,2 +169,3 @@ }; | ||
* 1-dimensional getter for position | ||
* | ||
* @method getPosition1D | ||
@@ -203,12 +180,4 @@ * @return value {Number} | ||
/** | ||
* Defines the position from outside the Physics Engine | ||
* @method positionFrom | ||
* @param positionGetter {Function} | ||
*/ | ||
Particle.prototype.positionFrom = function positionFrom(positionGetter) { | ||
this._positionGetter = positionGetter; | ||
}; | ||
/** | ||
* Basic setter function for velocity Vector | ||
* | ||
* @method setVelocity | ||
@@ -219,3 +188,4 @@ * @function | ||
this.velocity.set(velocity); | ||
this.wake(); | ||
if (!(velocity[0] === 0 && velocity[1] === 0 && velocity[2] === 0)) | ||
this.wake(); | ||
}; | ||
@@ -225,2 +195,3 @@ | ||
* 1-dimensional setter for velocity | ||
* | ||
* @method setVelocity1D | ||
@@ -231,3 +202,3 @@ * @param x {Number} | ||
this.velocity.x = x; | ||
this.wake(); | ||
if (x !== 0) this.wake(); | ||
}; | ||
@@ -237,2 +208,3 @@ | ||
* Basic getter function for velocity Vector | ||
* | ||
* @method getVelocity | ||
@@ -246,3 +218,15 @@ * @return velocity {Array} | ||
/** | ||
* Basic setter function for force Vector | ||
* | ||
* @method setForce | ||
* @return force {Array} | ||
*/ | ||
Particle.prototype.setForce = function setForce(force) { | ||
this.force.set(force); | ||
this.wake(); | ||
}; | ||
/** | ||
* 1-dimensional getter for velocity | ||
* | ||
* @method getVelocity1D | ||
@@ -257,2 +241,3 @@ * @return velocity {Number} | ||
* Basic setter function for mass quantity | ||
* | ||
* @method setMass | ||
@@ -268,2 +253,3 @@ * @param mass {Number} mass | ||
* Basic getter function for mass quantity | ||
* | ||
* @method getMass | ||
@@ -278,2 +264,3 @@ * @return mass {Number} | ||
* Reset position and velocity | ||
* | ||
* @method reset | ||
@@ -290,2 +277,3 @@ * @param position {Array|Vector} | ||
* Add force vector to existing internal force Vector | ||
* | ||
* @method applyForce | ||
@@ -302,2 +290,3 @@ * @param force {Vector} | ||
* Add impulse (change in velocity) Vector to this Vector's velocity. | ||
* | ||
* @method applyImpulse | ||
@@ -314,2 +303,3 @@ * @param impulse {Vector} | ||
* Update a particle's velocity from its force accumulator | ||
* | ||
* @method integrateVelocity | ||
@@ -319,3 +309,3 @@ * @param dt {Number} Time differential | ||
Particle.prototype.integrateVelocity = function integrateVelocity(dt) { | ||
Particle.INTEGRATOR.integrateVelocity(this, dt); | ||
Integrator.integrateVelocity(this, dt); | ||
}; | ||
@@ -325,2 +315,3 @@ | ||
* Update a particle's position from its velocity | ||
* | ||
* @method integratePosition | ||
@@ -330,3 +321,3 @@ * @param dt {Number} Time differential | ||
Particle.prototype.integratePosition = function integratePosition(dt) { | ||
Particle.INTEGRATOR.integratePosition(this, dt); | ||
Integrator.integratePosition(this, dt); | ||
}; | ||
@@ -336,2 +327,3 @@ | ||
* Update the position and velocity of the particle | ||
* | ||
* @method _integrate | ||
@@ -348,2 +340,3 @@ * @protected | ||
* Get kinetic energy of the particle. | ||
* | ||
* @method getEnergy | ||
@@ -358,2 +351,3 @@ * @function | ||
* Generate transform from the current position state | ||
* | ||
* @method getTransform | ||
@@ -366,21 +360,7 @@ * @return Transform {Transform} | ||
var position = this.position; | ||
var axis = this.axis; | ||
var transform = this.transform; | ||
if (axis !== undefined) { | ||
if (axis & ~Particle.AXES.X) { | ||
position.x = 0; | ||
} | ||
if (axis & ~Particle.AXES.Y) { | ||
position.y = 0; | ||
} | ||
if (axis & ~Particle.AXES.Z) { | ||
position.z = 0; | ||
} | ||
} | ||
transform[12] = position.x; | ||
transform[13] = position.y; | ||
transform[14] = position.z; | ||
return transform; | ||
@@ -391,2 +371,3 @@ }; | ||
* The modify interface of a Modifier | ||
* | ||
* @method modify | ||
@@ -397,6 +378,6 @@ * @param target {Spec} | ||
Particle.prototype.modify = function modify(target) { | ||
var _spec = this._spec; | ||
var _spec = this._spec.target; | ||
_spec.transform = this.getTransform(); | ||
_spec.target = target; | ||
return _spec; | ||
return this._spec; | ||
}; | ||
@@ -408,3 +389,2 @@ | ||
this._eventOutput.bindThis(this); | ||
//overrides on/removeListener/pipe/unpipe methods | ||
EventHandler.setOutputHandler(this, this._eventOutput); | ||
@@ -422,2 +402,3 @@ } | ||
}; | ||
Particle.prototype.removeListener = function removeListener() { | ||
@@ -427,2 +408,3 @@ _createEventOutput.call(this); | ||
}; | ||
Particle.prototype.pipe = function pipe() { | ||
@@ -432,2 +414,3 @@ _createEventOutput.call(this); | ||
}; | ||
Particle.prototype.unpipe = function unpipe() { | ||
@@ -434,0 +417,0 @@ _createEventOutput.call(this); |
@@ -22,4 +22,4 @@ /* This Source Code Form is subject to the terms of the Mozilla Public | ||
this.options = this.options || {}; | ||
this._energy = 0.0; | ||
this._eventOutput = null; | ||
this._eventOutput = new EventHandler(); | ||
EventHandler.setOutputHandler(this, this._eventOutput); | ||
} | ||
@@ -34,3 +34,3 @@ | ||
Constraint.prototype.setOptions = function setOptions(options) { | ||
for (var key in options) this.options[key] = options[key]; | ||
this._eventOutput.emit('change', options); | ||
}; | ||
@@ -52,40 +52,5 @@ | ||
Constraint.prototype.getEnergy = function getEnergy() { | ||
return this._energy; | ||
return 0.0; | ||
}; | ||
/** | ||
* Setter for energy | ||
* | ||
* @method setEnergy | ||
* @param energy {Number} | ||
*/ | ||
Constraint.prototype.setEnergy = function setEnergy(energy) { | ||
this._energy = energy; | ||
}; | ||
function _createEventOutput() { | ||
this._eventOutput = new EventHandler(); | ||
this._eventOutput.bindThis(this); | ||
EventHandler.setOutputHandler(this, this._eventOutput); | ||
} | ||
Constraint.prototype.on = function on() { | ||
_createEventOutput.call(this); | ||
return this.on.apply(this, arguments); | ||
}; | ||
Constraint.prototype.addListener = function addListener() { | ||
_createEventOutput.call(this); | ||
return this.addListener.apply(this, arguments); | ||
}; | ||
Constraint.prototype.pipe = function pipe() { | ||
_createEventOutput.call(this); | ||
return this.pipe.apply(this, arguments); | ||
}; | ||
Constraint.prototype.removeListener = function removeListener() { | ||
return this.removeListener.apply(this, arguments); | ||
}; | ||
Constraint.prototype.unpipe = function unpipe() { | ||
return this.unpipe.apply(this, arguments); | ||
}; | ||
module.exports = Constraint; |
@@ -31,2 +31,4 @@ /* This Source Code Form is subject to the terms of the Mozilla Public | ||
function Snap(options) { | ||
Constraint.call(this); | ||
this.options = Object.create(this.constructor.DEFAULT_OPTIONS); | ||
@@ -40,4 +42,2 @@ if (options) this.setOptions(options); | ||
this.impulse2 = new Vector(); | ||
Constraint.call(this); | ||
} | ||
@@ -49,3 +49,3 @@ | ||
Snap.DEFAULT_OPTIONS = { | ||
period : 300, | ||
period : 300, | ||
dampingRatio : 0.1, | ||
@@ -58,6 +58,2 @@ length : 0, | ||
function _calcEnergy(impulse, disp, dt) { | ||
return Math.abs(impulse.dot(disp)/dt); | ||
} | ||
/** | ||
@@ -78,25 +74,14 @@ * Basic options setter | ||
if (options.period !== undefined) this.options.period = options.period; | ||
Constraint.prototype.setOptions.call(this, options); | ||
}; | ||
/** | ||
* Set the anchor position | ||
* | ||
* @method setOptions | ||
* @param {Array} v TODO | ||
*/ | ||
Snap.prototype.setAnchor = function setAnchor(v) { | ||
if (this.options.anchor !== undefined) this.options.anchor = new Vector(); | ||
this.options.anchor.set(v); | ||
}; | ||
/** | ||
* Calculates energy of spring | ||
* | ||
* @method getEnergy | ||
* @param {Object} target TODO | ||
* @param {Object} source TODO | ||
* @param targets {Body} target physics body | ||
* @param source {Body} source physics body | ||
* @return energy {Number} | ||
*/ | ||
Snap.prototype.getEnergy = function getEnergy(target, source) { | ||
Snap.prototype.getEnergy = function getEnergy(targets, source) { | ||
var options = this.options; | ||
@@ -107,5 +92,9 @@ var restLength = options.length; | ||
var dist = anchor.sub(target.position).norm() - restLength; | ||
return 0.5 * strength * dist * dist; | ||
var energy = 0.0; | ||
for (var i = 0; i < targets.length; i++){ | ||
var target = targets[i]; | ||
var dist = anchor.sub(target.position).norm() - restLength; | ||
energy += 0.5 * strength * dist * dist; | ||
} | ||
return energy; | ||
}; | ||
@@ -122,3 +111,3 @@ | ||
Snap.prototype.applyConstraint = function applyConstraint(targets, source, dt) { | ||
var options = this.options; | ||
var options = this.options; | ||
var pDiff = this.pDiff; | ||
@@ -149,3 +138,3 @@ var vDiff = this.vDiff; | ||
vDiff.set(v1.sub(v2)); | ||
effMass = 1/(w1 + w2); | ||
effMass = 1 / (w1 + w2); | ||
} | ||
@@ -189,4 +178,2 @@ else { | ||
} | ||
this.setEnergy(_calcEnergy(impulse1, pDiff, dt)); | ||
} | ||
@@ -193,0 +180,0 @@ }; |
@@ -22,4 +22,4 @@ /* This Source Code Form is subject to the terms of the Mozilla Public | ||
this.force = new Vector(force); | ||
this._energy = 0.0; | ||
this._eventOutput = null; | ||
this._eventOutput = new EventHandler(); | ||
EventHandler.setOutputHandler(this, this._eventOutput); | ||
} | ||
@@ -34,3 +34,3 @@ | ||
Force.prototype.setOptions = function setOptions(options) { | ||
for (var key in options) this.options[key] = options[key]; | ||
this._eventOutput.emit('change', options); | ||
}; | ||
@@ -55,40 +55,5 @@ | ||
Force.prototype.getEnergy = function getEnergy() { | ||
return this._energy; | ||
return 0.0; | ||
}; | ||
/* | ||
* Setter for a force's potential energy. | ||
* | ||
* @method setEnergy | ||
* @param energy {Number} | ||
*/ | ||
Force.prototype.setEnergy = function setEnergy(energy) { | ||
this._energy = energy; | ||
}; | ||
function _createEventOutput() { | ||
this._eventOutput = new EventHandler(); | ||
this._eventOutput.bindThis(this); | ||
EventHandler.setOutputHandler(this, this._eventOutput); | ||
} | ||
Force.prototype.on = function on() { | ||
_createEventOutput.call(this); | ||
return this.on.apply(this, arguments); | ||
}; | ||
Force.prototype.addListener = function addListener() { | ||
_createEventOutput.call(this); | ||
return this.addListener.apply(this, arguments); | ||
}; | ||
Force.prototype.pipe = function pipe() { | ||
_createEventOutput.call(this); | ||
return this.pipe.apply(this, arguments); | ||
}; | ||
Force.prototype.removeListener = function removeListener() { | ||
return this.removeListener.apply(this, arguments); | ||
}; | ||
Force.prototype.unpipe = function unpipe() { | ||
return this.unpipe.apply(this, arguments); | ||
}; | ||
module.exports = Force; |
@@ -10,3 +10,2 @@ /* This Source Code Form is subject to the terms of the Mozilla Public | ||
//TODO: test options manager | ||
var Force = require('./Force'); | ||
@@ -13,0 +12,0 @@ var Vector = require('../../math/Vector'); |
@@ -11,3 +11,5 @@ /* This Source Code Form is subject to the terms of the Mozilla Public | ||
//TODO: test inheritance | ||
var Force = require('./Force'); | ||
var Spring = require('./Spring'); | ||
var Quaternion = require('../../math/Quaternion'); | ||
@@ -35,2 +37,42 @@ /** | ||
/** @const */ | ||
var pi = Math.PI; | ||
function _calcStiffness() { | ||
var options = this.options; | ||
options.stiffness = Math.pow(2 * pi / options.period, 2); | ||
} | ||
function _calcDamping() { | ||
var options = this.options; | ||
options.damping = 4 * pi * options.dampingRatio / options.period; | ||
} | ||
function _init() { | ||
_calcStiffness.call(this); | ||
_calcDamping.call(this); | ||
} | ||
RotationalSpring.prototype.setOptions = function setOptions(options) { | ||
// TODO fix no-console error | ||
/* eslint no-console: 0 */ | ||
if (options.anchor !== undefined) { | ||
if (options.anchor instanceof Quaternion) this.options.anchor = options.anchor; | ||
if (options.anchor instanceof Array) this.options.anchor = new Quaternion(options.anchor); | ||
} | ||
if (options.period !== undefined){ | ||
this.options.period = options.period; | ||
} | ||
if (options.dampingRatio !== undefined) this.options.dampingRatio = options.dampingRatio; | ||
if (options.length !== undefined) this.options.length = options.length; | ||
if (options.forceFunction !== undefined) this.options.forceFunction = options.forceFunction; | ||
if (options.maxLength !== undefined) this.options.maxLength = options.maxLength; | ||
_init.call(this); | ||
Force.prototype.setOptions.call(this, options); | ||
}; | ||
/** | ||
@@ -43,10 +85,12 @@ * Adds a torque force to a physics body's torque accumulator. | ||
RotationalSpring.prototype.applyForce = function applyForce(targets) { | ||
var force = this.force; | ||
var options = this.options; | ||
var disp = this.disp; | ||
var force = this.force; | ||
var options = this.options; | ||
var disp = this.disp; | ||
var stiffness = options.stiffness; | ||
var damping = options.damping; | ||
var restLength = options.length; | ||
var anchor = options.anchor; | ||
var stiffness = options.stiffness; | ||
var damping = options.damping; | ||
var restLength = options.length; | ||
var anchor = options.anchor; | ||
var forceFunction = options.forceFunction; | ||
var maxLength = options.maxLength; | ||
@@ -66,5 +110,5 @@ for (var i = 0; i < targets.length; i++) { | ||
force.set(disp.normalize(stiffness * this.forceFunction(dist, this.options.lMax))); | ||
force.set(disp.normalize(stiffness * forceFunction(dist, maxLength))); | ||
if (damping) force.set(force.add(target.angularVelocity.mult(-damping))); | ||
if (damping) force.add(target.angularVelocity.mult(-damping)).put(force); | ||
@@ -79,5 +123,5 @@ target.applyTorque(force); | ||
* @method getEnergy | ||
* @param {Body} target The physics body attached to the spring | ||
* @param [targets] target The physics body attached to the spring | ||
*/ | ||
RotationalSpring.prototype.getEnergy = function getEnergy(target) { | ||
RotationalSpring.prototype.getEnergy = function getEnergy(targets) { | ||
var options = this.options; | ||
@@ -88,6 +132,11 @@ var restLength = options.length; | ||
var dist = anchor.sub(target.orientation).norm() - restLength; | ||
return 0.5 * strength * dist * dist; | ||
var energy = 0.0; | ||
for (var i = 0; i < targets.length; i++) { | ||
var target = targets[i]; | ||
var dist = anchor.sub(target.orientation).norm() - restLength; | ||
energy += 0.5 * strength * dist * dist; | ||
} | ||
return energy; | ||
}; | ||
module.exports = RotationalSpring; |
@@ -10,2 +10,4 @@ /* This Source Code Form is subject to the terms of the Mozilla Public | ||
/*global console */ | ||
var Force = require('./Force'); | ||
@@ -24,2 +26,4 @@ var Vector = require('../../math/Vector'); | ||
function Spring(options) { | ||
Force.call(this); | ||
this.options = Object.create(this.constructor.DEFAULT_OPTIONS); | ||
@@ -32,3 +36,2 @@ if (options) this.setOptions(options); | ||
_init.call(this); | ||
Force.call(this); | ||
} | ||
@@ -39,3 +42,5 @@ | ||
/** @const */ var pi = Math.PI; | ||
/** @const */ | ||
var pi = Math.PI; | ||
var MIN_PERIOD = 150; | ||
@@ -67,3 +72,3 @@ /** | ||
* A Hookean spring force, linear in the displacement | ||
* see: http://en.wikipedia.org/wiki/FENE | ||
* see: http://en.wikipedia.org/wiki/Hooke's_law | ||
* @attribute FENE | ||
@@ -95,3 +100,3 @@ * @type Function | ||
*/ | ||
period : 300, | ||
period : 300, | ||
@@ -144,6 +149,2 @@ /** | ||
function _setForceFunction(fn) { | ||
this.forceFunction = fn; | ||
} | ||
function _calcStiffness() { | ||
@@ -159,8 +160,3 @@ var options = this.options; | ||
function _calcEnergy(strength, dist) { | ||
return 0.5 * strength * dist * dist; | ||
} | ||
function _init() { | ||
_setForceFunction.call(this, this.options.forceFunction); | ||
_calcStiffness.call(this); | ||
@@ -174,11 +170,22 @@ _calcDamping.call(this); | ||
* @method setOptions | ||
* @param options {Objects} | ||
* @param options {Object} | ||
*/ | ||
Spring.prototype.setOptions = function setOptions(options) { | ||
// TODO fix no-console error | ||
/* eslint no-console: 0 */ | ||
if (options.anchor !== undefined) { | ||
if (options.anchor.position instanceof Vector) this.options.anchor = options.anchor.position; | ||
if (options.anchor instanceof Vector) this.options.anchor = options.anchor; | ||
if (options.anchor instanceof Array) this.options.anchor = new Vector(options.anchor); | ||
if (options.anchor instanceof Vector) this.options.anchor = options.anchor; | ||
if (options.anchor instanceof Array) this.options.anchor = new Vector(options.anchor); | ||
} | ||
if (options.period !== undefined) this.options.period = options.period; | ||
if (options.period !== undefined){ | ||
if (options.period < MIN_PERIOD) { | ||
options.period = MIN_PERIOD; | ||
console.warn('The period of a SpringTransition is capped at ' + MIN_PERIOD + ' ms. Use a SnapTransition for faster transitions'); | ||
} | ||
this.options.period = options.period; | ||
} | ||
if (options.dampingRatio !== undefined) this.options.dampingRatio = options.dampingRatio; | ||
@@ -190,2 +197,3 @@ if (options.length !== undefined) this.options.length = options.length; | ||
_init.call(this); | ||
Force.prototype.setOptions.call(this, options); | ||
}; | ||
@@ -200,11 +208,12 @@ | ||
Spring.prototype.applyForce = function applyForce(targets, source) { | ||
var force = this.force; | ||
var disp = this.disp; | ||
var options = this.options; | ||
var force = this.force; | ||
var disp = this.disp; | ||
var options = this.options; | ||
var stiffness = options.stiffness; | ||
var damping = options.damping; | ||
var restLength = options.length; | ||
var lMax = options.maxLength; | ||
var anchor = options.anchor || source.position; | ||
var stiffness = options.stiffness; | ||
var damping = options.damping; | ||
var restLength = options.length; | ||
var maxLength = options.maxLength; | ||
var anchor = options.anchor || source.position; | ||
var forceFunction = options.forceFunction; | ||
@@ -226,3 +235,3 @@ for (var i = 0; i < targets.length; i++) { | ||
disp.normalize(stiffness * this.forceFunction(dist, lMax)) | ||
disp.normalize(stiffness * forceFunction(dist, maxLength)) | ||
.put(force); | ||
@@ -236,4 +245,2 @@ | ||
if (source) source.applyForce(force.mult(-1)); | ||
this.setEnergy(_calcEnergy(stiffness, dist)); | ||
} | ||
@@ -246,26 +253,20 @@ }; | ||
* @method getEnergy | ||
* @param target {Body} The physics body attached to the spring | ||
* @return energy {Number} | ||
* @param [targets] target The physics body attached to the spring | ||
* @return {source} The potential energy of the spring | ||
*/ | ||
Spring.prototype.getEnergy = function getEnergy(target) { | ||
var options = this.options; | ||
Spring.prototype.getEnergy = function getEnergy(targets, source) { | ||
var options = this.options; | ||
var restLength = options.length; | ||
var anchor = options.anchor; | ||
var anchor = (source) ? source.position : options.anchor; | ||
var strength = options.stiffness; | ||
var dist = anchor.sub(target.position).norm() - restLength; | ||
return 0.5 * strength * dist * dist; | ||
var energy = 0.0; | ||
for (var i = 0; i < targets.length; i++){ | ||
var target = targets[i]; | ||
var dist = anchor.sub(target.position).norm() - restLength; | ||
energy += 0.5 * strength * dist * dist; | ||
} | ||
return energy; | ||
}; | ||
/** | ||
* Sets the anchor to a new position | ||
* | ||
* @method setAnchor | ||
* @param anchor {Array} New anchor of the spring | ||
*/ | ||
Spring.prototype.setAnchor = function setAnchor(anchor) { | ||
if (!this.options.anchor) this.options.anchor = new Vector(); | ||
this.options.anchor.set(anchor); | ||
}; | ||
module.exports = Spring; |
@@ -23,10 +23,9 @@ /* This Source Code Form is subject to the terms of the Mozilla Public | ||
function VectorField(options) { | ||
Force.call(this); | ||
this.options = Object.create(VectorField.DEFAULT_OPTIONS); | ||
if (options) this.setOptions(options); | ||
_setFieldOptions.call(this, this.options.field); | ||
Force.call(this); | ||
//registers | ||
this.evaluation = new Vector(0,0,0); | ||
this.evaluation = new Vector(); | ||
} | ||
@@ -54,3 +53,3 @@ | ||
CONSTANT : function(v, options) { | ||
return v.set(options.direction); | ||
options.direction.put(this.evaluation); | ||
}, | ||
@@ -63,6 +62,6 @@ | ||
* @param v {Vector} Current position of physics body | ||
* @return {Number} unscaled force | ||
* @return {Vector} unscaled force | ||
*/ | ||
LINEAR : function(v) { | ||
return v; | ||
v.put(this.evaluation); | ||
}, | ||
@@ -75,22 +74,9 @@ | ||
* @param v {Vector} Current position of physics body | ||
* @return {Number} unscaled force | ||
* @return {Vector} unscaled force | ||
*/ | ||
RADIAL : function(v) { | ||
return v.set(v.mult(-1, v)); | ||
v.mult(-1).put(this.evaluation); | ||
}, | ||
/** | ||
* Spherical force | ||
* @attribute SPHERE_ATTRACTOR | ||
* @type Function | ||
* @param v {Vector} Current position of physics body | ||
* @param options {Object} An object with the radius of the sphere | ||
* Pass a {radius : Number} into the VectorField options | ||
* @return {Number} unscaled force | ||
*/ | ||
SPHERE_ATTRACTOR : function(v, options) { | ||
return v.set(v.mult((options.radius - v.norm()) / v.norm())); | ||
}, | ||
/** | ||
* Point attractor force, e.g., Hookean spring with an anchor | ||
@@ -102,6 +88,6 @@ * @attribute POINT_ATTRACTOR | ||
* Pass a {position : Vector} into the VectorField options | ||
* @return {Number} unscaled force | ||
* @return {Vector} unscaled force | ||
*/ | ||
POINT_ATTRACTOR : function(v, options) { | ||
return v.set(options.position.sub(v)); | ||
options.position.sub(v).put(this.evaluation); | ||
} | ||
@@ -123,5 +109,5 @@ }; | ||
* @type Number | ||
* @default 1 | ||
* @default .01 | ||
*/ | ||
strength : 1, | ||
strength : .01, | ||
@@ -144,3 +130,7 @@ /** | ||
VectorField.prototype.setOptions = function setOptions(options) { | ||
for (var key in options) this.options[key] = options[key]; | ||
if (options.strength !== undefined) this.options.strength = options.strength; | ||
if (options.field !== undefined) { | ||
this.options.field = options.field; | ||
_setFieldOptions.call(this, this.options.field); | ||
} | ||
}; | ||
@@ -154,21 +144,13 @@ | ||
if (!this.options.direction) this.options.direction = new Vector(0,1,0); | ||
else if (this.options.direction instanceof Array) this.options.direction = new Vector(this.options.direction); | ||
break; | ||
case FIELDS.POINT_ATTRACTOR: | ||
if (!this.options.position) this.options.position = new Vector(0,0,0); | ||
else if (this.options.position instanceof Array) this.options.position = new Vector(this.options.position); | ||
break; | ||
case FIELDS.SPHERE_ATTRACTOR: | ||
if (!this.options.radius) this.options.radius = 1; | ||
break; | ||
} | ||
} | ||
function _evaluate(v) { | ||
var evaluation = this.evaluation; | ||
var field = this.options.field; | ||
evaluation.set(v); | ||
return field(evaluation, this.options); | ||
} | ||
/** | ||
* Adds the vectorfield's force to a physics body's force accumulator. | ||
* Adds the VectorField's force to a physics body's force accumulator. | ||
* | ||
@@ -180,12 +162,42 @@ * @method applyForce | ||
var force = this.force; | ||
var strength = this.options.strength; | ||
var field = this.options.field; | ||
for (var i = 0; i < targets.length; i++) { | ||
var particle = targets[i]; | ||
force.set( | ||
_evaluate.call(this, particle.position) | ||
.mult(particle.mass * this.options.strength) | ||
); | ||
particle.applyForce(force); | ||
var target = targets[i]; | ||
field.call(this, target.position, this.options); | ||
this.evaluation.mult(target.mass * strength).put(force); | ||
target.applyForce(force); | ||
} | ||
}; | ||
VectorField.prototype.getEnergy = function getEnergy(targets) { | ||
var field = this.options.field; | ||
var FIELDS = VectorField.FIELDS; | ||
var energy = 0; | ||
var i; | ||
var target; | ||
switch (field) { | ||
case FIELDS.CONSTANT: | ||
energy = targets.length * this.options.direction.norm(); | ||
break; | ||
case FIELDS.RADIAL: | ||
for (i = 0; i < targets.length; i++){ | ||
target = targets[i]; | ||
energy += target.position.norm(); | ||
} | ||
break; | ||
case FIELDS.POINT_ATTRACTOR: | ||
for (i = 0; i < targets.length; i++){ | ||
target = targets[i]; | ||
energy += target.position.sub(this.options.position).norm(); | ||
} | ||
break; | ||
} | ||
energy *= this.options.strength; | ||
return energy; | ||
}; | ||
module.exports = VectorField; |
module.exports = { | ||
PhysicsEngine: require('./PhysicsEngine'), | ||
bodies: require('./bodies'), | ||
forces: require('./forces'), | ||
constraints: require('./constraints'), | ||
forces: require('./forces'), | ||
integrators: require('./integrators') | ||
}; |
@@ -10,4 +10,5 @@ /* This Source Code Form is subject to the terms of the Mozilla Public | ||
var OptionsManager = require('../../core/OptionsManager'); | ||
/** | ||
@@ -31,56 +32,5 @@ * Ordinary Differential Equation (ODE) Integrator. | ||
*/ | ||
function SymplecticEuler(options) { | ||
this.options = Object.create(SymplecticEuler.DEFAULT_OPTIONS); | ||
this._optionsManager = new OptionsManager(this.options); | ||
var SymplecticEuler = {}; | ||
if (options) this.setOptions(options); | ||
} | ||
/** | ||
* @property SymplecticEuler.DEFAULT_OPTIONS | ||
* @type Object | ||
* @protected | ||
* @static | ||
*/ | ||
SymplecticEuler.DEFAULT_OPTIONS = { | ||
/** | ||
* The maximum velocity of a physics body | ||
* Range : [0, Infinity] | ||
* @attribute velocityCap | ||
* @type Number | ||
*/ | ||
velocityCap : undefined, | ||
/** | ||
* The maximum angular velocity of a physics body | ||
* Range : [0, Infinity] | ||
* @attribute angularVelocityCap | ||
* @type Number | ||
*/ | ||
angularVelocityCap : undefined | ||
}; | ||
/* | ||
* Setter for options | ||
* | ||
* @method setOptions | ||
* @param {Object} options | ||
*/ | ||
SymplecticEuler.prototype.setOptions = function setOptions(options) { | ||
this._optionsManager.patch(options); | ||
}; | ||
/* | ||
* Getter for options | ||
* | ||
* @method getOptions | ||
* @return {Object} options | ||
*/ | ||
SymplecticEuler.prototype.getOptions = function getOptions() { | ||
return this._optionsManager.value(); | ||
}; | ||
/* | ||
* Updates the velocity of a physics body from its accumulated force. | ||
@@ -93,3 +43,3 @@ * v <- v + dt * f / m | ||
*/ | ||
SymplecticEuler.prototype.integrateVelocity = function integrateVelocity(body, dt) { | ||
SymplecticEuler.integrateVelocity = function integrateVelocity(body, dt) { | ||
var v = body.velocity; | ||
@@ -113,7 +63,6 @@ var w = body.inverseMass; | ||
*/ | ||
SymplecticEuler.prototype.integratePosition = function integratePosition(body, dt) { | ||
SymplecticEuler.integratePosition = function integratePosition(body, dt) { | ||
var p = body.position; | ||
var v = body.velocity; | ||
if (this.options.velocityCap) v.cap(this.options.velocityCap).put(v); | ||
p.add(v.mult(dt)).put(p); | ||
@@ -130,3 +79,3 @@ }; | ||
*/ | ||
SymplecticEuler.prototype.integrateAngularMomentum = function integrateAngularMomentum(body, dt) { | ||
SymplecticEuler.integrateAngularMomentum = function integrateAngularMomentum(body, dt) { | ||
var L = body.angularMomentum; | ||
@@ -137,3 +86,2 @@ var t = body.torque; | ||
if (this.options.angularVelocityCap) t.cap(this.options.angularVelocityCap).put(t); | ||
L.add(t.mult(dt)).put(L); | ||
@@ -151,3 +99,3 @@ t.clear(); | ||
*/ | ||
SymplecticEuler.prototype.integrateOrientation = function integrateOrientation(body, dt) { | ||
SymplecticEuler.integrateOrientation = function integrateOrientation(body, dt) { | ||
var q = body.orientation; | ||
@@ -154,0 +102,0 @@ var w = body.angularVelocity; |
@@ -11,7 +11,11 @@ /* This Source Code Form is subject to the terms of the Mozilla Public | ||
/** | ||
* The Physics Engine is responsible for mediating Bodies and their | ||
* interaction with forces and constraints. The Physics Engine handles the | ||
* logic of adding and removing bodies, updating their state of the over | ||
* time. | ||
* The Physics Engine is responsible for mediating bodies with their | ||
* interaction with forces and constraints (agents). Specifically, it | ||
* is responsible for: | ||
* | ||
* - adding and removing bodies | ||
* - updating a body's state over time | ||
* - attaching and detaching agents | ||
* - sleeping upon equillibrium and waking upon excitation | ||
* | ||
* @class PhysicsEngine | ||
@@ -27,5 +31,5 @@ * @constructor | ||
this._bodies = []; //list of managed bodies | ||
this._agents = {}; //hash of managed agents | ||
this._forces = []; //list of IDs of agents that are forces | ||
this._constraints = []; //list of IDs of agents that are constraints | ||
this._agentData = {}; //hash of managed agent data | ||
this._forces = []; //list of Ids of agents that are forces | ||
this._constraints = []; //list of Ids of agents that are constraints | ||
@@ -38,4 +42,6 @@ this._buffer = 0.0; | ||
this._hasBodies = false; | ||
this._eventHandler = null; | ||
} | ||
/** const */ | ||
var TIMESTEP = 17; | ||
@@ -45,2 +51,11 @@ var MIN_TIME_STEP = 1000 / 120; | ||
var now = Date.now; | ||
// Catalogue of outputted events | ||
var _events = { | ||
start : 'start', | ||
update : 'update', | ||
end : 'end' | ||
}; | ||
/** | ||
@@ -62,13 +77,25 @@ * @property PhysicsEngine.DEFAULT_OPTIONS | ||
/** | ||
* The energy threshold before the Engine stops updating | ||
* The energy threshold required for the Physics Engine to update | ||
* @attribute sleepTolerance | ||
* @type Number | ||
*/ | ||
sleepTolerance : 1e-7 | ||
sleepTolerance : 1e-7, | ||
/** | ||
* The maximum velocity magnitude of a physics body | ||
* Range : [0, Infinity] | ||
* @attribute velocityCap | ||
* @type Number | ||
*/ | ||
velocityCap : undefined, | ||
/** | ||
* The maximum angular velocity magnitude of a physics body | ||
* Range : [0, Infinity] | ||
* @attribute angularVelocityCap | ||
* @type Number | ||
*/ | ||
angularVelocityCap : undefined | ||
}; | ||
var now = (function() { | ||
return Date.now; | ||
})(); | ||
/** | ||
@@ -86,3 +113,3 @@ * Options setter | ||
* Method to add a physics body to the engine. Necessary to update the | ||
* body over time. | ||
* body over time. | ||
* | ||
@@ -100,2 +127,3 @@ * @method addBody | ||
else this._particles.push(body); | ||
body.on('start', this.wake.bind(this)); | ||
return body; | ||
@@ -106,4 +134,6 @@ }; | ||
* Remove a body from the engine. Detaches body from all forces and | ||
* constraints. | ||
* constraints. | ||
* | ||
* TODO: Fix for in loop | ||
* | ||
* @method removeBody | ||
@@ -116,3 +146,3 @@ * @param body {Body} | ||
if (index > -1) { | ||
for (var i = 0; i < Object.keys(this._agents).length; i++) this.detachFrom(i, body); | ||
for (var agent in this._agentData) this.detachFrom(agent.id, body); | ||
array.splice(index,1); | ||
@@ -132,4 +162,7 @@ } | ||
this._agents[this._currAgentId] = { | ||
agent.on('change', this.wake.bind(this)); | ||
this._agentData[this._currAgentId] = { | ||
agent : agent, | ||
id : this._currAgentId, | ||
targets : targets, | ||
@@ -145,3 +178,3 @@ source : source | ||
* Attaches a force or constraint to a Body. Returns an AgentId of the | ||
* attached agent which can be used to detach the agent. | ||
* attached agent which can be used to detach the agent. | ||
* | ||
@@ -155,2 +188,4 @@ * @method attach | ||
PhysicsEngine.prototype.attach = function attach(agents, targets, source) { | ||
this.wake(); | ||
if (agents instanceof Array) { | ||
@@ -173,3 +208,3 @@ var agentIDs = []; | ||
PhysicsEngine.prototype.attachTo = function attachTo(agentID, target) { | ||
_getBoundAgent.call(this, agentID).targets.push(target); | ||
_getAgentData.call(this, agentID).targets.push(target); | ||
}; | ||
@@ -179,3 +214,3 @@ | ||
* Undoes PhysicsEngine.attach. Removes an agent and its associated | ||
* effect on its affected Bodies. | ||
* effect on its affected Bodies. | ||
* | ||
@@ -193,3 +228,3 @@ * @method detach | ||
// detach agents array | ||
delete this._agents[id]; | ||
delete this._agentData[id]; | ||
}; | ||
@@ -205,3 +240,3 @@ | ||
PhysicsEngine.prototype.detachFrom = function detachFrom(id, target) { | ||
var boundAgent = _getBoundAgent.call(this, id); | ||
var boundAgent = _getAgentData.call(this, id); | ||
if (boundAgent.source === target) this.detach(id); | ||
@@ -222,3 +257,3 @@ else { | ||
PhysicsEngine.prototype.detachAll = function detachAll() { | ||
this._agents = {}; | ||
this._agentData = {}; | ||
this._forces = []; | ||
@@ -229,4 +264,4 @@ this._constraints = []; | ||
function _getBoundAgent(id) { | ||
return this._agents[id]; | ||
function _getAgentData(id) { | ||
return this._agentData[id]; | ||
} | ||
@@ -241,3 +276,3 @@ | ||
PhysicsEngine.prototype.getAgent = function getAgent(id) { | ||
return _getBoundAgent.call(this, id).agent; | ||
return _getAgentData.call(this, id).agent; | ||
}; | ||
@@ -277,3 +312,3 @@ | ||
* Iterates over every Particle and applies a function whose first | ||
* argument is the Particle | ||
* argument is the Particle | ||
* | ||
@@ -292,3 +327,3 @@ * @method forEachParticle | ||
* Iterates over every Body that isn't a Particle and applies | ||
* a function whose first argument is the Body | ||
* a function whose first argument is the Body | ||
* | ||
@@ -308,3 +343,3 @@ * @method forEachBody | ||
* Iterates over every Body and applies a function whose first | ||
* argument is the Body | ||
* argument is the Body | ||
* | ||
@@ -321,3 +356,3 @@ * @method forEach | ||
function _updateForce(index) { | ||
var boundAgent = _getBoundAgent.call(this, this._forces[index]); | ||
var boundAgent = _getAgentData.call(this, this._forces[index]); | ||
boundAgent.agent.applyForce(boundAgent.targets, boundAgent.source); | ||
@@ -332,3 +367,3 @@ } | ||
function _updateConstraint(index, dt) { | ||
var boundAgent = this._agents[this._constraints[index]]; | ||
var boundAgent = this._agentData[this._constraints[index]]; | ||
return boundAgent.agent.applyConstraint(boundAgent.targets, boundAgent.source, dt); | ||
@@ -346,4 +381,6 @@ } | ||
function _updateVelocities(particle, dt) { | ||
particle.integrateVelocity(dt); | ||
function _updateVelocities(body, dt) { | ||
body.integrateVelocity(dt); | ||
if (this.options.velocityCap) | ||
body.velocity.cap(this.options.velocityCap).put(body.velocity); | ||
} | ||
@@ -354,2 +391,4 @@ | ||
body.updateAngularVelocity(); | ||
if (this.options.angularVelocityCap) | ||
body.angularVelocity.cap(this.options.angularVelocityCap).put(body.angularVelocity); | ||
} | ||
@@ -361,5 +400,5 @@ | ||
function _updatePositions(particle, dt) { | ||
particle.integratePosition(dt); | ||
particle.emit('update', particle); | ||
function _updatePositions(body, dt) { | ||
body.integratePosition(dt); | ||
body.emit(_events.update, body); | ||
} | ||
@@ -376,3 +415,3 @@ | ||
function _getEnergyParticles() { | ||
function _getParticlesEnergy() { | ||
var energy = 0.0; | ||
@@ -383,3 +422,2 @@ var particleEnergy = 0.0; | ||
energy += particleEnergy; | ||
if (particleEnergy < particle.sleepTolerance) particle.sleep(); | ||
}); | ||
@@ -389,19 +427,24 @@ return energy; | ||
function _getEnergyForces() { | ||
function _getAgentsEnergy() { | ||
var energy = 0; | ||
for (var index = this._forces.length - 1; index > -1; index--) | ||
energy += this._forces[index].getEnergy() || 0.0; | ||
for (var id in this._agentData) | ||
energy += this.getAgentEnergy(id); | ||
return energy; | ||
} | ||
function _getEnergyConstraints() { | ||
var energy = 0; | ||
for (var index = this._constraints.length - 1; index > -1; index--) | ||
energy += this._constraints[index].getEnergy() || 0.0; | ||
return energy; | ||
} | ||
/** | ||
* Calculates the potential energy of an agent, like a spring, by its Id | ||
* | ||
* @method getAgentEnergy | ||
* @param agentId {Number} The attached agent Id | ||
* @return energy {Number} | ||
*/ | ||
PhysicsEngine.prototype.getAgentEnergy = function(agentId) { | ||
var agentData = _getAgentData.call(this, agentId); | ||
return agentData.agent.getEnergy(agentData.targets, agentData.source); | ||
}; | ||
/** | ||
* Calculates the kinetic energy of all Body objects and potential energy | ||
* of all attached agents. | ||
* of all attached agents. | ||
* | ||
@@ -413,3 +456,3 @@ * TODO: implement. | ||
PhysicsEngine.prototype.getEnergy = function getEnergy() { | ||
return _getEnergyParticles.call(this) + _getEnergyForces.call(this) + _getEnergyConstraints.call(this); | ||
return _getParticlesEnergy.call(this) + _getAgentsEnergy.call(this); | ||
}; | ||
@@ -419,3 +462,3 @@ | ||
* Updates all Body objects managed by the physics engine over the | ||
* time duration since the last time step was called. | ||
* time duration since the last time step was called. | ||
* | ||
@@ -425,6 +468,3 @@ * @method step | ||
PhysicsEngine.prototype.step = function step() { | ||
// if (this.getEnergy() < this.options.sleepTolerance) { | ||
// this.sleep(); | ||
// return; | ||
// }; | ||
if (this.isSleeping()) return; | ||
@@ -450,5 +490,8 @@ //set current frame's time | ||
// this._buffer = 0.0; | ||
_integrate.call(this, TIMESTEP); | ||
// this.emit('update', this); | ||
this.emit(_events.update, this); | ||
if (this.getEnergy() < this.options.sleepTolerance) this.sleep(); | ||
}; | ||
@@ -458,2 +501,3 @@ | ||
* Tells whether the Physics Engine is sleeping or awake. | ||
* | ||
* @method isSleeping | ||
@@ -467,7 +511,22 @@ * @return {Boolean} | ||
/** | ||
* Stops the Physics Engine from updating. Emits an 'end' event. | ||
* Tells whether the Physics Engine is sleeping or awake. | ||
* | ||
* @method isActive | ||
* @return {Boolean} | ||
*/ | ||
PhysicsEngine.prototype.isActive = function isSleeping() { | ||
return !this._isSleeping; | ||
}; | ||
/** | ||
* Stops the Physics Engine update loop. Emits an 'end' event. | ||
* | ||
* @method sleep | ||
*/ | ||
PhysicsEngine.prototype.sleep = function sleep() { | ||
this.emit('end', this); | ||
if (this._isSleeping) return; | ||
this.forEach(function(body) { | ||
body.sleep(); | ||
}); | ||
this.emit(_events.end, this); | ||
this._isSleeping = true; | ||
@@ -477,8 +536,10 @@ }; | ||
/** | ||
* Starts the Physics Engine from updating. Emits an 'start' event. | ||
* Restarts the Physics Engine update loop. Emits an 'start' event. | ||
* | ||
* @method wake | ||
*/ | ||
PhysicsEngine.prototype.wake = function wake() { | ||
if (!this._isSleeping) return; | ||
this._prevTime = now(); | ||
this.emit('start', this); | ||
this.emit(_events.start, this); | ||
this._isSleeping = false; | ||
@@ -485,0 +546,0 @@ }; |
@@ -44,31 +44,5 @@ Famo.us | ||
Cloning this repository directly is primarily for those wishing to contribute to our codebase. Check out our [contributing instructions][contributing] to get involved. Since we use git submodules, all subfolders will be unpopulated unless you initialize and update your submodules. To clone from the command line, run | ||
git clone git@github.com:Famous/famous.git path/to/folder | ||
cd path/to/folder | ||
git submodule update --init | ||
Cloning this repository directly is primarily for those wishing to contribute to our codebase. Check out our [contributing instructions][contributing] to get involved. | ||
Or clone with the `--recursive` flag for a convenient one-liner | ||
git clone git@github.com:Famous/famous.git --recursive path/to/folder | ||
Note: cloning only provides the Famo.us folder with all Famo.us code, but it does no application scaffolding. You will additionally need to create your own index.html, and include the `famous.css` file that is included in `famous/core`. Require.js is currently a hard dependency for using Famo.us. | ||
## Famous.git Package | ||
This package contains the submodules necessary to be productive in Famo.us. They are all hosted on [our github organization][famous-organization-github]. | ||
| Submodule | Description | | ||
| --------- | ----------- | | ||
| core.git | The low level componentry of Famo.us, plus the required famous.css stylesheet. | | ||
| events.git | Events are used for communication between objects in Famous. | | ||
| inputs.git | The inputs library is used to interpret user input to the device. | | ||
| math.git | A simple math library used throughout the core. | | ||
| modifiers.git | Implementations of the core/Modifier pattern which output transforms to the render tree. | | ||
| physics.git | Core engine controlling animations via physical simulation. | | ||
| surfaces.git | Surfaces extend core/Surface and encapsulate common HTML tags like `<img>` and `<canvas>`.| | ||
| transitions.git | Transitions are used to create animation, usually by providing input to a Modifier. | | ||
| utilities.git | Utilities hosts various helper classes and static methods. | | ||
| views.git | Views are visually interactable components for use in applications. | | ||
| widgets.git | Widgets are small visually interactable components for use in applications with their own styling. | | ||
Note: cloning only provides the Famo.us folder with all Famo.us code, but it does no application scaffolding. You will additionally need to create your own index.html, and include the `famous.css` file that is included in `famous/core`. Require.js is currently a hard dependency to work off of the Famo.us head. | ||
@@ -75,0 +49,0 @@ ## Documentation |
module.exports = { | ||
CanvasSurface: require('./CanvasSurface'), | ||
ContainerSurface: require('./ContainerSurface'), | ||
ImageSurface: require('./ImageSurface'), | ||
FormContainerSurface: require('./FormContainerSurface'), | ||
ImageSurface: require('./ImageSurface'), | ||
InputSurface: require('./InputSurface'), | ||
@@ -7,0 +7,0 @@ SubmitInputSurface: require('./SubmitInputSurface'), |
@@ -26,3 +26,3 @@ /* This Source Code Form is subject to the terms of the Mozilla Public | ||
* @param {Array} [options.properties] string dictionary of HTML attributes to set on target div | ||
* @param {string} [options.content] inner (HTML) content of surface | ||
* @param {String} [options.src] videoUrl URL | ||
* @param {boolean} [options.autoplay] autoplay | ||
@@ -56,4 +56,9 @@ */ | ||
VideoSurface.prototype.setOptions = function setOptions(options) { | ||
for (var key in VideoSurface.DEFAULT_OPTIONS) { | ||
if (options[key] !== undefined) this.options[key] = options[key]; | ||
if (options.size) this.setSize(options.size); | ||
if (options.classes) this.setClasses(options.classes); | ||
if (options.properties) this.setProperties(options.properties); | ||
if (options.autoplay) this.options.autoplay = options.autoplay; | ||
if (options.src) { | ||
this._videoUrl = options.src; | ||
this._contentDirty = true; | ||
} | ||
@@ -60,0 +65,0 @@ }; |
@@ -25,7 +25,13 @@ /* This Source Code Form is subject to the terms of the Mozilla Public | ||
this._final = Transform.identity.slice(); | ||
this.translate = new Transitionable([0, 0, 0]); | ||
this.rotate = new Transitionable([0, 0, 0]); | ||
this.skew = new Transitionable([0, 0, 0]); | ||
this.scale = new Transitionable([1, 1, 1]); | ||
this._finalTranslate = [0, 0, 0]; | ||
this._finalRotate = [0, 0, 0]; | ||
this._finalSkew = [0, 0, 0]; | ||
this._finalScale = [1, 1, 1]; | ||
this.translate = new Transitionable(this._finalTranslate); | ||
this.rotate = new Transitionable(this._finalRotate); | ||
this.skew = new Transitionable(this._finalSkew); | ||
this.scale = new Transitionable(this._finalScale); | ||
if (transform) this.set(transform); | ||
@@ -43,2 +49,11 @@ } | ||
function _buildFinal() { | ||
return Transform.build({ | ||
translate: this._finalTranslate, | ||
rotate: this._finalRotate, | ||
skew: this._finalSkew, | ||
scale: this._finalScale | ||
}); | ||
} | ||
/** | ||
@@ -56,7 +71,5 @@ * An optimized way of setting only the translation component of a Transform | ||
TransitionableTransform.prototype.setTranslate = function setTranslate(translate, transition, callback) { | ||
this._finalTranslate = translate; | ||
this._final = _buildFinal.call(this); | ||
this.translate.set(translate, transition, callback); | ||
this._final = this._final.slice(); | ||
this._final[12] = translate[0]; | ||
this._final[13] = translate[1]; | ||
if (translate[2] !== undefined) this._final[14] = translate[2]; | ||
return this; | ||
@@ -77,7 +90,5 @@ }; | ||
TransitionableTransform.prototype.setScale = function setScale(scale, transition, callback) { | ||
this._finalScale = scale; | ||
this._final = _buildFinal.call(this); | ||
this.scale.set(scale, transition, callback); | ||
this._final = this._final.slice(); | ||
this._final[0] = scale[0]; | ||
this._final[5] = scale[1]; | ||
if (scale[2] !== undefined) this._final[10] = scale[2]; | ||
return this; | ||
@@ -98,10 +109,5 @@ }; | ||
TransitionableTransform.prototype.setRotate = function setRotate(eulerAngles, transition, callback) { | ||
this._finalRotate = eulerAngles; | ||
this._final = _buildFinal.call(this); | ||
this.rotate.set(eulerAngles, transition, callback); | ||
this._final = _build.call(this); | ||
this._final = Transform.build({ | ||
translate: this.translate.get(), | ||
rotate: eulerAngles, | ||
scale: this.scale.get(), | ||
skew: this.skew.get() | ||
}); | ||
return this; | ||
@@ -122,9 +128,5 @@ }; | ||
TransitionableTransform.prototype.setSkew = function setSkew(skewAngles, transition, callback) { | ||
this._finalSkew = skewAngles; | ||
this._final = _buildFinal.call(this); | ||
this.skew.set(skewAngles, transition, callback); | ||
this._final = Transform.build({ | ||
translate: this.translate.get(), | ||
rotate: this.rotate.get(), | ||
scale: this.scale.get(), | ||
skew: skewAngles | ||
}); | ||
return this; | ||
@@ -146,5 +148,10 @@ }; | ||
TransitionableTransform.prototype.set = function set(transform, transition, callback) { | ||
this._final = transform; | ||
var components = Transform.interpret(transform); | ||
this._finalTranslate = components.translate; | ||
this._finalRotate = components.rotate; | ||
this._finalSkew = components.skew; | ||
this._finalScale = components.scale; | ||
this._final = transform; | ||
var _callback = callback ? Utility.after(4, callback) : null; | ||
@@ -151,0 +158,0 @@ this.translate.set(components.translate, transition, _callback); |
@@ -25,3 +25,3 @@ /* This Source Code Form is subject to the terms of the Mozilla Public | ||
var getTime = (window.performance) ? | ||
var getTime = (window.performance && window.performance.now) ? | ||
function() { | ||
@@ -28,0 +28,0 @@ return window.performance.now(); |
@@ -124,3 +124,3 @@ /* This Source Code Form is subject to the terms of the Mozilla Public | ||
* outTransformFrom sets the accessor for the state of the transform used in transitioning out renderables. | ||
* @method show | ||
* @method outTransformFrom | ||
* @param {Function|Transitionable} transform A function that returns a transform from outside closure, or a | ||
@@ -133,3 +133,3 @@ * a transitionable that manages a full transform (a sixteen value array). | ||
else if (transform && transform.get) this.outTransformMap = transform.get.bind(transform); | ||
else throw new Error('inTransformFrom takes only function or getter object'); | ||
else throw new Error('outTransformFrom takes only function or getter object'); | ||
//TODO: tween transition | ||
@@ -141,3 +141,3 @@ return this; | ||
* outOpacityFrom sets the accessor for the state of the opacity used in transitioning out renderables. | ||
* @method inOpacityFrom | ||
* @method outOpacityFrom | ||
* @param {Function|Transitionable} opacity A function that returns an opacity from outside closure, or a | ||
@@ -150,3 +150,3 @@ * a transitionable that manages opacity (a number between zero and one). | ||
else if (opacity && opacity.get) this.outOpacityMap = opacity.get.bind(opacity); | ||
else throw new Error('inOpacityFrom takes only function or getter object'); | ||
else throw new Error('outOpacityFrom takes only function or getter object'); | ||
//TODO: tween opacity | ||
@@ -158,3 +158,3 @@ return this; | ||
* outOriginFrom sets the accessor for the state of the origin used in transitioning out renderables. | ||
* @method inOriginFrom | ||
* @method outOriginFrom | ||
* @param {Function|Transitionable} origin A function that returns an origin from outside closure, or a | ||
@@ -167,3 +167,3 @@ * a transitionable that manages origin (a two value array of numbers between zero and one). | ||
else if (origin && origin.get) this.outOriginMap = origin.get.bind(origin); | ||
else throw new Error('inOriginFrom takes only function or getter object'); | ||
else throw new Error('outOriginFrom takes only function or getter object'); | ||
//TODO: tween origin | ||
@@ -170,0 +170,0 @@ return this; |
@@ -38,5 +38,12 @@ /* This Source Code Form is subject to the terms of the Mozilla Public | ||
EventHandler.setInputHandler(this, this.scrollview); | ||
EventHandler.setOutputHandler(this, this.scrollview); | ||
this.scrollview.subscribe(this.container); | ||
this._eventInput = new EventHandler(); | ||
EventHandler.setInputHandler(this, this._eventInput); | ||
this._eventInput.pipe(this.scrollview); | ||
this._eventOutput = new EventHandler(); | ||
EventHandler.setOutputHandler(this, this._eventOutput); | ||
this.container.pipe(this._eventOutput); | ||
this.scrollview.pipe(this._eventOutput); | ||
} | ||
@@ -48,3 +55,3 @@ | ||
}, | ||
scrollview: {direction: Utility.Direction.X} | ||
scrollview: {} | ||
}; | ||
@@ -73,2 +80,12 @@ | ||
/** | ||
* Returns the width and the height of the ScrollContainer instance. | ||
* | ||
* @method getSize | ||
* @return {Array} A two value array of the ScrollContainer instance's current width and height (in that order). | ||
*/ | ||
ScrollContainer.prototype.getSize = function getSize() { | ||
return this.container.getSize.apply(this.container, arguments); | ||
}; | ||
/** | ||
* Generate a render spec from the contents of this component. | ||
@@ -81,5 +98,5 @@ * | ||
ScrollContainer.prototype.render = function render() { | ||
return this.container.render.apply(this.container, arguments); | ||
return this.container.render(); | ||
}; | ||
module.exports = ScrollContainer; |
@@ -63,5 +63,7 @@ var Entity = require('../core/Entity'); | ||
var EDGE_TOLERANCE = 0; //slop for detecting passing the edge | ||
function _sizeForDir(size) { | ||
if (!size) size = this._contextSize; | ||
var dimension = (this.options.direction === Utility.Direction.X) ? 0 : 1; | ||
var dimension = this.options.direction; | ||
return (size[dimension] === undefined) ? this._contextSize[dimension] : size[dimension]; | ||
@@ -78,7 +80,21 @@ } | ||
function _getClipSize() { | ||
if (this.options.clipSize) return this.options.clipSize; | ||
else return _sizeForDir.call(this, this._contextSize); | ||
if (this.options.clipSize !== undefined) return this.options.clipSize; | ||
if (this._contextSize[this.options.direction] > this.getCumulativeSize()[this.options.direction]) { | ||
return _sizeForDir.call(this, this.getCumulativeSize()); | ||
} else { | ||
return _sizeForDir.call(this, this._contextSize); | ||
} | ||
} | ||
/** | ||
* Returns the cumulative size of the renderables in the view sequence | ||
* @method getCumulativeSize | ||
* @return {array} a two value array of the view sequence's cumulative size up to the index. | ||
*/ | ||
Scroller.prototype.getCumulativeSize = function(index) { | ||
if (index === undefined) index = this._node._.cumulativeSizes.length - 1; | ||
return this._node._.getSize(index); | ||
}; | ||
/** | ||
* Patches the Scroller instance's options with the passed-in ones. | ||
@@ -89,10 +105,9 @@ * @method setOptions | ||
Scroller.prototype.setOptions = function setOptions(options) { | ||
if (options.groupScroll !== this.options.groupScroll) { | ||
if (options.groupScroll) | ||
this.group.pipe(this._eventOutput); | ||
else | ||
this.group.unpipe(this._eventOutput); | ||
} | ||
this._optionsManager.setOptions(options); | ||
if (this.options.groupScroll) { | ||
this.group.pipe(this._eventOutput); | ||
} | ||
else { | ||
this.group.unpipe(this._eventOutput); | ||
} | ||
}; | ||
@@ -226,20 +241,2 @@ | ||
function _normalizeState() { | ||
var nodeSize = _sizeForDir.call(this, this._node.getSize()); | ||
var nextNode = this._node && this._node.getNext ? this._node.getNext() : null; | ||
while (nextNode && this._position + this._positionOffset >= nodeSize) { | ||
this._positionOffset -= nodeSize; | ||
this._node = nextNode; | ||
nodeSize = _sizeForDir.call(this, this._node.getSize()); | ||
nextNode = this._node && this._node.getNext ? this._node.getNext() : null; | ||
} | ||
var prevNode = this._node && this._node.getPrevious ? this._node.getPrevious() : null; | ||
while (prevNode && this._position + this._positionOffset < 0) { | ||
var prevNodeSize = _sizeForDir.call(this, prevNode.getSize()); | ||
this._positionOffset += prevNodeSize; | ||
this._node = prevNode; | ||
prevNode = this._node && this._node.getPrevious ? this._node.getPrevious() : null; | ||
} | ||
} | ||
function _innerRender() { | ||
@@ -250,4 +247,2 @@ var size = null; | ||
this._onEdge = 0; | ||
var offset = -this._positionOffset; | ||
@@ -275,16 +270,24 @@ var clipSize = _getClipSize.call(this); | ||
var edgeSize = (nodesSize !== undefined && nodesSize < clipSize) ? nodesSize : clipSize; | ||
if (!currNode && offset - position <= edgeSize) { | ||
this._onEdge = 1; | ||
this._eventOutput.emit('edgeHit', { | ||
position: offset - edgeSize | ||
}); | ||
if (!currNode && offset - position < clipSize - EDGE_TOLERANCE) { | ||
if (this._onEdge !== 1){ | ||
this._onEdge = 1; | ||
this._eventOutput.emit('onEdge', { | ||
position: offset - clipSize | ||
}); | ||
} | ||
} | ||
else if (!this._node.getPrevious() && position <= 0) { | ||
this._onEdge = -1; | ||
this._eventOutput.emit('edgeHit', { | ||
position: 0 | ||
}); | ||
else if (!this._node.getPrevious() && position < -EDGE_TOLERANCE) { | ||
if (this._onEdge !== -1) { | ||
this._onEdge = -1; | ||
this._eventOutput.emit('onEdge', { | ||
position: 0 | ||
}); | ||
} | ||
} | ||
else { | ||
if (this._onEdge !== 0){ | ||
this._onEdge = 0; | ||
this._eventOutput.emit('offEdge'); | ||
} | ||
} | ||
@@ -299,3 +302,3 @@ // backwards | ||
while (currNode && ((offset - position) > -(_getClipSize.call(this) + this.options.margin))) { | ||
while (currNode && ((offset - position) > -(clipSize + this.options.margin))) { | ||
_output.call(this, currNode, offset, result); | ||
@@ -309,3 +312,2 @@ currNode = currNode.getPrevious ? currNode.getPrevious() : null; | ||
_normalizeState.call(this); | ||
return result; | ||
@@ -312,0 +314,0 @@ } |
@@ -34,2 +34,9 @@ /* This Source Code Form is subject to the terms of the Mozilla Public | ||
/** @enum */ | ||
var EdgeStates = { | ||
TOP: -1, | ||
NONE: 0, | ||
BOTTOM: 1 | ||
}; | ||
/** | ||
@@ -69,7 +76,19 @@ * Scrollview will lay out a collection of renderables sequentially in the specified direction, and will | ||
function Scrollview(options) { | ||
// patch options with defaults | ||
this.options = Object.create(Scrollview.DEFAULT_OPTIONS); | ||
this._optionsManager = new OptionsManager(this.options); | ||
this._node = null; | ||
// create sub-components | ||
this._scroller = new Scroller(this.options); | ||
this.sync = new GenericSync( | ||
['scroll', 'touch'], | ||
{ | ||
direction : this.options.direction, | ||
scale : this.options.syncScale, | ||
rails: this.options.rails, | ||
preventDefault: this.options.preventDefault | ||
} | ||
); | ||
this._physicsEngine = new PhysicsEngine(); | ||
@@ -79,9 +98,34 @@ this._particle = new Particle(); | ||
this.spring = new Spring({anchor: [0, 0, 0]}); | ||
this.spring = new Spring({ | ||
anchor: [0, 0, 0], | ||
period: this.options.edgePeriod, | ||
dampingRatio: this.options.edgeDamp | ||
}); | ||
this.drag = new Drag({ | ||
forceFunction: Drag.FORCE_FUNCTIONS.QUADRATIC, | ||
strength: this.options.drag | ||
}); | ||
this.friction = new Drag({ | ||
forceFunction: Drag.FORCE_FUNCTIONS.LINEAR, | ||
strength: this.options.friction | ||
}); | ||
this.drag = new Drag({forceFunction: Drag.FORCE_FUNCTIONS.QUADRATIC}); | ||
this.friction = new Drag({forceFunction: Drag.FORCE_FUNCTIONS.LINEAR}); | ||
// state | ||
this._node = null; | ||
this._touchCount = 0; | ||
this._springState = SpringStates.NONE; | ||
this._onEdge = EdgeStates.NONE; | ||
this._pageSpringPosition = 0; | ||
this._edgeSpringPosition = 0; | ||
this._touchVelocity = 0; | ||
this._earlyEnd = false; | ||
this._needsPaginationCheck = false; | ||
this._displacement = 0; | ||
this._totalShift = 0; | ||
this._cachedIndex = 0; | ||
this.sync = new GenericSync(['scroll', 'touch'], {direction : this.options.direction}); | ||
// subcomponent logic | ||
this._scroller.positionFrom(this.getPosition.bind(this)); | ||
// eventing | ||
this._eventInput = new EventHandler(); | ||
@@ -96,17 +140,6 @@ this._eventOutput = new EventHandler(); | ||
this._touchCount = 0; | ||
this._springState = SpringStates.NONE; | ||
this._onEdge = 0; // -1 for top, 1 for bottom | ||
this._pageSpringPosition = 0; | ||
this._edgeSpringPosition = 0; | ||
this._touchVelocity = undefined; | ||
this._earlyEnd = false; | ||
this._needsPaginationCheck = false; | ||
_bindEvents.call(this); | ||
this._scroller = new Scroller(); | ||
this._scroller.positionFrom(this.getPosition.bind(this)); | ||
this.setOptions(options); | ||
_bindEvents.call(this); | ||
// override default options with passed-in custom options | ||
if (options) this.setOptions(options); | ||
} | ||
@@ -117,5 +150,5 @@ | ||
rails: true, | ||
friction: 0.001, | ||
friction: 0.005, | ||
drag: 0.0001, | ||
edgeGrip: 0.5, | ||
edgeGrip: 0.2, | ||
edgePeriod: 300, | ||
@@ -129,4 +162,5 @@ edgeDamp: 1, | ||
pageSwitchSpeed: 0.5, | ||
speedLimit: 10, | ||
groupScroll: false | ||
speedLimit: 5, | ||
groupScroll: false, | ||
syncScale: 1 | ||
}; | ||
@@ -139,2 +173,3 @@ | ||
_detachAgents.call(this); | ||
this.setVelocity(0); | ||
@@ -149,4 +184,4 @@ this._touchVelocity = 0; | ||
if (this._onEdge && event.slip) { | ||
if ((velocity < 0 && this._onEdge < 0) || (velocity > 0 && this._onEdge > 0)) { | ||
if (this._onEdge !== EdgeStates.NONE && event.slip) { | ||
if ((velocity < 0 && this._onEdge === EdgeStates.TOP) || (velocity > 0 && this._onEdge === EdgeStates.BOTTOM)) { | ||
if (!this._earlyEnd) { | ||
@@ -164,4 +199,18 @@ _handleEnd.call(this, event); | ||
if (event.slip) this.setVelocity(velocity); | ||
else this.setPosition(this.getPosition() + delta); | ||
if (event.slip) { | ||
var speedLimit = this.options.speedLimit; | ||
if (velocity < -speedLimit) velocity = -speedLimit; | ||
else if (velocity > speedLimit) velocity = speedLimit; | ||
this.setVelocity(velocity); | ||
var deltaLimit = speedLimit * 16; | ||
if (delta > deltaLimit) delta = deltaLimit; | ||
else if (delta < -deltaLimit) delta = -deltaLimit; | ||
} | ||
this.setPosition(this.getPosition() + delta); | ||
this._displacement += delta; | ||
if (this._springState === SpringStates.NONE) _normalizeState.call(this); | ||
} | ||
@@ -173,3 +222,3 @@ | ||
_detachAgents.call(this); | ||
if (this._onEdge) _setSpring.call(this, this._edgeSpringPosition, SpringStates.EDGE); | ||
if (this._onEdge !== EdgeStates.NONE) _setSpring.call(this, this._edgeSpringPosition, SpringStates.EDGE); | ||
_attachAgents.call(this); | ||
@@ -182,3 +231,3 @@ var velocity = -event.velocity; | ||
this.setVelocity(velocity); | ||
this._touchVelocity = undefined; | ||
this._touchVelocity = 0; | ||
this._needsPaginationCheck = true; | ||
@@ -194,5 +243,27 @@ } | ||
this._scroller.on('edgeHit', function(data) { | ||
this._eventInput.on('resize', function() { | ||
this._node._.calculateSize(); | ||
}.bind(this)); | ||
this._scroller.on('onEdge', function(data) { | ||
this._edgeSpringPosition = data.position; | ||
_handleEdge.call(this, this._scroller.onEdge()); | ||
this._eventOutput.emit('onEdge'); | ||
}.bind(this)); | ||
this._scroller.on('offEdge', function() { | ||
this.sync.setOptions({scale: this.options.syncScale}); | ||
this._onEdge = this._scroller.onEdge(); | ||
this._eventOutput.emit('offEdge'); | ||
}.bind(this)); | ||
this._particle.on('update', function(particle) { | ||
if (this._springState === SpringStates.NONE) _normalizeState.call(this); | ||
this._displacement = particle.position.x - this._totalShift; | ||
}.bind(this)); | ||
this._particle.on('end', function() { | ||
if (!this.options.paginated || (this.options.paginated && this._springState !== SpringStates.NONE)) | ||
this._eventOutput.emit('settle'); | ||
}.bind(this)); | ||
} | ||
@@ -212,28 +283,22 @@ | ||
var direction = this.options.direction; | ||
var nodeSize = (node.getSize() || this._scroller.getSize())[direction]; | ||
if (!nodeSize) nodeSize = this._scroller.getSize()[direction]; | ||
return nodeSize; | ||
var nodeSize = node.getSize(); | ||
return (!nodeSize) ? this._scroller.getSize()[direction] : nodeSize[direction]; | ||
} | ||
function _handleEdge(edgeDetected) { | ||
if (!this._onEdge && edgeDetected) { | ||
this.sync.setOptions({scale: this.options.edgeGrip}); | ||
if (!this._touchCount && this._springState !== SpringStates.EDGE) { | ||
_setSpring.call(this, this._edgeSpringPosition, SpringStates.EDGE); | ||
} | ||
function _handleEdge(edge) { | ||
this.sync.setOptions({scale: this.options.edgeGrip}); | ||
this._onEdge = edge; | ||
if (!this._touchCount && this._springState !== SpringStates.EDGE) { | ||
_setSpring.call(this, this._edgeSpringPosition, SpringStates.EDGE); | ||
} | ||
else if (this._onEdge && !edgeDetected) { | ||
this.sync.setOptions({scale: 1}); | ||
if (this._springState && Math.abs(this.getVelocity()) < 0.001) { | ||
// reset agents, detaching the spring | ||
_detachAgents.call(this); | ||
_attachAgents.call(this); | ||
} | ||
if (this._springState && Math.abs(this.getVelocity()) < 0.001) { | ||
// reset agents, detaching the spring | ||
_detachAgents.call(this); | ||
_attachAgents.call(this); | ||
} | ||
this._onEdge = edgeDetected; | ||
} | ||
function _handlePagination() { | ||
if (!this._needsPaginationCheck) return; | ||
if (this._touchCount) return; | ||
@@ -251,8 +316,16 @@ if (this._springState === SpringStates.EDGE) return; | ||
var positionNext = position > 0.5 * nodeSize; | ||
var positionPrev = position < 0.5 * nodeSize; | ||
var velocityNext = velocity > 0; | ||
var velocityPrev = velocity < 0; | ||
if ((positionNext && !velocitySwitch) || (velocitySwitch && velocityNext)) this.goToNextPage(); | ||
this._needsPaginationCheck = false; | ||
if ((positionNext && !velocitySwitch) || (velocitySwitch && velocityNext)) { | ||
this.goToNextPage(); | ||
} | ||
else if (velocitySwitch && velocityPrev) { | ||
this.goToPreviousPage(); | ||
} | ||
else _setSpring.call(this, 0, SpringStates.PAGE); | ||
this._needsPaginationCheck = false; | ||
} | ||
@@ -289,9 +362,12 @@ | ||
function _normalizeState() { | ||
var offset = 0; | ||
var position = this.getPosition(); | ||
position += (position < 0 ? -0.5 : 0.5) >> 0; | ||
var nodeSize = _nodeSizeForDirection.call(this, this._node); | ||
var nextNode = this._node.getNext(); | ||
while (position > nodeSize + TOLERANCE && nextNode) { | ||
_shiftOrigin.call(this, -nodeSize); | ||
position -= nodeSize; | ||
while (offset + position >= nodeSize && nextNode) { | ||
offset -= nodeSize; | ||
this._scroller.sequenceFrom(nextNode); | ||
@@ -306,10 +382,25 @@ this._node = nextNode; | ||
while (position < -TOLERANCE && previousNode) { | ||
while (offset + position <= 0 && previousNode) { | ||
previousNodeSize = _nodeSizeForDirection.call(this, previousNode); | ||
this._scroller.sequenceFrom(previousNode); | ||
this._node = previousNode; | ||
_shiftOrigin.call(this, previousNodeSize); | ||
position += previousNodeSize; | ||
offset += previousNodeSize; | ||
previousNode = this._node.getPrevious(); | ||
} | ||
if (offset) _shiftOrigin.call(this, offset); | ||
if (this._node) { | ||
if (this._node.index !== this._cachedIndex) { | ||
if (this.getPosition() < 0.5 * nodeSize) { | ||
this._cachedIndex = this._node.index; | ||
this._eventOutput.emit('pageChange', {direction: -1, index: this._cachedIndex}); | ||
} | ||
} else { | ||
if (this.getPosition() > 0.5 * nodeSize) { | ||
this._cachedIndex = this._node.index + 1; | ||
this._eventOutput.emit('pageChange', {direction: 1, index: this._cachedIndex}); | ||
} | ||
} | ||
} | ||
} | ||
@@ -321,2 +412,4 @@ | ||
this.setPosition(this.getPosition() + amount); | ||
this._totalShift += amount; | ||
if (this._springState === SpringStates.EDGE) { | ||
@@ -330,2 +423,78 @@ this.spring.setOptions({anchor: [this._edgeSpringPosition, 0, 0]}); | ||
/** | ||
* Returns the index of the first visible renderable | ||
* | ||
* @method getCurrentIndex | ||
* @return {Number} The current index of the ViewSequence | ||
*/ | ||
Scrollview.prototype.getCurrentIndex = function getCurrentIndex() { | ||
return this._node.index; | ||
}; | ||
/** | ||
* goToPreviousPage paginates your Scrollview instance backwards by one item. | ||
* | ||
* @method goToPreviousPage | ||
* @return {ViewSequence} The previous node. | ||
*/ | ||
Scrollview.prototype.goToPreviousPage = function goToPreviousPage() { | ||
if (!this._node || this._onEdge === EdgeStates.TOP) return null; | ||
// if moving back to the current node | ||
if (this.getPosition() > 1 && this._springState === SpringStates.NONE) { | ||
_setSpring.call(this, 0, SpringStates.PAGE); | ||
return this._node; | ||
} | ||
// if moving to the previous node | ||
var previousNode = this._node.getPrevious(); | ||
if (previousNode) { | ||
var previousNodeSize = _nodeSizeForDirection.call(this, previousNode); | ||
this._scroller.sequenceFrom(previousNode); | ||
this._node = previousNode; | ||
_shiftOrigin.call(this, previousNodeSize); | ||
_setSpring.call(this, 0, SpringStates.PAGE); | ||
} | ||
return previousNode; | ||
}; | ||
/** | ||
* goToNextPage paginates your Scrollview instance forwards by one item. | ||
* | ||
* @method goToNextPage | ||
* @return {ViewSequence} The next node. | ||
*/ | ||
Scrollview.prototype.goToNextPage = function goToNextPage() { | ||
if (!this._node || this._onEdge === EdgeStates.BOTTOM) return null; | ||
var nextNode = this._node.getNext(); | ||
if (nextNode) { | ||
var currentNodeSize = _nodeSizeForDirection.call(this, this._node); | ||
this._scroller.sequenceFrom(nextNode); | ||
this._node = nextNode; | ||
_shiftOrigin.call(this, -currentNodeSize); | ||
_setSpring.call(this, 0, SpringStates.PAGE); | ||
} | ||
return nextNode; | ||
}; | ||
/** | ||
* Paginates the Scrollview to an absolute page index. | ||
* | ||
* @method goToPage | ||
*/ | ||
Scrollview.prototype.goToPage = function goToPage(index) { | ||
var currentIndex = this.getCurrentIndex(); | ||
var i; | ||
if (currentIndex > index) { | ||
for (i = 0; i < currentIndex - index; i++) | ||
this.goToPreviousPage(); | ||
} | ||
if (currentIndex < index) { | ||
for (i = 0; i < index - currentIndex; i++) | ||
this.goToNextPage(); | ||
} | ||
}; | ||
Scrollview.prototype.outputFrom = function outputFrom() { | ||
@@ -338,2 +507,4 @@ return this._scroller.outputFrom.apply(this._scroller, arguments); | ||
* (generally the node currently at the top). | ||
* | ||
* @deprecated | ||
* @method getPosition | ||
@@ -350,3 +521,28 @@ * @param {number} [node] If specified, returns the position of the node at that index in the | ||
/** | ||
* Sets position of the physics particle that controls Scrollview instance's "position" | ||
* Returns the absolute position associated with the Scrollview instance | ||
* | ||
* @method getAbsolutePosition | ||
* @return {number} The position of the Scrollview's current Node, | ||
* in pixels translated. | ||
*/ | ||
Scrollview.prototype.getAbsolutePosition = function getAbsolutePosition() { | ||
return this._scroller.getCumulativeSize(this.getCurrentIndex())[this.options.direction] + this.getPosition(); | ||
}; | ||
/** | ||
* Returns the offset associated with the Scrollview instance's current node | ||
* (generally the node currently at the top). | ||
* | ||
* @method getOffset | ||
* @param {number} [node] If specified, returns the position of the node at that index in the | ||
* Scrollview instance's currently managed collection. | ||
* @return {number} The position of either the specified node, or the Scrollview's current Node, | ||
* in pixels translated. | ||
*/ | ||
Scrollview.prototype.getOffset = Scrollview.prototype.getPosition; | ||
/** | ||
* Sets the position of the physics particle that controls Scrollview instance's "position" | ||
* | ||
* @deprecated | ||
* @method setPosition | ||
@@ -360,3 +556,12 @@ * @param {number} x The amount of pixels you want your scrollview to progress by. | ||
/** | ||
* Sets the offset of the physics particle that controls Scrollview instance's "position" | ||
* | ||
* @method setPosition | ||
* @param {number} x The amount of pixels you want your scrollview to progress by. | ||
*/ | ||
Scrollview.prototype.setOffset = Scrollview.prototype.setPosition; | ||
/** | ||
* Returns the Scrollview instance's velocity. | ||
* | ||
* @method getVelocity | ||
@@ -373,2 +578,3 @@ * @return {Number} The velocity. | ||
* the Scrollview instance will scroll at the passed-in velocity. | ||
* | ||
* @method setVelocity | ||
@@ -383,2 +589,3 @@ * @param {number} v The magnitude of the velocity. | ||
* Patches the Scrollview instance's options with the passed-in ones. | ||
* | ||
* @method setOptions | ||
@@ -388,73 +595,42 @@ * @param {Options} options An object of configurable options for the Scrollview instance. | ||
Scrollview.prototype.setOptions = function setOptions(options) { | ||
if (options) { | ||
if (options.direction !== undefined) { | ||
if (options.direction === 'x') options.direction = Utility.Direction.X; | ||
else if (options.direction === 'y') options.direction = Utility.Direction.Y; | ||
} | ||
// preprocess custom options | ||
if (options.direction !== undefined) { | ||
if (options.direction === 'x') options.direction = Utility.Direction.X; | ||
else if (options.direction === 'y') options.direction = Utility.Direction.Y; | ||
} | ||
this._scroller.setOptions(options); | ||
this._optionsManager.setOptions(options); | ||
if (options.groupScroll !== this.options.groupScroll) { | ||
if (options.groupScroll) | ||
this.subscribe(this._scroller); | ||
else | ||
this.unsubscribe(this._scroller); | ||
} | ||
this._scroller.setOptions(this.options); | ||
if (this.options.groupScroll) | ||
this._scroller.pipe(this._eventInput); | ||
else | ||
this._scroller.unpipe(this._eventInput); | ||
// patch custom options | ||
this._optionsManager.setOptions(options); | ||
this.drag.setOptions({strength: this.options.drag}); | ||
this.friction.setOptions({strength: this.options.friction}); | ||
// propagate options to sub-components | ||
this.spring.setOptions({ | ||
period: this.options.edgePeriod, | ||
dampingRatio: this.options.edgeDamp | ||
}); | ||
// scroller sub-component | ||
this._scroller.setOptions(options); | ||
this.sync.setOptions({ | ||
rails: this.options.rails, | ||
direction: (this.options.direction === Utility.Direction.X) ? GenericSync.DIRECTION_X : GenericSync.DIRECTION_Y | ||
}); | ||
}; | ||
/** | ||
* goToPreviousPage paginates your Scrollview instance backwards by one item. | ||
* @method goToPreviousPage | ||
* @return {ViewSequence} The previous node. | ||
*/ | ||
Scrollview.prototype.goToPreviousPage = function goToPreviousPage() { | ||
if (!this._node) return null; | ||
var previousNode = this._node.getPrevious(); | ||
if (previousNode) { | ||
var currentPosition = this.getPosition(); | ||
var previousNodeSize = _nodeSizeForDirection.call(this, previousNode); | ||
this._scroller.sequenceFrom(previousNode); | ||
this._node = previousNode; | ||
var previousSpringPosition = (currentPosition < TOLERANCE) ? -previousNodeSize : 0; | ||
_setSpring.call(this, previousSpringPosition, SpringStates.PAGE); | ||
_shiftOrigin.call(this, previousNodeSize); | ||
// physics sub-components | ||
if (options.drag !== undefined) this.drag.setOptions({strength: this.options.drag}); | ||
if (options.friction !== undefined) this.friction.setOptions({strength: this.options.friction}); | ||
if (options.edgePeriod !== undefined || options.edgeDamp !== undefined) { | ||
this.spring.setOptions({ | ||
period: this.options.edgePeriod, | ||
dampingRatio: this.options.edgeDamp | ||
}); | ||
} | ||
this._eventOutput.emit('pageChange', {direction: -1}); | ||
return previousNode; | ||
}; | ||
/** | ||
* goToNextPage paginates your Scrollview instance forwards by one item. | ||
* @method goToNextPage | ||
* @return {ViewSequence} The next node. | ||
*/ | ||
Scrollview.prototype.goToNextPage = function goToNextPage() { | ||
if (!this._node) return null; | ||
var nextNode = this._node.getNext(); | ||
if (nextNode) { | ||
var currentPosition = this.getPosition(); | ||
var currentNodeSize = _nodeSizeForDirection.call(this, this._node); | ||
var nextNodeSize = _nodeSizeForDirection.call(this, nextNode); | ||
this._scroller.sequenceFrom(nextNode); | ||
this._node = nextNode; | ||
var nextSpringPosition = (currentPosition > currentNodeSize - TOLERANCE) ? currentNodeSize + nextNodeSize : currentNodeSize; | ||
_setSpring.call(this, nextSpringPosition, SpringStates.PAGE); | ||
_shiftOrigin.call(this, -currentNodeSize); | ||
// sync sub-component | ||
if (options.rails || options.direction !== undefined || options.syncScale !== undefined || options.preventDefault) { | ||
this.sync.setOptions({ | ||
rails: this.options.rails, | ||
direction: (this.options.direction === Utility.Direction.X) ? GenericSync.DIRECTION_X : GenericSync.DIRECTION_Y, | ||
scale: this.options.syncScale, | ||
preventDefault: this.options.preventDefault | ||
}); | ||
} | ||
this._eventOutput.emit('pageChange', {direction: 1}); | ||
return nextNode; | ||
}; | ||
@@ -472,3 +648,3 @@ | ||
Scrollview.prototype.sequenceFrom = function sequenceFrom(node) { | ||
if (node instanceof Array) node = new ViewSequence({array: node}); | ||
if (node instanceof Array) node = new ViewSequence({array: node, trackSize: true}); | ||
this._node = node; | ||
@@ -496,8 +672,4 @@ return this._scroller.sequenceFrom(node); | ||
Scrollview.prototype.render = function render() { | ||
if (!this._node) return null; | ||
if (this.options.paginated && this._needsPaginationCheck) _handlePagination.call(this); | ||
_normalizeState.call(this); | ||
_handleEdge.call(this, this._scroller.onEdge()); | ||
if (this.options.paginated) _handlePagination.call(this); | ||
return this._scroller.render(); | ||
@@ -504,0 +676,0 @@ }; |
@@ -24,5 +24,2 @@ /* This Source Code Form is subject to the terms of the Mozilla Public | ||
* to just use integers as well. | ||
* @param {Array.Number} [options.defaultItemSize=[50, 50]] In the case where a renderable layed out | ||
* under SequentialLayout's control doesen't have a getSize method, SequentialLayout will assign it | ||
* this default size. (Commonly a case with Views). | ||
*/ | ||
@@ -37,8 +34,2 @@ function SequentialLayout(options) { | ||
this._itemsCache = []; | ||
this._outputCache = { | ||
size: null, | ||
target: this._itemsCache | ||
}; | ||
if (options) this.setOptions(options); | ||
@@ -48,5 +39,3 @@ } | ||
SequentialLayout.DEFAULT_OPTIONS = { | ||
direction: Utility.Direction.Y, | ||
itemSpacing: 0, | ||
defaultItemSize: [50, 50] | ||
direction: Utility.Direction.Y | ||
}; | ||
@@ -120,39 +109,35 @@ | ||
SequentialLayout.prototype.render = function render() { | ||
var length = 0; | ||
var girth = 0; | ||
var length = 0; | ||
var secondaryDirection = this.options.direction ^ 1; | ||
var currentNode = this._items; | ||
var item = null; | ||
var itemSize = []; | ||
var output = {}; | ||
var result = []; | ||
var i = 0; | ||
var lengthDim = (this.options.direction === Utility.Direction.X) ? 0 : 1; | ||
var girthDim = (this.options.direction === Utility.Direction.X) ? 1 : 0; | ||
this._size = [0, 0]; | ||
var currentNode = this._items; | ||
var result = this._itemsCache; | ||
var i = 0; | ||
while (currentNode) { | ||
var item = currentNode.get(); | ||
item = currentNode.get(); | ||
if (!item) break; | ||
var itemSize; | ||
if (item && item.getSize) itemSize = item.getSize(); | ||
if (!itemSize) itemSize = this.options.defaultItemSize; | ||
if (itemSize[girthDim] !== true) girth = Math.max(girth, itemSize[girthDim]); | ||
if (item.getSize) itemSize = item.getSize(); | ||
var output = this._outputFunction.call(this, item, length, i); | ||
result[i] = output; | ||
output = this._outputFunction.call(this, item, length, i++); | ||
result.push(output); | ||
if (itemSize[lengthDim] && (itemSize[lengthDim] !== true)) length += itemSize[lengthDim] + this.options.itemSpacing; | ||
if (itemSize) { | ||
if (itemSize[this.options.direction]) length += itemSize[this.options.direction]; | ||
if (itemSize[secondaryDirection] > this._size[secondaryDirection]) this._size[secondaryDirection] = itemSize[secondaryDirection]; | ||
} | ||
currentNode = currentNode.getNext(); | ||
i++; | ||
} | ||
this._itemsCache.splice(i); | ||
if (!girth) girth = undefined; | ||
this._size[this.options.direction] = length; | ||
if (!this._size) this._size = [0, 0]; | ||
this._size[lengthDim] = length - this.options.itemSpacing; // account for last itemSpacing | ||
this._size[girthDim] = girth; | ||
this._outputCache.size = this.getSize(); | ||
return this._outputCache; | ||
return result; | ||
}; | ||
module.exports = SequentialLayout; |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
571837
16339
91