aboutsummaryrefslogtreecommitdiffstats
path: root/src/animation
diff options
context:
space:
mode:
Diffstat (limited to 'src/animation')
-rw-r--r--src/animation/Controller.js7
-rw-r--r--src/animation/Morphable.js85
-rw-r--r--src/animation/Queue.js45
-rw-r--r--src/animation/Runner.js416
-rw-r--r--src/animation/Timeline.js179
5 files changed, 368 insertions, 364 deletions
diff --git a/src/animation/Controller.js b/src/animation/Controller.js
index 972679e..ae49de9 100644
--- a/src/animation/Controller.js
+++ b/src/animation/Controller.js
@@ -128,13 +128,14 @@ export class Controller extends Stepper {
this.stepper = fn
}
+ done (c) {
+ return c.done
+ }
+
step (current, target, dt, c) {
return this.stepper(current, target, dt, c)
}
- done (c) {
- return c.done
- }
}
function recalculate () {
diff --git a/src/animation/Morphable.js b/src/animation/Morphable.js
index 2d48e10..46dd166 100644
--- a/src/animation/Morphable.js
+++ b/src/animation/Morphable.js
@@ -49,6 +49,25 @@ export default class Morphable {
this._morphObj = null
}
+ at (pos) {
+ var _this = this
+
+ return this._morphObj.fromArray(
+ this._from.map(function (i, index) {
+ return _this._stepper.step(i, _this._to[index], pos, _this._context[index], _this._context)
+ })
+ )
+ }
+
+ done () {
+ var complete = this._context
+ .map(this._stepper.done)
+ .reduce(function (last, curr) {
+ return last && curr
+ }, true)
+ return complete
+ }
+
from (val) {
if (val == null) {
return this._from
@@ -58,6 +77,12 @@ export default class Morphable {
return this
}
+ stepper (stepper) {
+ if (stepper == null) return this._stepper
+ this._stepper = stepper
+ return this
+ }
+
to (val) {
if (val == null) {
return this._to
@@ -109,30 +134,6 @@ export default class Morphable {
return result
}
- stepper (stepper) {
- if (stepper == null) return this._stepper
- this._stepper = stepper
- return this
- }
-
- done () {
- var complete = this._context
- .map(this._stepper.done)
- .reduce(function (last, curr) {
- return last && curr
- }, true)
- return complete
- }
-
- at (pos) {
- var _this = this
-
- return this._morphObj.fromArray(
- this._from.map(function (i, index) {
- return _this._stepper.step(i, _this._to[index], pos, _this._context[index], _this._context)
- })
- )
- }
}
export class NonMorphable {
@@ -146,13 +147,14 @@ export class NonMorphable {
return this
}
+ toArray () {
+ return [ this.value ]
+ }
+
valueOf () {
return this.value
}
- toArray () {
- return [ this.value ]
- }
}
export class TransformBag {
@@ -214,6 +216,17 @@ export class ObjectBag {
this.init(...args)
}
+ align (other) {
+ for (let i = 0, il = this.values.length; i < il; ++i) {
+ if (this.values[i] === Color) {
+ const space = other[i + 6]
+ const color = new Color(this.values.splice(i + 2, 5))[space]().toArray()
+ this.values.splice(i + 2, 0, ...color)
+ }
+ }
+ return this
+ }
+
init (objOrArr) {
this.values = []
@@ -237,6 +250,10 @@ export class ObjectBag {
return this
}
+ toArray () {
+ return this.values
+ }
+
valueOf () {
var obj = {}
var arr = this.values
@@ -253,20 +270,6 @@ export class ObjectBag {
return obj
}
- toArray () {
- return this.values
- }
-
- align (other) {
- for (let i = 0, il = this.values.length; i < il; ++i) {
- if (this.values[i] === Color) {
- const space = other[i + 6]
- const color = new Color(this.values.splice(i + 2, 5))[space]().toArray()
- this.values.splice(i + 2, 0, ...color)
- }
- }
- return this
- }
}
const morphableTypes = [
diff --git a/src/animation/Queue.js b/src/animation/Queue.js
index 1858b99..e01c3d6 100644
--- a/src/animation/Queue.js
+++ b/src/animation/Queue.js
@@ -4,6 +4,16 @@ export default class Queue {
this._last = null
}
+ // Shows us the first item in the list
+ first () {
+ return this._first && this._first.value
+ }
+
+ // Shows us the last item in the list
+ last () {
+ return this._last && this._last.value
+ }
+
push (value) {
// An item stores an id and the provided value
var item = typeof value.next !== 'undefined' ? value : { value: value, next: null, prev: null }
@@ -22,28 +32,6 @@ export default class Queue {
return item
}
- shift () {
- // Check if we have a value
- var remove = this._first
- if (!remove) return null
-
- // If we do, remove it and relink things
- this._first = remove.next
- if (this._first) this._first.prev = null
- this._last = this._first ? this._last : null
- return remove.value
- }
-
- // Shows us the first item in the list
- first () {
- return this._first && this._first.value
- }
-
- // Shows us the last item in the list
- last () {
- return this._last && this._last.value
- }
-
// Removes the item that was returned from the push
remove (item) {
// Relink the previous item
@@ -56,4 +44,17 @@ export default class Queue {
item.prev = null
item.next = null
}
+
+ shift () {
+ // Check if we have a value
+ var remove = this._first
+ if (!remove) return null
+
+ // If we do, remove it and relink things
+ this._first = remove.next
+ if (this._first) this._first.prev = null
+ this._last = this._first ? this._last : null
+ return remove.value
+ }
+
}
diff --git a/src/animation/Runner.js b/src/animation/Runner.js
index e0ac5a8..bd60915 100644
--- a/src/animation/Runner.js
+++ b/src/animation/Runner.js
@@ -71,27 +71,55 @@ export default class Runner extends EventTarget {
this._persist = this._isDeclarative ? true : null
}
- /*
- Runner Definitions
- ==================
- These methods help us define the runtime behaviour of the Runner or they
- help us make new runners from the current runner
- */
+ static sanitise (duration, delay, when) {
+ // Initialise the default parameters
+ var times = 1
+ var swing = false
+ var wait = 0
+ duration = duration || timeline.duration
+ delay = delay || timeline.delay
+ when = when || 'last'
- element (element) {
- if (element == null) return this._element
- this._element = element
- element._prepareRunner()
+ // If we have an object, unpack the values
+ if (typeof duration === 'object' && !(duration instanceof Stepper)) {
+ delay = duration.delay || delay
+ when = duration.when || when
+ swing = duration.swing || swing
+ times = duration.times || times
+ wait = duration.wait || wait
+ duration = duration.duration || timeline.duration
+ }
+
+ return {
+ duration: duration,
+ delay: delay,
+ swing: swing,
+ times: times,
+ wait: wait,
+ when: when
+ }
+ }
+
+ active (enabled) {
+ if (enabled == null) return this.enabled
+ this.enabled = enabled
return this
}
- timeline (timeline) {
- // check explicitly for undefined so we can set the timeline to null
- if (typeof timeline === 'undefined') return this._timeline
- this._timeline = timeline
+ /*
+ Private Methods
+ ===============
+ Methods that shouldn't be used externally
+ */
+ addTransform (transform, index) {
+ this.transforms.lmultiplyO(transform)
return this
}
+ after (fn) {
+ return this.on('finished', fn)
+ }
+
animate (duration, delay, when) {
var o = Runner.sanitise(duration, delay, when)
var runner = new Runner(o.duration)
@@ -100,30 +128,54 @@ export default class Runner extends EventTarget {
return runner.loop(o).schedule(o.delay, o.when)
}
- schedule (timeline, delay, when) {
- // The user doesn't need to pass a timeline if we already have one
- if (!(timeline instanceof Timeline)) {
- when = delay
- delay = timeline
- timeline = this.timeline()
- }
+ clearTransform () {
+ this.transforms = new Matrix()
+ return this
+ }
- // If there is no timeline, yell at the user...
- if (!timeline) {
- throw Error('Runner cannot be scheduled without timeline')
+ // TODO: Keep track of all transformations so that deletion is faster
+ clearTransformsFromQueue () {
+ if (!this.done || !this._timeline || !this._timeline._runnerIds.includes(this.id)) {
+ this._queue = this._queue.filter((item) => {
+ return !item.isTransform
+ })
}
+ }
- // Schedule the runner on the timeline provided
- timeline.schedule(this, delay, when)
+ delay (delay) {
+ return this.animate(0, delay)
+ }
+
+ duration () {
+ return this._times * (this._wait + this._duration) - this._wait
+ }
+
+ during (fn) {
+ return this.queue(null, fn)
+ }
+
+ ease (fn) {
+ this._stepper = new Ease(fn)
return this
}
+ /*
+ Runner Definitions
+ ==================
+ These methods help us define the runtime behaviour of the Runner or they
+ help us make new runners from the current runner
+ */
- unschedule () {
- var timeline = this.timeline()
- timeline && timeline.unschedule(this)
+ element (element) {
+ if (element == null) return this._element
+ this._element = element
+ element._prepareRunner()
return this
}
+ finish () {
+ return this.step(Infinity)
+ }
+
loop (times, swing, wait) {
// Deal with the user passing in an object
if (typeof times === 'object') {
@@ -143,57 +195,6 @@ export default class Runner extends EventTarget {
return this
}
- delay (delay) {
- return this.animate(0, delay)
- }
-
- /*
- Basic Functionality
- ===================
- These methods allow us to attach basic functions to the runner directly
- */
-
- queue (initFn, runFn, retargetFn, isTransform) {
- this._queue.push({
- initialiser: initFn || noop,
- runner: runFn || noop,
- retarget: retargetFn,
- isTransform: isTransform,
- initialised: false,
- finished: false
- })
- var timeline = this.timeline()
- timeline && this.timeline()._continue()
- return this
- }
-
- during (fn) {
- return this.queue(null, fn)
- }
-
- after (fn) {
- return this.on('finished', fn)
- }
-
- /*
- Runner animation methods
- ========================
- Control how the animation plays
- */
-
- time (time) {
- if (time == null) {
- return this._time
- }
- const dt = time - this._time
- this.step(dt)
- return this
- }
-
- duration () {
- return this._times * (this._wait + this._duration) - this._wait
- }
-
loops (p) {
var loopDuration = this._duration + this._wait
if (p == null) {
@@ -264,6 +265,55 @@ export default class Runner extends EventTarget {
return this.time(p * this.duration())
}
+ /*
+ Basic Functionality
+ ===================
+ These methods allow us to attach basic functions to the runner directly
+ */
+ queue (initFn, runFn, retargetFn, isTransform) {
+ this._queue.push({
+ initialiser: initFn || noop,
+ runner: runFn || noop,
+ retarget: retargetFn,
+ isTransform: isTransform,
+ initialised: false,
+ finished: false
+ })
+ var timeline = this.timeline()
+ timeline && this.timeline()._continue()
+ return this
+ }
+
+ reset () {
+ if (this._reseted) return this
+ this.time(0)
+ this._reseted = true
+ return this
+ }
+
+ reverse (reverse) {
+ this._reverse = reverse == null ? !this._reverse : reverse
+ return this
+ }
+
+ schedule (timeline, delay, when) {
+ // The user doesn't need to pass a timeline if we already have one
+ if (!(timeline instanceof Timeline)) {
+ when = delay
+ delay = timeline
+ timeline = this.timeline()
+ }
+
+ // If there is no timeline, yell at the user...
+ if (!timeline) {
+ throw Error('Runner cannot be scheduled without timeline')
+ }
+
+ // Schedule the runner on the timeline provided
+ timeline.schedule(this, delay, when)
+ return this
+ }
+
step (dt) {
// If we are inactive, this stepper just gets skipped
if (!this.enabled) return this
@@ -315,38 +365,54 @@ export default class Runner extends EventTarget {
return this
}
- reset () {
- if (this._reseted) return this
- this.time(0)
- this._reseted = true
+ /*
+ Runner animation methods
+ ========================
+ Control how the animation plays
+ */
+ time (time) {
+ if (time == null) {
+ return this._time
+ }
+ const dt = time - this._time
+ this.step(dt)
return this
}
- finish () {
- return this.step(Infinity)
- }
-
- reverse (reverse) {
- this._reverse = reverse == null ? !this._reverse : reverse
+ timeline (timeline) {
+ // check explicitly for undefined so we can set the timeline to null
+ if (typeof timeline === 'undefined') return this._timeline
+ this._timeline = timeline
return this
}
- ease (fn) {
- this._stepper = new Ease(fn)
+ unschedule () {
+ var timeline = this.timeline()
+ timeline && timeline.unschedule(this)
return this
}
- active (enabled) {
- if (enabled == null) return this.enabled
- this.enabled = enabled
- return this
- }
+ // Run each initialise function in the runner if required
+ _initialise (running) {
+ // If we aren't running, we shouldn't initialise when not declarative
+ if (!running && !this._isDeclarative) return
- /*
- Private Methods
- ===============
- Methods that shouldn't be used externally
- */
+ // Loop through all of the initialisers
+ for (var i = 0, len = this._queue.length; i < len; ++i) {
+ // Get the current initialiser
+ var current = this._queue[i]
+
+ // Determine whether we need to initialise
+ var needsIt = this._isDeclarative || (!current.initialised && running)
+ running = !current.finished
+
+ // Call the initialiser if we need to
+ if (needsIt && running) {
+ current.initialiser.call(this)
+ current.initialised = true
+ }
+ }
+ }
// Save a morpher to the morpher list so that we can retarget it later
_rememberMorpher (method, morpher) {
@@ -368,6 +434,25 @@ export default class Runner extends EventTarget {
}
// Try to set the target for a morpher if the morpher exists, otherwise
+ // Run each run function for the position or dt given
+ _run (positionOrDt) {
+ // Run all of the _queue directly
+ var allfinished = true
+ for (var i = 0, len = this._queue.length; i < len; ++i) {
+ // Get the current function to run
+ var current = this._queue[i]
+
+ // Run the function if its not finished, we keep track of the finished
+ // flag for the sake of declarative _queue
+ var converged = current.runner.call(this, positionOrDt)
+ current.finished = current.finished || (converged === true)
+ allfinished = allfinished && current.finished
+ }
+
+ // We report when all of the constructors are finished
+ return allfinished
+ }
+
// do nothing and return false
_tryRetarget (method, target, extra) {
if (this._history[method]) {
@@ -395,94 +480,6 @@ export default class Runner extends EventTarget {
return false
}
- // Run each initialise function in the runner if required
- _initialise (running) {
- // If we aren't running, we shouldn't initialise when not declarative
- if (!running && !this._isDeclarative) return
-
- // Loop through all of the initialisers
- for (var i = 0, len = this._queue.length; i < len; ++i) {
- // Get the current initialiser
- var current = this._queue[i]
-
- // Determine whether we need to initialise
- var needsIt = this._isDeclarative || (!current.initialised && running)
- running = !current.finished
-
- // Call the initialiser if we need to
- if (needsIt && running) {
- current.initialiser.call(this)
- current.initialised = true
- }
- }
- }
-
- // Run each run function for the position or dt given
- _run (positionOrDt) {
- // Run all of the _queue directly
- var allfinished = true
- for (var i = 0, len = this._queue.length; i < len; ++i) {
- // Get the current function to run
- var current = this._queue[i]
-
- // Run the function if its not finished, we keep track of the finished
- // flag for the sake of declarative _queue
- var converged = current.runner.call(this, positionOrDt)
- current.finished = current.finished || (converged === true)
- allfinished = allfinished && current.finished
- }
-
- // We report when all of the constructors are finished
- return allfinished
- }
-
- addTransform (transform, index) {
- this.transforms.lmultiplyO(transform)
- return this
- }
-
- clearTransform () {
- this.transforms = new Matrix()
- return this
- }
-
- // TODO: Keep track of all transformations so that deletion is faster
- clearTransformsFromQueue () {
- if (!this.done || !this._timeline || !this._timeline._runnerIds.includes(this.id)) {
- this._queue = this._queue.filter((item) => {
- return !item.isTransform
- })
- }
- }
-
- static sanitise (duration, delay, when) {
- // Initialise the default parameters
- var times = 1
- var swing = false
- var wait = 0
- duration = duration || timeline.duration
- delay = delay || timeline.delay
- when = when || 'last'
-
- // If we have an object, unpack the values
- if (typeof duration === 'object' && !(duration instanceof Stepper)) {
- delay = duration.delay || delay
- when = duration.when || when
- swing = duration.swing || swing
- times = duration.times || times
- wait = duration.wait || wait
- duration = duration.duration || timeline.duration
- }
-
- return {
- duration: duration,
- delay: delay,
- swing: swing,
- times: times,
- wait: wait,
- when: when
- }
- }
}
Runner.id = 0
@@ -543,17 +540,29 @@ export class RunnerArray {
return this
}
- getByID (id) {
- return this.runners[this.ids.indexOf(id + 1)]
+ clearBefore (id) {
+ const deleteCnt = this.ids.indexOf(id + 1) || 1
+ this.ids.splice(0, deleteCnt, 0)
+ this.runners.splice(0, deleteCnt, new FakeRunner())
+ .forEach((r) => r.clearTransformsFromQueue())
+ return this
}
- remove (id) {
+ edit (id, newRunner) {
const index = this.ids.indexOf(id + 1)
- this.ids.splice(index, 1)
- this.runners.splice(index, 1)
+ this.ids.splice(index, 1, id + 1)
+ this.runners.splice(index, 1, newRunner)
return this
}
+ getByID (id) {
+ return this.runners[this.ids.indexOf(id + 1)]
+ }
+
+ length () {
+ return this.ids.length
+ }
+
merge () {
let lastRunner = null
for (let i = 0; i < this.runners.length; ++i) {
@@ -580,24 +589,13 @@ export class RunnerArray {
return this
}
- edit (id, newRunner) {
+ remove (id) {
const index = this.ids.indexOf(id + 1)
- this.ids.splice(index, 1, id + 1)
- this.runners.splice(index, 1, newRunner)
+ this.ids.splice(index, 1)
+ this.runners.splice(index, 1)
return this
}
- length () {
- return this.ids.length
- }
-
- clearBefore (id) {
- const deleteCnt = this.ids.indexOf(id + 1) || 1
- this.ids.splice(0, deleteCnt, 0)
- this.runners.splice(0, deleteCnt, new FakeRunner())
- .forEach((r) => r.clearTransformsFromQueue())
- return this
- }
}
registerMethods({
diff --git a/src/animation/Timeline.js b/src/animation/Timeline.js
index d175ae6..a29d683 100644
--- a/src/animation/Timeline.js
+++ b/src/animation/Timeline.js
@@ -44,6 +44,62 @@ export default class Timeline extends EventTarget {
this._stepImmediate = this._stepFn.bind(this, true)
}
+ active () {
+ return !!this._nextFrame
+ }
+
+ finish () {
+ // Go to end and pause
+ this.time(this.getEndTimeOfTimeline() + 1)
+ return this.pause()
+ }
+
+ // Calculates the end of the timeline
+ getEndTime () {
+ var lastRunnerInfo = this.getLastRunnerInfo()
+ var lastDuration = lastRunnerInfo ? lastRunnerInfo.runner.duration() : 0
+ var lastStartTime = lastRunnerInfo ? lastRunnerInfo.start : this._time
+ return lastStartTime + lastDuration
+ }
+
+ getEndTimeOfTimeline () {
+ const endTimes = this._runners.map((i) => i.start + i.runner.duration())
+ return Math.max(0, ...endTimes)
+ }
+
+ getLastRunnerInfo () {
+ return this.getRunnerInfoById(this._lastRunnerId)
+ }
+
+ getRunnerInfoById (id) {
+ return this._runners[this._runnerIds.indexOf(id)] || null
+ }
+
+ pause () {
+ this._paused = true
+ return this._continue()
+ }
+
+ persist (dtOrForever) {
+ if (dtOrForever == null) return this._persist
+ this._persist = dtOrForever
+ return this
+ }
+
+ play () {
+ // Now make sure we are not paused and continue the animation
+ this._paused = false
+ return this.updateTime()._continue()
+ }
+
+ reverse (yes) {
+ var currentSpeed = this.speed()
+ if (yes == null) return this.speed(-currentSpeed)
+
+ var positive = Math.abs(currentSpeed)
+ return this.speed(yes ? -positive : positive)
+ }
+
// schedules a runner on the timeline
schedule (runner, delay, when) {
if (runner == null) {
@@ -102,56 +158,20 @@ export default class Timeline extends EventTarget {
return this
}
- // Remove the runner from this timeline
- unschedule (runner) {
- var index = this._runnerIds.indexOf(runner.id)
- if (index < 0) return this
-
- this._runners.splice(index, 1)
- this._runnerIds.splice(index, 1)
-
- runner.timeline(null)
- return this
- }
-
- getRunnerInfoById (id) {
- return this._runners[this._runnerIds.indexOf(id)] || null
- }
-
- getLastRunnerInfo () {
- return this.getRunnerInfoById(this._lastRunnerId)
- }
-
- // Calculates the end of the timeline
- getEndTime () {
- var lastRunnerInfo = this.getLastRunnerInfo()
- var lastDuration = lastRunnerInfo ? lastRunnerInfo.runner.duration() : 0
- var lastStartTime = lastRunnerInfo ? lastRunnerInfo.start : this._time
- return lastStartTime + lastDuration
- }
-
- getEndTimeOfTimeline () {
- const endTimes = this._runners.map((i) => i.start + i.runner.duration())
- return Math.max(0, ...endTimes)
+ seek (dt) {
+ return this.time(this._time + dt)
}
- // Makes sure, that after pausing the time doesn't jump
- updateTime () {
- if (!this.active()) {
- this._lastSourceTime = this._timeSource()
- }
+ source (fn) {
+ if (fn == null) return this._timeSource
+ this._timeSource = fn
return this
}
- play () {
- // Now make sure we are not paused and continue the animation
- this._paused = false
- return this.updateTime()._continue()
- }
-
- pause () {
- this._paused = true
- return this._continue()
+ speed (speed) {
+ if (speed == null) return this._speed
+ this._speed = speed
+ return this
}
stop () {
@@ -160,45 +180,41 @@ export default class Timeline extends EventTarget {
return this.pause()
}
- finish () {
- // Go to end and pause
- this.time(this.getEndTimeOfTimeline() + 1)
- return this.pause()
+ time (time) {
+ if (time == null) return this._time
+ this._time = time
+ return this._continue(true)
}
- speed (speed) {
- if (speed == null) return this._speed
- this._speed = speed
- return this
- }
+ // Remove the runner from this timeline
+ unschedule (runner) {
+ var index = this._runnerIds.indexOf(runner.id)
+ if (index < 0) return this
- reverse (yes) {
- var currentSpeed = this.speed()
- if (yes == null) return this.speed(-currentSpeed)
+ this._runners.splice(index, 1)
+ this._runnerIds.splice(index, 1)
- var positive = Math.abs(currentSpeed)
- return this.speed(yes ? -positive : positive)
+ runner.timeline(null)
+ return this
}
- seek (dt) {
- return this.time(this._time + dt)
+ // Makes sure, that after pausing the time doesn't jump
+ updateTime () {
+ if (!this.active()) {
+ this._lastSourceTime = this._timeSource()
+ }
+ return this
}
- time (time) {
- if (time == null) return this._time
- this._time = time
- return this._continue(true)
- }
+ // Checks if we are running and continues the animation
+ _continue (immediateStep = false) {
+ Animator.cancelFrame(this._nextFrame)
+ this._nextFrame = null
- persist (dtOrForever) {
- if (dtOrForever == null) return this._persist
- this._persist = dtOrForever
- return this
- }
+ if (immediateStep) return this._stepImmediate()
+ if (this._paused) return this
- source (fn) {
- if (fn == null) return this._timeSource
- this._timeSource = fn
+ this._nextFrame = Animator.frame(this._step)
return this
}
@@ -303,21 +319,6 @@ export default class Timeline extends EventTarget {
return this
}
- // Checks if we are running and continues the animation
- _continue (immediateStep = false) {
- Animator.cancelFrame(this._nextFrame)
- this._nextFrame = null
-
- if (immediateStep) return this._stepImmediate()
- if (this._paused) return this
-
- this._nextFrame = Animator.frame(this._step)
- return this
- }
-
- active () {
- return !!this._nextFrame
- }
}
registerMethods({