diff options
Diffstat (limited to 'src/animation')
-rw-r--r-- | src/animation/Controller.js | 7 | ||||
-rw-r--r-- | src/animation/Morphable.js | 85 | ||||
-rw-r--r-- | src/animation/Queue.js | 45 | ||||
-rw-r--r-- | src/animation/Runner.js | 416 | ||||
-rw-r--r-- | src/animation/Timeline.js | 179 |
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({ |