From 8555e13b252f07f8079b08c0b29f4399d389b1e0 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Ulrich-Matthias=20Sch=C3=A4fer?= Date: Fri, 23 Nov 2018 15:06:37 +0100 Subject: [PATCH] fixed lots of bugs in the timeline. Its now working as expected in all cases. - refactor code into functions - handle timeSource correctly - allow for immediate updates - Runners dont know about their delay anymore. Thats far more simple. - Timeline is paused on creation --- dirty.html | 9 +++++ dist/svg.js | 74 ++++++++++++++++++++++----------------- spec/spec/runner.js | 6 ++-- src/animation/Runner.js | 2 +- src/animation/Timeline.js | 65 +++++++++++++++++++--------------- 5 files changed, 90 insertions(+), 66 deletions(-) diff --git a/dirty.html b/dirty.html index 9380cf5..decb180 100644 --- a/dirty.html +++ b/dirty.html @@ -145,6 +145,8 @@ schedule.forEach((s, i) => { // s.runner.during(console.log) }) +// t.play() + var mover = canvas.line(100, 100, 100, 300).attr('stroke', 'black') mover.clone().insertAfter(mover) canvas.line(100, 300, 800, 300).attr('stroke', 'black') @@ -202,6 +204,13 @@ SVG('button[name="reverse"]').on('click', () => { }) +canvas.rect(100, 100).on('click', (e) => { + e.target.instance.animate().move(Math.random()*1000, Math.random()*750).timeline().on('finished', () => { + console.log('rect finished') + }) +}) + + console.log(schedule) // var bla = SVG('').size(0, 0).move(200, 200).addTo('svg') diff --git a/dist/svg.js b/dist/svg.js index 544f337..b55a529 100644 --- a/dist/svg.js +++ b/dist/svg.js @@ -6,7 +6,7 @@ * @copyright Wout Fierens * @license MIT * -* BUILT: Thu Nov 22 2018 15:30:35 GMT+0100 (GMT+01:00) +* BUILT: Fri Nov 23 2018 14:47:42 GMT+0100 (GMT+01:00) */; var SVG = (function () { 'use strict'; @@ -5030,13 +5030,12 @@ var SVG = (function () { _this._timeSource = timeSource; // Store the timing variables _this._startTime = 0; - _this._speed = 1.0; // Play control variables control how the animation proceeds + _this._speed = 1.0; // Determines how long a runner is hold in memory. Can be a dt or true/false - _this._reverse = false; _this._persist = 0; // Keep track of the running animations and their starting parameters _this._nextFrame = null; - _this._paused = false; + _this._paused = true; _this._runners = []; _this._order = []; _this._time = 0; @@ -5055,25 +5054,18 @@ var SVG = (function () { return this._runners.map(makeSchedule).sort(function (a, b) { return a.runner.id - b.runner.id; }); - } - - if (!this.active()) { - this._step(); - - if (when == null) { - when = 'now'; - } } // The start time for the next animation can either be given explicitly, // derived from the current timeline time or it can be relative to the // last start time to chain animations direclty var absoluteStartTime = 0; + var endTime = this.getEndTime(); delay = delay || 0; // Work out when to start the animation if (when == null || when === 'last' || when === 'after') { // Take the last time and increment - absoluteStartTime = this._startTime; + absoluteStartTime = endTime; } else if (when === 'absolute' || when === 'start') { absoluteStartTime = delay; delay = 0; @@ -5092,22 +5084,19 @@ var SVG = (function () { runner.unschedule(); - runner.timeline(this); - runner.time(-delay); // Save startTime for next runner - - this._startTime = absoluteStartTime + runner.duration() + delay; // Save runnerInfo + runner.timeline(this); // runner.time(-delay) + // Save runnerInfo this._runners[runner.id] = { persist: this.persist(), runner: runner, - start: absoluteStartTime // + delay - // Save order and continue + start: absoluteStartTime + delay // Save order, update Time if needed and continue }; this._order.push(runner.id); - this._continue(); + this.updateTime()._continue(); return this; } // Remove the runner from this timeline @@ -5123,6 +5112,25 @@ var SVG = (function () { this._order.splice(index, 1); runner.timeline(null); + return this; + } // Calculates the end of the timeline + + }, { + key: "getEndTime", + value: function getEndTime() { + var lastRunnerInfo = this._runners[this._order[this._order.length - 1]]; + var lastDuration = lastRunnerInfo ? lastRunnerInfo.runner.duration() : 0; + var lastStartTime = lastRunnerInfo ? lastRunnerInfo.start : 0; + return lastStartTime + lastDuration; + } // Makes sure, that after pausing the time doesn't jump + + }, { + key: "updateTime", + value: function updateTime() { + if (!this.active()) { + this._lastSourceTime = this._timeSource(); + } + return this; } }, { @@ -5130,8 +5138,7 @@ 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(); + return this.updateTime()._continue(); } }, { key: "pause", @@ -5143,14 +5150,14 @@ var SVG = (function () { key: "stop", value: function stop() { // Go to start and pause - this.seek(-this._time); + this.time(0); return this.pause(); } }, { key: "finish", value: function finish() { - // Go to Infinity and pause - this.seek(Infinity); + // Go to end and pause + this.time(this.getEndTime() + 1); return this.pause(); } }, { @@ -5171,16 +5178,14 @@ var SVG = (function () { }, { key: "seek", value: function seek(dt) { - this._time += dt; // this._lastStepTime = this._time - - return this._continue(true); + return this.time(this._time + dt); } }, { key: "time", value: function time(_time) { if (_time == null) return this._time; this._time = _time; - return this; + return this._continue(true); } }, { key: "persist", @@ -5213,6 +5218,7 @@ var SVG = (function () { if (!immediateStep) { // Update the time this._time += dtTime; + this._time = this._time < 0 ? 0 : this._time; } this._lastStepTime = this._time; @@ -5259,13 +5265,15 @@ var SVG = (function () { runner.timeline(null); } } - } + } // Basically: we continue when there are runners right from us in time + // when -->, and when runners are left from us when <-- + - if (runnersLeft) { + if (runnersLeft && !(this._speed < 0 && this._time === 0) || this._order.length && this._speed < 0 && this._time > 0) { this._continue(); } else { this.fire('finished'); - this._nextFrame = null; + this.pause(); } return this; @@ -5915,7 +5923,7 @@ var SVG = (function () { animate: function animate(duration, delay, when) { var o = Runner.sanitise(duration, delay, when); var timeline$$1 = this.timeline(); - return new Runner(o.duration).loop(o).element(this).timeline(timeline$$1).schedule(delay, when); + return new Runner(o.duration).loop(o).element(this).timeline(timeline$$1.play()).schedule(delay, when); }, delay: function delay(by, when) { return this.animate(0, by, when); diff --git a/spec/spec/runner.js b/spec/spec/runner.js index 0f231a3..ff9df7b 100644 --- a/spec/spec/runner.js +++ b/spec/spec/runner.js @@ -742,11 +742,11 @@ describe('SVG.Runner', function () { var t = timeline.time() expect(runner2.timeline()).toBe(timeline) - expect(runner2.time()).toBe(-1000) + expect(runner2.time()).toBe(0) - expect(timeline.schedule()).toEqual(jasmine.objectContaining([ + expect(timeline.schedule()).toEqual(jasmine.arrayContaining([ jasmine.objectContaining({start: t, duration: 1000, end: t+1000, runner: runner}), - jasmine.objectContaining({start: t+1000, duration: 500, end: t+1500, runner: runner2}) + jasmine.objectContaining({start: t+2000, duration: 500, end: t+2500, runner: runner2}) ])) }) }) diff --git a/src/animation/Runner.js b/src/animation/Runner.js index 63c95d6..5551162 100644 --- a/src/animation/Runner.js +++ b/src/animation/Runner.js @@ -578,7 +578,7 @@ registerMethods({ return new Runner(o.duration) .loop(o) .element(this) - .timeline(timeline) + .timeline(timeline.play()) .schedule(delay, when) }, diff --git a/src/animation/Timeline.js b/src/animation/Timeline.js index bafd319..c3ad07c 100644 --- a/src/animation/Timeline.js +++ b/src/animation/Timeline.js @@ -26,13 +26,12 @@ export default class Timeline extends EventTarget { this._startTime = 0 this._speed = 1.0 - // Play control variables control how the animation proceeds - this._reverse = false + // Determines how long a runner is hold in memory. Can be a dt or true/false this._persist = 0 // Keep track of the running animations and their starting parameters this._nextFrame = null - this._paused = false + this._paused = true this._runners = [] this._order = [] this._time = 0 @@ -51,23 +50,18 @@ export default class Timeline extends EventTarget { }) } - if (!this.active()) { - this._step() - if (when == null) { - when = 'now' - } - } - // The start time for the next animation can either be given explicitly, // derived from the current timeline time or it can be relative to the // last start time to chain animations direclty + var absoluteStartTime = 0 + var endTime = this.getEndTime() delay = delay || 0 // Work out when to start the animation if (when == null || when === 'last' || when === 'after') { // Take the last time and increment - absoluteStartTime = this._startTime + absoluteStartTime = endTime } else if (when === 'absolute' || when === 'start') { absoluteStartTime = delay delay = 0 @@ -86,21 +80,18 @@ export default class Timeline extends EventTarget { // Manage runner runner.unschedule() runner.timeline(this) - runner.time(-delay) - - // Save startTime for next runner - this._startTime = absoluteStartTime + runner.duration() + delay + // runner.time(-delay) // Save runnerInfo this._runners[runner.id] = { persist: this.persist(), runner: runner, - start: absoluteStartTime// + delay + start: absoluteStartTime + delay } - // Save order and continue + // Save order, update Time if needed and continue this._order.push(runner.id) - this._continue() + this.updateTime()._continue() return this } @@ -115,11 +106,26 @@ export default class Timeline extends EventTarget { return this } + // Calculates the end of the timeline + getEndTime () { + var lastRunnerInfo = this._runners[this._order[this._order.length - 1]] + var lastDuration = lastRunnerInfo ? lastRunnerInfo.runner.duration() : 0 + var lastStartTime = lastRunnerInfo ? lastRunnerInfo.start : 0 + return lastStartTime + lastDuration + } + + // Makes sure, that after pausing the time doesn't jump + updateTime () { + if (!this.active()) { + this._lastSourceTime = this._timeSource() + } + return this + } + play () { // Now make sure we are not paused and continue the animation this._paused = false - this._lastSourceTime = this._timeSource() - return this._continue() + return this.updateTime()._continue() } pause () { @@ -129,13 +135,13 @@ export default class Timeline extends EventTarget { stop () { // Go to start and pause - this.seek(-this._time) + this.time(0) return this.pause() } finish () { - // Go to Infinity and pause - this.seek(Infinity) + // Go to end and pause + this.time(this.getEndTime() + 1) return this.pause() } @@ -154,15 +160,13 @@ export default class Timeline extends EventTarget { } seek (dt) { - this._time += dt - // this._lastStepTime = this._time - return this._continue(true) + return this.time(this._time + dt) } time (time) { if (time == null) return this._time this._time = time - return this + return this._continue(true) } persist (dtOrForever) { @@ -192,6 +196,7 @@ export default class Timeline extends EventTarget { if (!immediateStep) { // Update the time this._time += dtTime + this._time = this._time < 0 ? 0 : this._time } this._lastStepTime = this._time this.fire('time', this._time) @@ -244,11 +249,13 @@ export default class Timeline extends EventTarget { } } - if (runnersLeft) { + // 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._order.length && this._speed < 0 && this._time > 0)) { this._continue() } else { this.fire('finished') - this._nextFrame = null + this.pause() } return this -- 2.39.5