From d5c01a7848154d240f434b38d802fdb65727fd7f Mon Sep 17 00:00:00 2001 From: =?utf8?q?Ulrich-Matthias=20Sch=C3=A4fer?= Date: Thu, 22 Nov 2018 15:56:39 +0100 Subject: [PATCH] fix Timeline so that play, pause, stop, reverse... work correctly. Update runners accordingly --- dirty.html | 68 ++++++++++++++++++++++++++++++++-- dist/svg.js | 78 ++++++++++++++++++++++++++------------- src/animation/Animator.js | 4 +- src/animation/Runner.js | 14 +++++++ src/animation/Timeline.js | 54 +++++++++++++++++---------- 5 files changed, 168 insertions(+), 50 deletions(-) diff --git a/dirty.html b/dirty.html index 712c13c..9380cf5 100644 --- a/dirty.html +++ b/dirty.html @@ -14,8 +14,21 @@ -
-
+ + + + + + + + + + + + + + @@ -109,7 +122,8 @@ canvas.attr('viewBox', null) SVG.defaults.timeline.ease = '-' var r = new SVG.Runner(1000) -var t = new SVG.Timeline() +var t = new SVG.Timeline().persist(true) + r.schedule(t, 200) .animate(500).loop(5, true, 100) .animate(600, 200, 'absolute') @@ -135,8 +149,56 @@ var mover = canvas.line(100, 100, 100, 300).attr('stroke', 'black') mover.clone().insertAfter(mover) canvas.line(100, 300, 800, 300).attr('stroke', 'black') +const text = SVG('#displayText') + t.on('time', function (e) { mover.x(100 + e.detail/10) + text.node.textContent = e.detail +}) + +t.on('finished', () => { + console.log('finished') +}) + +SVG('button[name="back100"]').on('click', () => { + t.seek(-100) +}) +SVG('button[name="back1000"]').on('click', () => { + t.seek(-1000) +}) +SVG('button[name="forth100"]').on('click', () => { + t.seek(100) +}) +SVG('button[name="forth1000"]').on('click', () => { + t.seek(1000) +}) + +SVG('button[name="speed2"]').on('click', () => { + t.speed(2) +}) + +SVG('button[name="speed05"]').on('click', () => { + t.speed(0.5) +}) + +SVG('button[name="pause"]').on('click', () => { + t.pause() +}) + +SVG('button[name="play"]').on('click', () => { + t.play() +}) + +SVG('button[name="stop"]').on('click', () => { + t.stop() +}) + +SVG('button[name="finish"]').on('click', () => { + t.finish() +}) + +SVG('button[name="reverse"]').on('click', () => { + t.reverse() }) diff --git a/dist/svg.js b/dist/svg.js index 5407a1a..544f337 100644 --- a/dist/svg.js +++ b/dist/svg.js @@ -6,7 +6,7 @@ * @copyright Wout Fierens * @license MIT * -* BUILT: Wed Nov 21 2018 22:04:17 GMT+0100 (GMT+01:00) +* BUILT: Thu Nov 22 2018 15:30:35 GMT+0100 (GMT+01:00) */; var SVG = (function () { 'use strict'; @@ -4957,10 +4957,10 @@ var SVG = (function () { return node; }, cancelFrame: function cancelFrame(node) { - Animator.frames.remove(node); + node != null && Animator.frames.remove(node); }, clearTimeout: function clearTimeout(node) { - Animator.timeouts.remove(node); + node != null && Animator.timeouts.remove(node); }, _draw: function _draw(now) { // Run all the timeouts we can run, if they are not ready yet, add them @@ -5041,7 +5041,9 @@ var SVG = (function () { _this._order = []; _this._time = 0; _this._lastSourceTime = 0; - _this._lastStepTime = 0; + _this._lastStepTime = 0; // Make sure that step is always called in class context + + _this._step = _this._step.bind(_assertThisInitialized(_assertThisInitialized(_this))); return _this; } // schedules a runner on the timeline @@ -5098,7 +5100,8 @@ var SVG = (function () { this._runners[runner.id] = { persist: this.persist(), runner: runner, - start: absoluteStartTime // Save order and continue + start: absoluteStartTime // + delay + // Save order and continue }; @@ -5127,26 +5130,26 @@ var SVG = (function () { value: function play() { // Now make sure we are not paused and continue the animation this._paused = false; + this._lastSourceTime = this._timeSource(); return this._continue(); } }, { key: "pause", value: function pause() { - // Cancel the next animation frame and pause - this._nextFrame = null; this._paused = true; - return this; + return this._continue(); } }, { key: "stop", value: function stop() { - // Cancel the next animation frame and go to start + // Go to start and pause this.seek(-this._time); return this.pause(); } }, { key: "finish", value: function finish() { + // Go to Infinity and pause this.seek(Infinity); return this.pause(); } @@ -5168,8 +5171,9 @@ var SVG = (function () { }, { key: "seek", value: function seek(dt) { - this._time += dt; - return this._continue(); + this._time += dt; // this._lastStepTime = this._time + + return this._continue(true); } }, { key: "time", @@ -5195,16 +5199,22 @@ var SVG = (function () { }, { key: "_step", value: function _step() { - // If the timeline is paused, just do nothing - if (this._paused) return; // Get the time delta from the last time and update the time + var immediateStep = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; + // Get the time delta from the last time and update the time var time = this._timeSource(); var dtSource = time - this._lastSourceTime; + if (immediateStep) dtSource = 0; var dtTime = this._speed * dtSource + (this._time - this._lastStepTime); - this._lastSourceTime = time; // Update the time + this._lastSourceTime = time; // Only update the time if we use the timeSource. + // Otherwise use the current time + + if (!immediateStep) { + // Update the time + this._time += dtTime; + } - this._time += dtTime; this._lastStepTime = this._time; this.fire('time', this._time); // Run all of the runners directly @@ -5219,8 +5229,12 @@ var SVG = (function () { var dtToStart = this._time - runnerInfo.start; // Dont run runner if not started yet - if (dtToStart < 0) { - runnersLeft = true; + if (dtToStart <= 0) { + runnersLeft = true; // This is for the case that teh timeline was seeked so that the time + // is now before the startTime of the runner. Thats why we need to set + // the runner to position 0 + + runner.reset(); continue; } else if (dtToStart < dt) { // Adjust dt to make sure that animation is on point @@ -5245,11 +5259,10 @@ var SVG = (function () { runner.timeline(null); } } - } // Get the next animation frame to keep the simulation going - + } if (runnersLeft) { - this._nextFrame = Animator.frame(this._step.bind(this)); + this._continue(); } else { this.fire('finished'); this._nextFrame = null; @@ -5261,12 +5274,12 @@ var SVG = (function () { }, { key: "_continue", value: function _continue() { + var immediateStep = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; + Animator.cancelFrame(this._nextFrame); + this._nextFrame = null; + if (immediateStep) return this._step(true); if (this._paused) return this; - - if (!this._nextFrame) { - this._nextFrame = Animator.frame(this._step.bind(this)); - } - + this._nextFrame = Animator.frame(this._step); return this; } }, { @@ -5318,7 +5331,9 @@ var SVG = (function () { _this.enabled = true; _this._time = 0; - _this._last = 0; // Save transforms applied to this runner + _this._last = 0; // At creation, the runner is in reseted state + + _this._reseted = true; // Save transforms applied to this runner _this.transforms = new Matrix(); _this.transformId = 1; // Looping variables @@ -5563,6 +5578,9 @@ var SVG = (function () { this.done = !declarative && !justFinished && this._time >= duration; // Call initialise and the run function if (running || declarative) { + // Runner is running. So its not in reseted state anymore + this._reseted = false; + this._initialise(running); // clear the transforms on this runner so they dont get added again and again @@ -5584,6 +5602,14 @@ var SVG = (function () { return this; } }, { + key: "reset", + value: function reset() { + if (this._reseted) return this; + this.loops(0); + this._reseted = true; + return this; + } + }, { key: "finish", value: function finish() { return this.step(Infinity); diff --git a/src/animation/Animator.js b/src/animation/Animator.js index 9b460f5..1392daa 100644 --- a/src/animation/Animator.js +++ b/src/animation/Animator.js @@ -43,11 +43,11 @@ const Animator = { }, cancelFrame (node) { - Animator.frames.remove(node) + node != null && Animator.frames.remove(node) }, clearTimeout (node) { - Animator.timeouts.remove(node) + node != null && Animator.timeouts.remove(node) }, _draw (now) { diff --git a/src/animation/Runner.js b/src/animation/Runner.js index 47929fd..63c95d6 100644 --- a/src/animation/Runner.js +++ b/src/animation/Runner.js @@ -50,6 +50,9 @@ export default class Runner extends EventTarget { this._time = 0 this._last = 0 + // At creation, the runner is in reseted state + this._reseted = true + // Save transforms applied to this runner this.transforms = new Matrix() this.transformId = 1 @@ -276,11 +279,15 @@ export default class Runner extends EventTarget { // Call initialise and the run function if (running || declarative) { + // Runner is running. So its not in reseted state anymore + this._reseted = false + this._initialise(running) // clear the transforms on this runner so they dont get added again and again this.transforms = new Matrix() var converged = this._run(declarative ? dt : position) + this.fire('step', this) } // correct the done flag here @@ -292,6 +299,13 @@ export default class Runner extends EventTarget { return this } + reset () { + if (this._reseted) return this + this.loops(0) + this._reseted = true + return this + } + finish () { return this.step(Infinity) } diff --git a/src/animation/Timeline.js b/src/animation/Timeline.js index a0d8069..bafd319 100644 --- a/src/animation/Timeline.js +++ b/src/animation/Timeline.js @@ -38,6 +38,9 @@ export default class Timeline extends EventTarget { this._time = 0 this._lastSourceTime = 0 this._lastStepTime = 0 + + // Make sure that step is always called in class context + this._step = this._step.bind(this) } // schedules a runner on the timeline @@ -92,7 +95,7 @@ export default class Timeline extends EventTarget { this._runners[runner.id] = { persist: this.persist(), runner: runner, - start: absoluteStartTime + start: absoluteStartTime// + delay } // Save order and continue @@ -115,23 +118,23 @@ export default class Timeline extends EventTarget { play () { // Now make sure we are not paused and continue the animation this._paused = false + this._lastSourceTime = this._timeSource() return this._continue() } pause () { - // Cancel the next animation frame and pause - this._nextFrame = null this._paused = true - return this + return this._continue() } stop () { - // Cancel the next animation frame and go to start + // Go to start and pause this.seek(-this._time) return this.pause() } finish () { + // Go to Infinity and pause this.seek(Infinity) return this.pause() } @@ -152,7 +155,8 @@ export default class Timeline extends EventTarget { seek (dt) { this._time += dt - return this._continue() + // this._lastStepTime = this._time + return this._continue(true) } time (time) { @@ -173,18 +177,22 @@ export default class Timeline extends EventTarget { return this } - _step () { - // If the timeline is paused, just do nothing - if (this._paused) return - + _step (immediateStep = false) { // Get the time delta from the last time and update the time var time = this._timeSource() var dtSource = time - this._lastSourceTime + + if (immediateStep) dtSource = 0 + var dtTime = this._speed * dtSource + (this._time - this._lastStepTime) this._lastSourceTime = time - // Update the time - this._time += dtTime + // Only update the time if we use the timeSource. + // Otherwise use the current time + if (!immediateStep) { + // Update the time + this._time += dtTime + } this._lastStepTime = this._time this.fire('time', this._time) @@ -201,8 +209,13 @@ export default class Timeline extends EventTarget { let dtToStart = this._time - runnerInfo.start // Dont run runner if not started yet - if (dtToStart < 0) { + if (dtToStart <= 0) { runnersLeft = true + + // This is for the case that teh timeline was seeked so that the time + // is now before the startTime of the runner. Thats why we need to set + // the runner to position 0 + runner.reset() continue } else if (dtToStart < dt) { // Adjust dt to make sure that animation is on point @@ -231,22 +244,25 @@ export default class Timeline extends EventTarget { } } - // Get the next animation frame to keep the simulation going if (runnersLeft) { - this._nextFrame = Animator.frame(this._step.bind(this)) + this._continue() } else { this.fire('finished') this._nextFrame = null } + return this } // Checks if we are running and continues the animation - _continue () { + _continue (immediateStep = false) { + Animator.cancelFrame(this._nextFrame) + this._nextFrame = null + + if (immediateStep) return this._step(true) if (this._paused) return this - if (!this._nextFrame) { - this._nextFrame = Animator.frame(this._step.bind(this)) - } + + this._nextFrame = Animator.frame(this._step) return this } -- 2.39.5