diff options
author | Ulrich-Matthias Schäfer <ulima.ums@googlemail.com> | 2018-05-24 23:42:01 +0200 |
---|---|---|
committer | Ulrich-Matthias Schäfer <ulima.ums@googlemail.com> | 2018-05-24 23:42:01 +0200 |
commit | 4d39baa3e4191c70d3eb7c9b67704b29f79a812d (patch) | |
tree | 36540bcb48b8e4be66fe8332b0502eb3d1218815 | |
parent | ebf2a8bb395b78b5bc513fb1ec17e1222dcb0f1d (diff) | |
download | svg.js-4d39baa3e4191c70d3eb7c9b67704b29f79a812d.tar.gz svg.js-4d39baa3e4191c70d3eb7c9b67704b29f79a812d.zip |
fixed some more errores, make declaritive controllers partially work. added PID controller for testing reasons
-rw-r--r-- | dirty.html | 46 | ||||
-rw-r--r-- | spec/spec/runner.js | 20 | ||||
-rw-r--r-- | src/controller.js | 35 | ||||
-rw-r--r-- | src/morph.js | 7 | ||||
-rw-r--r-- | src/runner.js | 42 | ||||
-rw-r--r-- | src/timeline.js | 39 |
6 files changed, 112 insertions, 77 deletions
@@ -55,26 +55,32 @@ function getColor(t) { -for (let i = 0 ; i < 15; i++) { - for (let j = 0 ; j < 10; j++) { - - // Make the rect - let o = i + j - let rect = SVG('rect').clone().show() - .width(40).height(40) - .x(50 * i).y(50 * j) - .attr('fill', getColor(o * 0.1)) - - // Move the rect - let {cx, cy} = rect.bbox() - - // Animate the rect - rect.animate(3000, Math.random() * 2000) - .during(t => rect.transform({rotate: 720 * t, origin: [cx, cy]})) - .during(t => rect.attr('fill', getColor(o * 0.1 + t))) - } -} - +// for (let i = 0 ; i < 15; i++) { +// for (let j = 0 ; j < 10; j++) { +// +// // Make the rect +// let o = i + j +// let rect = SVG('rect').clone().show() +// .width(40).height(40) +// .x(50 * i).y(50 * j) +// .attr('fill', getColor(o * 0.1)) +// +// // Move the rect +// let {cx, cy} = rect.bbox() +// +// // Animate the rect +// rect.animate(3000, Math.random() * 2000) +// .during(t => rect.transform({rotate: 720 * t, origin: [cx, cy]})) +// .during(t => rect.attr('fill', getColor(o * 0.1 + t))) +// } +// } + +var mover = SVG('rect').clone().show() +mover.animate(SVG.PID()).move(500, 500) + +// SVG.on(document, 'click', function (e) { +// console.log(mover.animate(SVG.Spring(300, 30)).move(e.pageX, e.pageY)) +// }) </script> diff --git a/spec/spec/runner.js b/spec/spec/runner.js index b09cd43..ce64c23 100644 --- a/spec/spec/runner.js +++ b/spec/spec/runner.js @@ -85,7 +85,6 @@ describe('SVG.Runner', function () { }) describe('tag()', function () { - it('acts as a getter', function () { var runner = new SVG.Runner() @@ -108,6 +107,25 @@ describe('SVG.Runner', function () { }) }) + describe('untag()', function () { + it('untags with a string given', function () { + var runner = new SVG.Runner() + + runner.tag('foo') + runner.untag('foo') + expect(runner.tags).toEqual(jasmine.objectContaining({})) + }) + + it('untags multiple tags with an array given', function () { + var runner = new SVG.Runner() + + runner.tag(['foo', 'bar', 'baz']) + runner.untag(['bar', 'baz']) + expect(runner.tags).toEqual(jasmine.objectContaining({foo: true})) + }) + }) + + describe('step()', function () { it('calls initFn once and runFn at every step when alwaysInitialise is false', function() { diff --git a/src/controller.js b/src/controller.js index fdb26f7..f6fab93 100644 --- a/src/controller.js +++ b/src/controller.js @@ -81,16 +81,21 @@ SVG.Controller = SVG.invent ({ create: function (fn) { SVG.Stepper.call(this, fn) + this.stepper = fn }, extend: { step: function (current, target, dt, c) { - + return this.stepper(current, target, dt, c) }, isComplete: function (dt, c) { - + var result = false + for(var i = c.length; i--;) { + result = result || (Math.abs(c[i].error) < 0.01) + } + return result }, }, }) @@ -129,3 +134,29 @@ SVG.Spring = function spring(duration, overshoot) { return newPosition }) } + +SVG.PID = function (P, I, D, antiwindup) { + P = P == null ? 0.1 : P + I = I == null ? 0.01 : I + D = D == null ? 0 : D + antiwindup = antiwindup == null ? 1000 : antiwindup + + // Return the acceleration required + return new SVG.Controller( + function (current, target, dt, c) { + + if(dt == Infinity) return target + + var p = target - current + var i = (c.integral || 0) + p * dt + var d = (p - (c.error || 0)) / dt + + // antiwindup + i = Math.max(-antiwindup, Math.min(i, antiwindup)) + + c.error = p + c.integral = i + + return current + (P * p + I * i + D * d) + }) +} diff --git a/src/morph.js b/src/morph.js index c6e2152..8bb6fbd 100644 --- a/src/morph.js +++ b/src/morph.js @@ -72,10 +72,9 @@ SVG.Morphable = SVG.invent({ } else { this.type(SVG.Morphable.NonMorphable) - } - } else if (value in SVG.MorphableTypes) { + } else if (SVG.MorphableTypes.indexOf(value.constructor) > -1) { this.type(value.constructor) } else if (Array.isArray(value)) { @@ -102,7 +101,7 @@ SVG.Morphable = SVG.invent({ // FIXME: we can call this._stepper.isComplete directly // no need for this wrapper here isComplete: function () { - return this._stepper && this._stepper.isComplete() + return this._stepper && this._stepper.isComplete(null, this._context) }, at: function (pos) { @@ -115,7 +114,7 @@ SVG.Morphable = SVG.invent({ return this._type.prototype.fromArray( this.modifier( this._from.map(function (i, index) { - return _this._stepper.step(i, _this._to[index], pos, _this._context[i]) + return _this._stepper.step(i, _this._to[index], pos, _this._context[index]) }) ) ) diff --git a/src/runner.js b/src/runner.js index 9ed5bbb..718c5c8 100644 --- a/src/runner.js +++ b/src/runner.js @@ -27,14 +27,14 @@ SVG.Runner = SVG.invent({ // Work out the stepper and the duration this._duration = typeof options === 'number' && options - this._isDeclaritive = options instanceof SVG.Controller - this._stepper = this._isDeclaritive ? options : new SVG.Ease() + this._isDeclarative = options instanceof SVG.Controller + this._stepper = this._isDeclarative ? options : new SVG.Ease() // We copy the current values from the timeline because they can change this._morphers = {} // Store the state of the runner - this._enabled = true + this.enabled = true this._time = 0 this._last = 0 this.tags = {} @@ -50,7 +50,7 @@ SVG.Runner = SVG.invent({ var waits = [] // If we have an object, unpack the values - if (typeof duration == 'object') { + if (typeof duration == 'object' && !(duration instanceof SVG.Stepper)) { delay = duration.delay || 0 when = duration.when || 'now' duration = duration.duration || 1000 @@ -169,19 +169,25 @@ SVG.Runner = SVG.invent({ step: function (dt) { + // FIXME: It makes more sense to have this in the timeline + // because the user should still ne able to step a runner + // even if disabled + // Don't bother running when not enabled + if(!this.enabled) return false + // If there is no duration, we are in declarative mode and dt has to be // positive always, so if its negative, we ignore it. - if ( this._isDeclarative && dt < 0 ) return false + if (this._isDeclarative && dt < 0) return false // Increment the time and read out the parameters var duration = this._duration - this._time += dt || 16 + this._time += dt || 16 // FIXME: step(0) is valid but will get changed to 16 here var time = this._time // Work out if we are in range to run the function var timeInside = 0 <= time && time <= duration var position = time / duration - var finished = time >= duration + var finished = !this._isDeclarative && time >= duration // TODO: clean this up. finished returns true even for declarative if we do not check for it explicitly // If we are on the rising edge, initialise everything, otherwise, // initialise only what needs to be initialised on the rising edge @@ -192,16 +198,16 @@ SVG.Runner = SVG.invent({ this._last = time // If we haven't started yet or we are over the time, just exit - if(!timeInside && !justFinished) return finished + if(!this._isDeclarative && !timeInside && !justFinished) return finished // TODO: same as above // Run the runner and store the last time it was run finished = this._run( - duration === null ? dt // No duration, declarative + this._isDeclarative ? dt // No duration, declarative : finished ? 1 // If completed, provide 1 : position // If running, ) || finished - // FIXME: for the sake of unifirmity this method should return This + // FIXME: for the sake of conformity this method should return this // we can then add a functon isFinished to see if a runner is finished // Work out if we are finished return finished @@ -225,8 +231,8 @@ SVG.Runner = SVG.invent({ }, active: function (enabled) { - if(enabled == null) return this._enabled - this._enabled = enabled + if(enabled == null) return this.enabled + this.enabled = enabled return this }, @@ -249,7 +255,12 @@ SVG.Runner = SVG.invent({ }, untag: function (name) { + name = Array.isArray(name) ? name : [name] + for(var i = name.length; i--;) { + delete this.tags[name[i]] + } + return this }, /* @@ -288,6 +299,10 @@ SVG.Runner = SVG.invent({ // Run each run function for the position given _run: function (position) { + // TODO: review this one + // Make sure to keep runner running when no functions where added yet + if(!this._functions.length) return false + // Run all of the _functions directly var allfinished = false for (var i = 0, len = this._functions.length; i < len ; ++i) { @@ -313,7 +328,6 @@ SVG.Runner = SVG.invent({ // Timeline class above SVG.extend(SVG.Runner, { - attr: function (a, v) { return this.styleAttr('attr', a, v) }, @@ -497,8 +511,10 @@ SVG.extend(SVG.Runner, { // Make a morpher and queue the animation var morpher = new SVG.Morphable(this._stepper).to(to) this.queue(function () { + console.log('init') morpher.from(this[method]()) }, function (pos) { + console.log('run', pos) this[method](morpher.at(pos)) return morpher.isComplete() }, this._isDeclarative) diff --git a/src/timeline.js b/src/timeline.js index 5bdcb55..9ed6b74 100644 --- a/src/timeline.js +++ b/src/timeline.js @@ -132,6 +132,7 @@ SVG.Timeline = SVG.invent({ // derived from the current timeline time or it can be relative to the // last start time to chain animations direclty var absoluteStartTime + delay = delay || 0 // Work out when to start the animation if ( when == null || when === 'last' || when === 'relative' ) { @@ -154,43 +155,6 @@ SVG.Timeline = SVG.invent({ this._step() return this - - - - // Clear the controller and the looping parameters - this._controller = duration instanceof Function ? duration : null - this._backwards = false - this._swing = false - this._loops = 0 - - // If we have an object we are declaring imperative animations - if (typeof duration === 'object') { - duration = duration.duration - delay = duration.delay - nowOrAbsolute = duration.absolute || duration.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 = typeof nowOrAbsolute === 'number' ? nowOrAbsolute - : nowOrAbsolute ? this._time - : this._startTime + this._duration - - // We start the next animation after the delay required - this._startTime = absoluteStartTime + (delay || 0) - this._duration = duration instanceof Function ? null - : (duration || SVG.defaults.timeline.duration) - - // Make a new runner to queue all of the animations onto - this._runner = new Runner(this._time - this._startTime, this.duration) - this._runners.push(this._runner) - - // Step the animation - this._step() - - // Allow for chaining - return this }, play () { @@ -286,6 +250,7 @@ SVG.Timeline = SVG.invent({ // Get and run the current runner and figure out if its done running var runner = this._runners[i] + var finished = runner.step(dt) // If this runner is still going, signal that we need another animation |