diff options
author | Ulrich-Matthias Schäfer <ulima.ums@googlemail.com> | 2023-06-13 01:43:37 +0200 |
---|---|---|
committer | Ulrich-Matthias Schäfer <ulima.ums@googlemail.com> | 2023-06-13 01:43:37 +0200 |
commit | 03322672782a6318b019eff33fe44ec800d6f12c (patch) | |
tree | 9d807e1585cf28a1ea98e87b855932dba00f1880 /src/animation | |
parent | 8e18b4698edd00f83af0be05bb3374f92fecaeb9 (diff) | |
download | svg.js-03322672782a6318b019eff33fe44ec800d6f12c.tar.gz svg.js-03322672782a6318b019eff33fe44ec800d6f12c.zip |
dependency updates, easier formatting
Diffstat (limited to 'src/animation')
-rw-r--r-- | src/animation/Animator.js | 23 | ||||
-rw-r--r-- | src/animation/Controller.js | 44 | ||||
-rw-r--r-- | src/animation/Morphable.js | 117 | ||||
-rw-r--r-- | src/animation/Queue.js | 18 | ||||
-rw-r--r-- | src/animation/Runner.js | 426 | ||||
-rw-r--r-- | src/animation/Timeline.js | 61 |
6 files changed, 367 insertions, 322 deletions
diff --git a/src/animation/Animator.js b/src/animation/Animator.js index fc3df10..11cca54 100644 --- a/src/animation/Animator.js +++ b/src/animation/Animator.js @@ -9,7 +9,7 @@ const Animator = { timer: () => globals.window.performance || globals.window.Date, transforms: [], - frame (fn) { + frame(fn) { // Store the node const node = Animator.frames.push({ run: fn }) @@ -22,7 +22,7 @@ const Animator = { return node }, - timeout (fn, delay) { + timeout(fn, delay) { delay = delay || 0 // Work out when the event should fire @@ -39,7 +39,7 @@ const Animator = { return node }, - immediate (fn) { + immediate(fn) { // Add the immediate fn to the end of the queue const node = Animator.immediates.push(fn) // Request another animation frame if we need one @@ -50,19 +50,19 @@ const Animator = { return node }, - cancelFrame (node) { + cancelFrame(node) { node != null && Animator.frames.remove(node) }, - clearTimeout (node) { + clearTimeout(node) { node != null && Animator.timeouts.remove(node) }, - cancelImmediate (node) { + cancelImmediate(node) { node != null && Animator.immediates.remove(node) }, - _draw (now) { + _draw(now) { // Run all the timeouts we can run, if they are not ready yet, add them // to the end of the queue immediately! (bad timeouts!!! [sarcasm]) let nextTimeout = null @@ -82,7 +82,7 @@ const Animator = { // Run all of the animation frames let nextFrame = null const lastFrame = Animator.frames.last() - while ((nextFrame !== lastFrame) && (nextFrame = Animator.frames.shift())) { + while (nextFrame !== lastFrame && (nextFrame = Animator.frames.shift())) { nextFrame.run(now) } @@ -92,9 +92,10 @@ const Animator = { } // If we have remaining timeouts or frames, draw until we don't anymore - Animator.nextDraw = Animator.timeouts.first() || Animator.frames.first() - ? globals.window.requestAnimationFrame(Animator._draw) - : null + Animator.nextDraw = + Animator.timeouts.first() || Animator.frames.first() + ? globals.window.requestAnimationFrame(Animator._draw) + : null } } diff --git a/src/animation/Controller.js b/src/animation/Controller.js index 303fb71..1cf879d 100644 --- a/src/animation/Controller.js +++ b/src/animation/Controller.js @@ -7,7 +7,7 @@ Base Class The base stepper class that will be ***/ -function makeSetterGetter (k, f) { +function makeSetterGetter(k, f) { return function (v) { if (v == null) return this[k] this[k] = v @@ -24,27 +24,27 @@ export const easing = { return -Math.cos(pos * Math.PI) / 2 + 0.5 }, '>': function (pos) { - return Math.sin(pos * Math.PI / 2) + return Math.sin((pos * Math.PI) / 2) }, '<': function (pos) { - return -Math.cos(pos * Math.PI / 2) + 1 + return -Math.cos((pos * Math.PI) / 2) + 1 }, bezier: function (x1, y1, x2, y2) { // see https://www.w3.org/TR/css-easing-1/#cubic-bezier-algo return function (t) { if (t < 0) { if (x1 > 0) { - return y1 / x1 * t + return (y1 / x1) * t } else if (x2 > 0) { - return y2 / x2 * t + return (y2 / x2) * t } else { return 0 } } else if (t > 1) { if (x2 < 1) { - return (1 - y2) / (1 - x2) * t + (y2 - x2) / (1 - x2) + return ((1 - y2) / (1 - x2)) * t + (y2 - x2) / (1 - x2) } else if (x1 < 1) { - return (1 - y1) / (1 - x1) * t + (y1 - x1) / (1 - x1) + return ((1 - y1) / (1 - x1)) * t + (y1 - x1) / (1 - x1) } else { return 1 } @@ -93,7 +93,7 @@ export const easing = { } export class Stepper { - done () { + done() { return false } } @@ -104,12 +104,12 @@ Easing Functions ***/ export class Ease extends Stepper { - constructor (fn = timeline.ease) { + constructor(fn = timeline.ease) { super() this.ease = easing[fn] || fn } - step (from, to, pos) { + step(from, to, pos) { if (typeof from !== 'number') { return pos < 1 ? from : to } @@ -123,22 +123,21 @@ Controller Types ***/ export class Controller extends Stepper { - constructor (fn) { + constructor(fn) { super() this.stepper = fn } - done (c) { + done(c) { return c.done } - step (current, target, dt, c) { + step(current, target, dt, c) { return this.stepper(current, target, dt, c) } - } -function recalculate () { +function recalculate() { // Apply the default parameters const duration = (this._duration || 500) / 1000 const overshoot = this._overshoot || 0 @@ -156,13 +155,12 @@ function recalculate () { } export class Spring extends Controller { - constructor (duration = 500, overshoot = 0) { + constructor(duration = 500, overshoot = 0) { super() - this.duration(duration) - .overshoot(overshoot) + this.duration(duration).overshoot(overshoot) } - step (current, target, dt, c) { + step(current, target, dt, c) { if (typeof current === 'string') return current c.done = dt === Infinity if (dt === Infinity) return target @@ -177,9 +175,7 @@ export class Spring extends Controller { // Apply the control to get the new position and store it const acceleration = -this.d * velocity - this.k * (current - target) - const newPosition = current - + velocity * dt - + acceleration * dt * dt / 2 + const newPosition = current + velocity * dt + (acceleration * dt * dt) / 2 // Store the velocity c.velocity = velocity + acceleration * dt @@ -196,12 +192,12 @@ extend(Spring, { }) export class PID extends Controller { - constructor (p = 0.1, i = 0.01, d = 0, windup = 1000) { + constructor(p = 0.1, i = 0.01, d = 0, windup = 1000) { super() this.p(p).i(i).d(d).windup(windup) } - step (current, target, dt, c) { + step(current, target, dt, c) { if (typeof current === 'string') return current c.done = dt === Infinity diff --git a/src/animation/Morphable.js b/src/animation/Morphable.js index e9b3ff8..240ca7b 100644 --- a/src/animation/Morphable.js +++ b/src/animation/Morphable.js @@ -19,9 +19,7 @@ const getClassForType = (value) => { if (Color.isColor(value)) { return Color } else if (delimiter.test(value)) { - return isPathLetter.test(value) - ? PathArray - : SVGArray + return isPathLetter.test(value) ? PathArray : SVGArray } else if (numberAndUnit.test(value)) { return SVGNumber } else { @@ -39,7 +37,7 @@ const getClassForType = (value) => { } export default class Morphable { - constructor (stepper) { + constructor(stepper) { this._stepper = stepper || new Ease('-') this._from = null @@ -49,12 +47,17 @@ export default class Morphable { this._morphObj = null } - at (pos) { - return this._morphObj.morph(this._from, this._to, pos, this._stepper, this._context) - + at(pos) { + return this._morphObj.morph( + this._from, + this._to, + pos, + this._stepper, + this._context + ) } - done () { + done() { const complete = this._context .map(this._stepper.done) .reduce(function (last, curr) { @@ -63,7 +66,7 @@ export default class Morphable { return complete } - from (val) { + from(val) { if (val == null) { return this._from } @@ -72,13 +75,13 @@ export default class Morphable { return this } - stepper (stepper) { + stepper(stepper) { if (stepper == null) return this._stepper this._stepper = stepper return this } - to (val) { + to(val) { if (val == null) { return this._to } @@ -87,7 +90,7 @@ export default class Morphable { return this } - type (type) { + type(type) { // getter if (type == null) { return this._type @@ -98,33 +101,34 @@ export default class Morphable { return this } - _set (value) { + _set(value) { if (!this._type) { this.type(getClassForType(value)) } - let result = (new this._type(value)) + let result = new this._type(value) if (this._type === Color) { result = this._to ? result[this._to[4]]() : this._from - ? result[this._from[4]]() - : result + ? result[this._from[4]]() + : result } if (this._type === ObjectBag) { result = this._to ? result.align(this._to) : this._from - ? result.align(this._from) - : result + ? result.align(this._from) + : result } result = result.toConsumable() this._morphObj = this._morphObj || new this._type() - this._context = this._context - || Array.apply(null, Array(result.length)) + this._context = + this._context || + Array.apply(null, Array(result.length)) .map(Object) .map(function (o) { o.done = true @@ -132,36 +136,34 @@ export default class Morphable { }) return result } - } export class NonMorphable { - constructor (...args) { + constructor(...args) { this.init(...args) } - init (val) { + init(val) { val = Array.isArray(val) ? val[0] : val this.value = val return this } - toArray () { - return [ this.value ] + toArray() { + return [this.value] } - valueOf () { + valueOf() { return this.value } - } export class TransformBag { - constructor (...args) { + constructor(...args) { this.init(...args) } - init (obj) { + init(obj) { if (Array.isArray(obj)) { obj = { scaleX: obj[0], @@ -179,7 +181,7 @@ export class TransformBag { return this } - toArray () { + toArray() { const v = this return [ @@ -207,23 +209,24 @@ TransformBag.defaults = { } const sortByKey = (a, b) => { - return (a[0] < b[0] ? -1 : (a[0] > b[0] ? 1 : 0)) + return a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0 } export class ObjectBag { - constructor (...args) { + constructor(...args) { this.init(...args) } - align (other) { + align(other) { const values = this.values for (let i = 0, il = values.length; i < il; ++i) { - // If the type is the same we only need to check if the color is in the correct format if (values[i + 1] === other[i + 1]) { if (values[i + 1] === Color && other[i + 7] !== values[i + 7]) { const space = other[i + 7] - const color = new Color(this.values.splice(i + 3, 5))[space]().toArray() + const color = new Color(this.values.splice(i + 3, 5)) + [space]() + .toArray() this.values.splice(i + 3, 0, ...color) } @@ -237,19 +240,26 @@ export class ObjectBag { // The types differ, so we overwrite the new type with the old one // And initialize it with the types default (e.g. black for color or 0 for number) - const defaultObject = new (other[i + 1])().toArray() + const defaultObject = new other[i + 1]().toArray() // Than we fix the values array - const toDelete = (values[i + 2]) + 3 + const toDelete = values[i + 2] + 3 - values.splice(i, toDelete, other[i], other[i + 1], other[i + 2], ...defaultObject) + values.splice( + i, + toDelete, + other[i], + other[i + 1], + other[i + 2], + ...defaultObject + ) i += values[i + 2] + 2 } return this } - init (objOrArr) { + init(objOrArr) { this.values = [] if (Array.isArray(objOrArr)) { @@ -263,7 +273,7 @@ export class ObjectBag { for (const i in objOrArr) { const Type = getClassForType(objOrArr[i]) const val = new Type(objOrArr[i]).toArray() - entries.push([ i, Type, val.length, ...val ]) + entries.push([i, Type, val.length, ...val]) } entries.sort(sortByKey) @@ -272,11 +282,11 @@ export class ObjectBag { return this } - toArray () { + toArray() { return this.values } - valueOf () { + valueOf() { const obj = {} const arr = this.values @@ -286,40 +296,35 @@ export class ObjectBag { const Type = arr.shift() const num = arr.shift() const values = arr.splice(0, num) - obj[key] = new Type(values)// .valueOf() + obj[key] = new Type(values) // .valueOf() } return obj } - } -const morphableTypes = [ - NonMorphable, - TransformBag, - ObjectBag -] +const morphableTypes = [NonMorphable, TransformBag, ObjectBag] -export function registerMorphableType (type = []) { +export function registerMorphableType(type = []) { morphableTypes.push(...[].concat(type)) } -export function makeMorphable () { +export function makeMorphable() { extend(morphableTypes, { - to (val) { + to(val) { return new Morphable() .type(this.constructor) - .from(this.toArray())// this.valueOf()) + .from(this.toArray()) // this.valueOf()) .to(val) }, - fromArray (arr) { + fromArray(arr) { this.init(arr) return this }, - toConsumable () { + toConsumable() { return this.toArray() }, - morph (from, to, pos, stepper, context) { + morph(from, to, pos, stepper, context) { const mapper = function (i, index) { return stepper.step(i, to[index], pos, context[index], context) } diff --git a/src/animation/Queue.js b/src/animation/Queue.js index ba484dc..65108f3 100644 --- a/src/animation/Queue.js +++ b/src/animation/Queue.js @@ -1,22 +1,25 @@ export default class Queue { - constructor () { + constructor() { this._first = null this._last = null } // Shows us the first item in the list - first () { + first() { return this._first && this._first.value } // Shows us the last item in the list - last () { + last() { return this._last && this._last.value } - push (value) { + push(value) { // An item stores an id and the provided value - const item = typeof value.next !== 'undefined' ? value : { value: value, next: null, prev: null } + const item = + typeof value.next !== 'undefined' + ? value + : { value: value, next: null, prev: null } // Deal with the queue being empty or populated if (this._last) { @@ -33,7 +36,7 @@ export default class Queue { } // Removes the item that was returned from the push - remove (item) { + remove(item) { // Relink the previous item if (item.prev) item.prev.next = item.next if (item.next) item.next.prev = item.prev @@ -45,7 +48,7 @@ export default class Queue { item.next = null } - shift () { + shift() { // Check if we have a value const remove = this._first if (!remove) return null @@ -56,5 +59,4 @@ export default class Queue { this._last = this._first ? this._last : null return remove.value } - } diff --git a/src/animation/Runner.js b/src/animation/Runner.js index 9d50042..a83a02f 100644 --- a/src/animation/Runner.js +++ b/src/animation/Runner.js @@ -15,21 +15,17 @@ import SVGNumber from '../types/SVGNumber.js' import Timeline from './Timeline.js' export default class Runner extends EventTarget { - constructor (options) { + constructor(options) { super() // Store a unique id on the runner, so that we can identify it later this.id = Runner.id++ // Ensure a default value - options = options == null - ? timeline.duration - : options + options = options == null ? timeline.duration : options // Ensure that we get a controller - options = typeof options === 'function' - ? new Controller(options) - : options + options = typeof options === 'function' ? new Controller(options) : options // Declare all of the variables this._element = null @@ -71,7 +67,7 @@ export default class Runner extends EventTarget { this._persist = this._isDeclarative ? true : null } - static sanitise (duration, delay, when) { + static sanitise(duration, delay, when) { // Initialise the default parameters let times = 1 let swing = false @@ -100,7 +96,7 @@ export default class Runner extends EventTarget { } } - active (enabled) { + active(enabled) { if (enabled == null) return this.enabled this.enabled = enabled return this @@ -111,16 +107,16 @@ export default class Runner extends EventTarget { =============== Methods that shouldn't be used externally */ - addTransform (transform, index) { + addTransform(transform) { this.transforms.lmultiplyO(transform) return this } - after (fn) { + after(fn) { return this.on('finished', fn) } - animate (duration, delay, when) { + animate(duration, delay, when) { const o = Runner.sanitise(duration, delay, when) const runner = new Runner(o.duration) if (this._timeline) runner.timeline(this._timeline) @@ -128,33 +124,37 @@ export default class Runner extends EventTarget { return runner.loop(o).schedule(o.delay, o.when) } - clearTransform () { + 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)) { + clearTransformsFromQueue() { + if ( + !this.done || + !this._timeline || + !this._timeline._runnerIds.includes(this.id) + ) { this._queue = this._queue.filter((item) => { return !item.isTransform }) } } - delay (delay) { + delay(delay) { return this.animate(0, delay) } - duration () { + duration() { return this._times * (this._wait + this._duration) - this._wait } - during (fn) { + during(fn) { return this.queue(null, fn) } - ease (fn) { + ease(fn) { this._stepper = new Ease(fn) return this } @@ -165,18 +165,18 @@ export default class Runner extends EventTarget { help us make new runners from the current runner */ - element (element) { + element(element) { if (element == null) return this._element this._element = element element._prepareRunner() return this } - finish () { + finish() { return this.step(Infinity) } - loop (times, swing, wait) { + loop(times, swing, wait) { // Deal with the user passing in an object if (typeof times === 'object') { swing = times.swing @@ -190,16 +190,18 @@ export default class Runner extends EventTarget { this._wait = wait || 0 // Allow true to be passed - if (this._times === true) { this._times = Infinity } + if (this._times === true) { + this._times = Infinity + } return this } - loops (p) { + loops(p) { const loopDuration = this._duration + this._wait if (p == null) { const loopsDone = Math.floor(this._time / loopDuration) - const relativeTime = (this._time - loopsDone * loopDuration) + const relativeTime = this._time - loopsDone * loopDuration const position = relativeTime / this._duration return Math.min(loopsDone + position, this._times) } @@ -209,13 +211,13 @@ export default class Runner extends EventTarget { return this.time(time) } - persist (dtOrForever) { + persist(dtOrForever) { if (dtOrForever == null) return this._persist this._persist = dtOrForever return this } - position (p) { + position(p) { // Get all of the variables we need const x = this._time const d = this._duration @@ -235,18 +237,20 @@ export default class Runner extends EventTarget { // Figure out the value without thinking about the start or end time const f = function (x) { - const swinging = s * Math.floor(x % (2 * (w + d)) / (w + d)) + const swinging = s * Math.floor((x % (2 * (w + d))) / (w + d)) const backwards = (swinging && !r) || (!swinging && r) - const uncliped = Math.pow(-1, backwards) * (x % (w + d)) / d + backwards + const uncliped = + (Math.pow(-1, backwards) * (x % (w + d))) / d + backwards const clipped = Math.max(Math.min(uncliped, 1), 0) return clipped } // Figure out the value by incorporating the start time const endTime = t * (w + d) - w - position = x <= 0 - ? Math.round(f(1e-5)) - : x < endTime + position = + x <= 0 + ? Math.round(f(1e-5)) + : x < endTime ? f(x) : Math.round(f(endTime - 1e-5)) return position @@ -254,13 +258,13 @@ export default class Runner extends EventTarget { // Work out the loops done and add the position to the loops done const loopsDone = Math.floor(this.loops()) - const swingForward = s && (loopsDone % 2 === 0) + const swingForward = s && loopsDone % 2 === 0 const forwards = (swingForward && !r) || (r && swingForward) position = loopsDone + (forwards ? p : 1 - p) return this.loops(position) } - progress (p) { + progress(p) { if (p == null) { return Math.min(1, this._time / this.duration()) } @@ -272,7 +276,7 @@ export default class Runner extends EventTarget { =================== These methods allow us to attach basic functions to the runner directly */ - queue (initFn, runFn, retargetFn, isTransform) { + queue(initFn, runFn, retargetFn, isTransform) { this._queue.push({ initialiser: initFn || noop, runner: runFn || noop, @@ -286,19 +290,19 @@ export default class Runner extends EventTarget { return this } - reset () { + reset() { if (this._reseted) return this this.time(0) this._reseted = true return this } - reverse (reverse) { + reverse(reverse) { this._reverse = reverse == null ? !this._reverse : reverse return this } - schedule (timeline, delay, 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 @@ -316,7 +320,7 @@ export default class Runner extends EventTarget { return this } - step (dt) { + step(dt) { // If we are inactive, this stepper just gets skipped if (!this.enabled) return this @@ -373,7 +377,7 @@ export default class Runner extends EventTarget { ======================== Control how the animation plays */ - time (time) { + time(time) { if (time == null) { return this._time } @@ -382,21 +386,21 @@ export default class Runner extends EventTarget { return this } - timeline (timeline) { + 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 } - unschedule () { + unschedule() { const timeline = this.timeline() timeline && timeline.unschedule(this) return this } // Run each initialise function in the runner if required - _initialise (running) { + _initialise(running) { // If we aren't running, we shouldn't initialise when not declarative if (!running && !this._isDeclarative) return @@ -418,7 +422,7 @@ export default class Runner extends EventTarget { } // Save a morpher to the morpher list so that we can retarget it later - _rememberMorpher (method, morpher) { + _rememberMorpher(method, morpher) { this._history[method] = { morpher: morpher, caller: this._queue[this._queue.length - 1] @@ -438,7 +442,7 @@ 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(positionOrDt) { // Run all of the _queue directly let allfinished = true for (let i = 0, len = this._queue.length; i < len; ++i) { @@ -448,7 +452,7 @@ export default class Runner extends EventTarget { // Run the function if its not finished, we keep track of the finished // flag for the sake of declarative _queue const converged = current.runner.call(this, positionOrDt) - current.finished = current.finished || (converged === true) + current.finished = current.finished || converged === true allfinished = allfinished && current.finished } @@ -457,7 +461,7 @@ export default class Runner extends EventTarget { } // do nothing and return false - _tryRetarget (method, target, extra) { + _tryRetarget(method, target, extra) { if (this._history[method]) { // if the last method wasn't even initialised, throw it away if (!this._history[method].caller.initialised) { @@ -482,23 +486,22 @@ export default class Runner extends EventTarget { } return false } - } Runner.id = 0 export class FakeRunner { - constructor (transforms = new Matrix(), id = -1, done = true) { + constructor(transforms = new Matrix(), id = -1, done = true) { this.transforms = transforms this.id = id this.done = done } - clearTransformsFromQueue () { } + clearTransformsFromQueue() {} } -extend([ Runner, FakeRunner ], { - mergeWith (runner) { +extend([Runner, FakeRunner], { + mergeWith(runner) { return new FakeRunner( runner.transforms.lmultiply(this.transforms), runner.id @@ -511,7 +514,7 @@ extend([ Runner, FakeRunner ], { const lmultiply = (last, curr) => last.lmultiplyO(curr) const getRunnerTransform = (runner) => runner.transforms -function mergeTransforms () { +function mergeTransforms() { // Find the matrix to apply to the element and apply it const runners = this._transformationRunners.runners const netTransform = runners @@ -528,12 +531,12 @@ function mergeTransforms () { } export class RunnerArray { - constructor () { + constructor() { this.runners = [] this.ids = [] } - add (runner) { + add(runner) { if (this.runners.includes(runner)) return const id = runner.id + 1 @@ -543,39 +546,44 @@ export class RunnerArray { return this } - clearBefore (id) { + clearBefore(id) { const deleteCnt = this.ids.indexOf(id + 1) || 1 this.ids.splice(0, deleteCnt, 0) - this.runners.splice(0, deleteCnt, new FakeRunner()) + this.runners + .splice(0, deleteCnt, new FakeRunner()) .forEach((r) => r.clearTransformsFromQueue()) return this } - edit (id, newRunner) { + edit(id, newRunner) { const index = this.ids.indexOf(id + 1) this.ids.splice(index, 1, id + 1) this.runners.splice(index, 1, newRunner) return this } - getByID (id) { + getByID(id) { return this.runners[this.ids.indexOf(id + 1)] } - length () { + length() { return this.ids.length } - merge () { + merge() { let lastRunner = null for (let i = 0; i < this.runners.length; ++i) { const runner = this.runners[i] - const condition = lastRunner - && runner.done && lastRunner.done + const condition = + lastRunner && + runner.done && + lastRunner.done && // don't merge runner when persisted on timeline - && (!runner._timeline || !runner._timeline._runnerIds.includes(runner.id)) - && (!lastRunner._timeline || !lastRunner._timeline._runnerIds.includes(lastRunner.id)) + (!runner._timeline || + !runner._timeline._runnerIds.includes(runner.id)) && + (!lastRunner._timeline || + !lastRunner._timeline._runnerIds.includes(lastRunner.id)) if (condition) { // the +1 happens in the function @@ -592,18 +600,17 @@ export class RunnerArray { return this } - remove (id) { + remove(id) { const index = this.ids.indexOf(id + 1) this.ids.splice(index, 1) this.runners.splice(index, 1) return this } - } registerMethods({ Element: { - animate (duration, delay, when) { + animate(duration, delay, when) { const o = Runner.sanitise(duration, delay, when) const timeline = this.timeline() return new Runner(o.duration) @@ -613,7 +620,7 @@ registerMethods({ .schedule(o.delay, o.when) }, - delay (by, when) { + delay(by, when) { return this.animate(0, by, when) }, @@ -621,21 +628,23 @@ registerMethods({ // which run before the current one. This is because absolute transformations // overwrite anything anyway so there is no need to waste time computing // other runners - _clearTransformRunnersBefore (currentRunner) { + _clearTransformRunnersBefore(currentRunner) { this._transformationRunners.clearBefore(currentRunner.id) }, - _currentTransform (current) { - return this._transformationRunners.runners - // we need the equal sign here to make sure, that also transformations - // on the same runner which execute before the current transformation are - // taken into account - .filter((runner) => runner.id <= current.id) - .map(getRunnerTransform) - .reduce(lmultiply, new Matrix()) + _currentTransform(current) { + return ( + this._transformationRunners.runners + // we need the equal sign here to make sure, that also transformations + // on the same runner which execute before the current transformation are + // taken into account + .filter((runner) => runner.id <= current.id) + .map(getRunnerTransform) + .reduce(lmultiply, new Matrix()) + ) }, - _addRunner (runner) { + _addRunner(runner) { this._transformationRunners.add(runner) // Make sure that the runner merge is executed at the very end of @@ -645,29 +654,30 @@ registerMethods({ this._frameId = Animator.immediate(mergeTransforms.bind(this)) }, - _prepareRunner () { + _prepareRunner() { if (this._frameId == null) { - this._transformationRunners = new RunnerArray() - .add(new FakeRunner(new Matrix(this))) + this._transformationRunners = new RunnerArray().add( + new FakeRunner(new Matrix(this)) + ) } } } }) // Will output the elements from array A that are not in the array B -const difference = (a, b) => a.filter(x => !b.includes(x)) +const difference = (a, b) => a.filter((x) => !b.includes(x)) extend(Runner, { - attr (a, v) { + attr(a, v) { return this.styleAttr('attr', a, v) }, // Add animatable styles - css (s, v) { + css(s, v) { return this.styleAttr('css', s, v) }, - styleAttr (type, nameOrAttrs, val) { + styleAttr(type, nameOrAttrs, val) { if (typeof nameOrAttrs === 'string') { return this.styleAttr(type, { [nameOrAttrs]: val }) } @@ -678,62 +688,69 @@ extend(Runner, { let morpher = new Morphable(this._stepper).to(attrs) let keys = Object.keys(attrs) - this.queue(function () { - morpher = morpher.from(this.element()[type](keys)) - }, function (pos) { - this.element()[type](morpher.at(pos).valueOf()) - return morpher.done() - }, function (newToAttrs) { + this.queue( + function () { + morpher = morpher.from(this.element()[type](keys)) + }, + function (pos) { + this.element()[type](morpher.at(pos).valueOf()) + return morpher.done() + }, + function (newToAttrs) { + // Check if any new keys were added + const newKeys = Object.keys(newToAttrs) + const differences = difference(newKeys, keys) + + // If their are new keys, initialize them and add them to morpher + if (differences.length) { + // Get the values + const addedFromAttrs = this.element()[type](differences) + + // Get the already initialized values + const oldFromAttrs = new ObjectBag(morpher.from()).valueOf() + + // Merge old and new + Object.assign(oldFromAttrs, addedFromAttrs) + morpher.from(oldFromAttrs) + } - // Check if any new keys were added - const newKeys = Object.keys(newToAttrs) - const differences = difference(newKeys, keys) + // Get the object from the morpher + const oldToAttrs = new ObjectBag(morpher.to()).valueOf() - // If their are new keys, initialize them and add them to morpher - if (differences.length) { - // Get the values - const addedFromAttrs = this.element()[type](differences) + // Merge in new attributes + Object.assign(oldToAttrs, newToAttrs) - // Get the already initialized values - const oldFromAttrs = new ObjectBag(morpher.from()).valueOf() + // Change morpher target + morpher.to(oldToAttrs) - // Merge old and new - Object.assign(oldFromAttrs, addedFromAttrs) - morpher.from(oldFromAttrs) + // Make sure that we save the work we did so we don't need it to do again + keys = newKeys + attrs = newToAttrs } - - // Get the object from the morpher - const oldToAttrs = new ObjectBag(morpher.to()).valueOf() - - // Merge in new attributes - Object.assign(oldToAttrs, newToAttrs) - - // Change morpher target - morpher.to(oldToAttrs) - - // Make sure that we save the work we did so we don't need it to do again - keys = newKeys - attrs = newToAttrs - }) + ) this._rememberMorpher(type, morpher) return this }, - zoom (level, point) { + zoom(level, point) { if (this._tryRetarget('zoom', level, point)) return this let morpher = new Morphable(this._stepper).to(new SVGNumber(level)) - this.queue(function () { - morpher = morpher.from(this.element().zoom()) - }, function (pos) { - this.element().zoom(morpher.at(pos), point) - return morpher.done() - }, function (newLevel, newPoint) { - point = newPoint - morpher.to(newLevel) - }) + this.queue( + function () { + morpher = morpher.from(this.element().zoom()) + }, + function (pos) { + this.element().zoom(morpher.at(pos), point) + return morpher.done() + }, + function (newLevel, newPoint) { + point = newPoint + morpher.to(newLevel) + } + ) this._rememberMorpher('zoom', morpher) return this @@ -756,22 +773,30 @@ extend(Runner, { // - Note F(1) = T // 4. Now you get the delta matrix as a result: D = F * inv(M) - transform (transforms, relative, affine) { + transform(transforms, relative, affine) { // If we have a declarative function, we should retarget it if possible relative = transforms.relative || relative - if (this._isDeclarative && !relative && this._tryRetarget('transform', transforms)) { + if ( + this._isDeclarative && + !relative && + this._tryRetarget('transform', transforms) + ) { return this } // Parse the parameters const isMatrix = Matrix.isMatrixLike(transforms) - affine = transforms.affine != null - ? transforms.affine - : (affine != null ? affine : !isMatrix) + affine = + transforms.affine != null + ? transforms.affine + : affine != null + ? affine + : !isMatrix // Create a morpher and set its type - const morpher = new Morphable(this._stepper) - .type(affine ? TransformBag : Matrix) + const morpher = new Morphable(this._stepper).type( + affine ? TransformBag : Matrix + ) let origin let element @@ -779,7 +804,7 @@ extend(Runner, { let currentAngle let startTransform - function setup () { + function setup() { // make sure element and origin is defined element = element || this.element() origin = origin || getOrigin(transforms, element) @@ -795,17 +820,17 @@ extend(Runner, { } } - function run (pos) { + function run(pos) { // clear all other transforms before this in case something is saved // on this runner. We are absolute. We dont need these! if (!relative) this.clearTransform() - const { x, y } = new Point(origin).transform(element._currentTransform(this)) + const { x, y } = new Point(origin).transform( + element._currentTransform(this) + ) - let target = new Matrix({ ...transforms, origin: [ x, y ] }) - let start = this._isDeclarative && current - ? current - : startTransform + let target = new Matrix({ ...transforms, origin: [x, y] }) + let start = this._isDeclarative && current ? current : startTransform if (affine) { target = target.decompose(x, y) @@ -816,8 +841,8 @@ extend(Runner, { const rCurrent = start.rotate // Figure out the shortest path to rotate directly - const possibilities = [ rTarget - 360, rTarget, rTarget + 360 ] - const distances = possibilities.map(a => Math.abs(a - rCurrent)) + const possibilities = [rTarget - 360, rTarget, rTarget + 360] + const distances = possibilities.map((a) => Math.abs(a - rCurrent)) const shortest = Math.min(...distances) const index = distances.indexOf(shortest) target.rotate = possibilities[index] @@ -846,11 +871,11 @@ extend(Runner, { return morpher.done() } - function retarget (newTransforms) { + function retarget(newTransforms) { // only get a new origin if it changed since the last call if ( - (newTransforms.origin || 'center').toString() - !== (transforms.origin || 'center').toString() + (newTransforms.origin || 'center').toString() !== + (transforms.origin || 'center').toString() ) { origin = getOrigin(newTransforms, element) } @@ -865,28 +890,28 @@ extend(Runner, { }, // Animatable x-axis - x (x, relative) { + x(x) { return this._queueNumber('x', x) }, // Animatable y-axis - y (y) { + y(y) { return this._queueNumber('y', y) }, - dx (x = 0) { + dx(x = 0) { return this._queueNumberDelta('x', x) }, - dy (y = 0) { + dy(y = 0) { return this._queueNumberDelta('y', y) }, - dmove (x, y) { + dmove(x, y) { return this.dx(x).dy(y) }, - _queueNumberDelta (method, to) { + _queueNumberDelta(method, to) { to = new SVGNumber(to) // Try to change the target if we have this method already registered @@ -895,66 +920,73 @@ extend(Runner, { // Make a morpher and queue the animation const morpher = new Morphable(this._stepper).to(to) let from = null - this.queue(function () { - from = this.element()[method]() - morpher.from(from) - morpher.to(from + to) - }, function (pos) { - this.element()[method](morpher.at(pos)) - return morpher.done() - }, function (newTo) { - morpher.to(from + new SVGNumber(newTo)) - }) + this.queue( + function () { + from = this.element()[method]() + morpher.from(from) + morpher.to(from + to) + }, + function (pos) { + this.element()[method](morpher.at(pos)) + return morpher.done() + }, + function (newTo) { + morpher.to(from + new SVGNumber(newTo)) + } + ) // Register the morpher so that if it is changed again, we can retarget it this._rememberMorpher(method, morpher) return this }, - _queueObject (method, to) { + _queueObject(method, to) { // Try to change the target if we have this method already registered if (this._tryRetarget(method, to)) return this // Make a morpher and queue the animation const morpher = new Morphable(this._stepper).to(to) - this.queue(function () { - morpher.from(this.element()[method]()) - }, function (pos) { - this.element()[method](morpher.at(pos)) - return morpher.done() - }) + this.queue( + function () { + morpher.from(this.element()[method]()) + }, + function (pos) { + this.element()[method](morpher.at(pos)) + return morpher.done() + } + ) // Register the morpher so that if it is changed again, we can retarget it this._rememberMorpher(method, morpher) return this }, - _queueNumber (method, value) { + _queueNumber(method, value) { return this._queueObject(method, new SVGNumber(value)) }, // Animatable center x-axis - cx (x) { + cx(x) { return this._queueNumber('cx', x) }, // Animatable center y-axis - cy (y) { + cy(y) { return this._queueNumber('cy', y) }, // Add animatable move - move (x, y) { + move(x, y) { return this.x(x).y(y) }, // Add animatable center - center (x, y) { + center(x, y) { return this.cx(x).cy(y) }, // Add animatable size - size (width, height) { + size(width, height) { // animate bbox based size for all other elements let box @@ -963,62 +995,64 @@ extend(Runner, { } if (!width) { - width = box.width / box.height * height + width = (box.width / box.height) * height } if (!height) { - height = box.height / box.width * width + height = (box.height / box.width) * width } - return this - .width(width) - .height(height) + return this.width(width).height(height) }, // Add animatable width - width (width) { + width(width) { return this._queueNumber('width', width) }, // Add animatable height - height (height) { + height(height) { return this._queueNumber('height', height) }, // Add animatable plot - plot (a, b, c, d) { + plot(a, b, c, d) { // Lines can be plotted with 4 arguments if (arguments.length === 4) { - return this.plot([ a, b, c, d ]) + return this.plot([a, b, c, d]) } if (this._tryRetarget('plot', a)) return this const morpher = new Morphable(this._stepper) - .type(this._element.MorphArray).to(a) - - this.queue(function () { - morpher.from(this._element.array()) - }, function (pos) { - this._element.plot(morpher.at(pos)) - return morpher.done() - }) + .type(this._element.MorphArray) + .to(a) + + this.queue( + function () { + morpher.from(this._element.array()) + }, + function (pos) { + this._element.plot(morpher.at(pos)) + return morpher.done() + } + ) this._rememberMorpher('plot', morpher) return this }, // Add leading method - leading (value) { + leading(value) { return this._queueNumber('leading', value) }, // Add animatable viewbox - viewbox (x, y, width, height) { + viewbox(x, y, width, height) { return this._queueObject('viewbox', new Box(x, y, width, height)) }, - update (o) { + update(o) { if (typeof o !== 'object') { return this.update({ offset: arguments[0], diff --git a/src/animation/Timeline.js b/src/animation/Timeline.js index 3f81b66..39e0f1a 100644 --- a/src/animation/Timeline.js +++ b/src/animation/Timeline.js @@ -7,7 +7,12 @@ const makeSchedule = function (runnerInfo) { const start = runnerInfo.start const duration = runnerInfo.runner.duration() const end = start + duration - return { start: start, duration: duration, end: end, runner: runnerInfo.runner } + return { + start: start, + duration: duration, + end: end, + runner: runnerInfo.runner + } } const defaultSource = function () { @@ -17,7 +22,7 @@ const defaultSource = function () { export default class Timeline extends EventTarget { // Construct a new timeline on the given element - constructor (timeSource = defaultSource) { + constructor(timeSource = defaultSource) { super() this._timeSource = timeSource @@ -44,55 +49,55 @@ export default class Timeline extends EventTarget { this._stepImmediate = this._stepFn.bind(this, true) } - active () { + active() { return !!this._nextFrame } - finish () { + finish() { // Go to end and pause this.time(this.getEndTimeOfTimeline() + 1) return this.pause() } // Calculates the end of the timeline - getEndTime () { + getEndTime() { const lastRunnerInfo = this.getLastRunnerInfo() const lastDuration = lastRunnerInfo ? lastRunnerInfo.runner.duration() : 0 const lastStartTime = lastRunnerInfo ? lastRunnerInfo.start : this._time return lastStartTime + lastDuration } - getEndTimeOfTimeline () { + getEndTimeOfTimeline() { const endTimes = this._runners.map((i) => i.start + i.runner.duration()) return Math.max(0, ...endTimes) } - getLastRunnerInfo () { + getLastRunnerInfo() { return this.getRunnerInfoById(this._lastRunnerId) } - getRunnerInfoById (id) { + getRunnerInfoById(id) { return this._runners[this._runnerIds.indexOf(id)] || null } - pause () { + pause() { this._paused = true return this._continue() } - persist (dtOrForever) { + persist(dtOrForever) { if (dtOrForever == null) return this._persist this._persist = dtOrForever return this } - play () { + play() { // Now make sure we are not paused and continue the animation this._paused = false return this.updateTime()._continue() } - reverse (yes) { + reverse(yes) { const currentSpeed = this.speed() if (yes == null) return this.speed(-currentSpeed) @@ -101,7 +106,7 @@ export default class Timeline extends EventTarget { } // schedules a runner on the timeline - schedule (runner, delay, when) { + schedule(runner, delay, when) { if (runner == null) { return this._runners.map(makeSchedule) } @@ -152,42 +157,42 @@ export default class Timeline extends EventTarget { this._runners.push(runnerInfo) this._runners.sort((a, b) => a.start - b.start) - this._runnerIds = this._runners.map(info => info.runner.id) + this._runnerIds = this._runners.map((info) => info.runner.id) this.updateTime()._continue() return this } - seek (dt) { + seek(dt) { return this.time(this._time + dt) } - source (fn) { + source(fn) { if (fn == null) return this._timeSource this._timeSource = fn return this } - speed (speed) { + speed(speed) { if (speed == null) return this._speed this._speed = speed return this } - stop () { + stop() { // Go to start and pause this.time(0) return this.pause() } - time (time) { + time(time) { if (time == null) return this._time this._time = time return this._continue(true) } // Remove the runner from this timeline - unschedule (runner) { + unschedule(runner) { const index = this._runnerIds.indexOf(runner.id) if (index < 0) return this @@ -199,7 +204,7 @@ export default class Timeline extends EventTarget { } // Makes sure, that after pausing the time doesn't jump - updateTime () { + updateTime() { if (!this.active()) { this._lastSourceTime = this._timeSource() } @@ -207,7 +212,7 @@ export default class Timeline extends EventTarget { } // Checks if we are running and continues the animation - _continue (immediateStep = false) { + _continue(immediateStep = false) { Animator.cancelFrame(this._nextFrame) this._nextFrame = null @@ -218,7 +223,7 @@ export default class Timeline extends EventTarget { return this } - _stepFn (immediateStep = false) { + _stepFn(immediateStep = false) { // Get the time delta from the last time and update the time const time = this._timeSource() let dtSource = time - this._lastSourceTime @@ -249,7 +254,7 @@ export default class Timeline extends EventTarget { // runner always wins the reset even if the other runner started earlier // and therefore should win the attribute battle // this can be solved by resetting them backwards - for (let k = this._runners.length; k--;) { + for (let k = this._runners.length; k--; ) { // Get and run the current runner and ignore it if its inactive const runnerInfo = this._runners[k] const runner = runnerInfo.runner @@ -309,7 +314,10 @@ export default class Timeline extends EventTarget { // Basically: we continue when there are runners right from us in time // when -->, and when runners are left from us when <-- - if ((runnersLeft && !(this._speed < 0 && this._time === 0)) || (this._runnerIds.length && this._speed < 0 && this._time > 0)) { + if ( + (runnersLeft && !(this._speed < 0 && this._time === 0)) || + (this._runnerIds.length && this._speed < 0 && this._time > 0) + ) { this._continue() } else { this.pause() @@ -318,14 +326,13 @@ export default class Timeline extends EventTarget { return this } - } registerMethods({ Element: { timeline: function (timeline) { if (timeline == null) { - this._timeline = (this._timeline || new Timeline()) + this._timeline = this._timeline || new Timeline() return this._timeline } else { this._timeline = timeline |