diff options
author | Saivan <savian@me.com> | 2018-05-01 23:06:43 +1000 |
---|---|---|
committer | Saivan <savian@me.com> | 2018-05-01 23:06:43 +1000 |
commit | b5d2b9d1429f01ba992ff6900d9d7a7388ae6dbd (patch) | |
tree | 9d38ee839284ae9e1b4676fb6de1288524f289b4 /src/timeline.js | |
parent | 6d46e412ae2aeb9c8c75896f74496137f0016cc3 (diff) | |
download | svg.js-b5d2b9d1429f01ba992ff6900d9d7a7388ae6dbd.tar.gz svg.js-b5d2b9d1429f01ba992ff6900d9d7a7388ae6dbd.zip |
More work on the timeline and morphables
Diffstat (limited to 'src/timeline.js')
-rw-r--r-- | src/timeline.js | 434 |
1 files changed, 358 insertions, 76 deletions
diff --git a/src/timeline.js b/src/timeline.js index 8af678d..07dca45 100644 --- a/src/timeline.js +++ b/src/timeline.js @@ -17,8 +17,9 @@ function Runner (timeline) { // We copy the current values from the timeline because they can change this._timeline = timeline - this._startTime = timeline._startTime + this._start = timeline._startTime this._duration = timeline._duration + this._last = 0 this._active = false // TODO: Think about looping and how to use the runner @@ -29,7 +30,6 @@ Runner.prototype = { add: function (initFn, runFn, alwaysInitialise) { this.functions.push({ - initialised: false, alwaysInitialise: alwaysInitialise || false, initialiser: initFn, runner: runFn, @@ -39,41 +39,57 @@ Runner.prototype = { step: function (time) { // If it is time to do something, act now. - var end = this._startTime + this._duration - var running = (this._startTime < time && time < end) || !this._duration + var end = this._start + this._duration + var timeInside = this._start < time && time < end + var running = timeInside || !this._duration + var allDone = running - // If its time run the animation, we do so - var allDone = time > end - if (running && !this._timeline._paused) { + // If we don't have a duration, we are in declarative mode - // Get the current position for the current animation - // TODO: Deal with looping - var position = (time - this._startTime) / this._duration + // If the time is inside the bounds, run all of the + if (timeInside) { - // We run all of the functions - for (var i = 0, len = this.functions.length; i < len ; ++i) { + // Work out if we need to do the first initialisation + var rising = this._last < this._start + if (rising) { - // Get the current queued item - var current = this.functions[i] + } - // Work out if we need to initialise, and do so if we do - var initialise = current.alwaysInitialise || !current.initialised - if (initialise) { - current.initialiser(position) - } + } else { + + // Work out if we just finished + var justFinished = this._start < this._last && this._last < end + if (justFinished) { - // Run the function required - // TODO: Figure out what declarative needs that it doesn't have - var stillRunning = current.runner(position) - if (stillRunning) { - allDone = false - } } } - // Tell the caller whether this animation is finished return allDone }, + + initialise: function (time) { + + }, + + run: function (type, time) { + + // We run all of the functions + var stillGoing = false + for (var i = 0, len = this.functions.length; i < len ; ++i) { + + // Get the current queued item + var current = this.functions[i][type] + + // Work out if we need to initialise, and do so if we do + var initialise = current.alwaysInitialise + if (initialise) { + current.initialiser(position) + } + + // Run the functions + + } + }, } @@ -157,9 +173,6 @@ SVG.Timeline = SVG.invent({ }, play () { - -console.log("hello"); - this._paused = false this._continue() return this @@ -204,8 +217,6 @@ console.log("hello"); _step (time) { - -console.log("going", this._paused); // If we are paused, just exit if (this._paused) return @@ -284,48 +295,319 @@ console.log("going", this._paused); } }) -// // Extend the attribute methods separately to avoid cluttering the main -// // Timeline class above -// SVG.extend(SVG.Timeline, { -// -// -// attr: function (a, v) { -// return this.styleAttr('attr', a, v) -// }, -// -// // Add animatable styles -// css: function (s, v) { -// return this.styleAttr('css', s, v) -// }, -// -// styleAttr (type, name, val) { -// // apply attributes individually -// if (typeof name === 'object') { -// for (var key in val) { -// this.styleAttr(type, key, val[key]) -// } -// } -// -// var morpher = new Morphable(this.controller).to(val) -// -// this.queue( -// function () { -// morpher = morpher.from(element[type](name)) -// }, -// function () { -// this.element[type](name, morpher.at(pos)) -// } -// ) -// -// return this -// }, -// -// zoom(level, point) { -// let morpher = SVG.Number(level).controller(this.controller) -// this.queue( -// () => {morpher = morpher.from(element.zoom())}, -// (pos) => {element.zoom(morpher.at(pos), point)} -// ) -// return this -// } -// }) + +// Extend the attribute methods separately to avoid cluttering the main +// Timeline class above +SVG.extend(SVG.Timeline, { + + + attr: function (a, v) { + return this.styleAttr('attr', a, v) + }, + + // Add animatable styles + css: function (s, v) { + return this.styleAttr('css', s, v) + }, + + styleAttr (type, name, val) { + // apply attributes individually + if (typeof name === 'object') { + for (var key in val) { + this.styleAttr(type, key, val[key]) + } + } + + var morpher = new Morphable(this.controller).to(val) + + this.queue( + function () { + morpher = morpher.from(element[type](name)) + }, + function () { + this.element[type](name, morpher.at(pos)) + } + ) + + return this + }, + + zoom: function (level, point) { + var morpher = SVG.Number().to(level).controller(this.controller) + var el = this.target() + + this.queue(function() { + morpher = morpher.from(element.zoom()) + }, function (pos) { + el.zoom(morpher.at(pos), point) + }) + + return this + }, + + /** + ** absolute transformations + **/ + + // M v -----|-----(D M v = I v)------|-----> T v + // + // 1. define the final state (T) and decompose it (once) t = [tx, ty, the, lam, sy, sx] + // 2. on every frame: pull the current state of all previous transforms (M - m can change) + // and then write this as m = [tx0, ty0, the0, lam0, sy0, sx0] + // 3. Find the interpolated matrix I(pos) = m + pos * (t - m) + // - Note I(0) = M + // - Note I(1) = T + // 4. Now you get the delta matrix as a result: D = I * inv(M) + + transform: function (o, relative, affine) { + affine = transforms.affine || affine || !!transform.a + relative = transforms.relative || relative || false + + var morpher + var el = this.target() + + /** + The default of relative is false + affine defaults to true if transformations are used and to false when a matrix is given + + We end up with 4 possibilities: + false, false: absolute direct matrix morph with SVG.Matrix + true, false: relative direct matrix morph with SVG.Marix or relative whatever was passed transformation with ObjectBag + + false, true: absolute affine transformation with SVG.TransformBag + true, true: relative whatever was passed transformation with ObjectBag + **/ + + + // if we have a relative transformation and its not a matrix + // we morph all parameters directly with the ObjectBag + // the following cases are covered here: + // - true, false with ObjectBag + // - true, true with ObjectBag + if(relative && transforms.a == null) { + morpher = SVG.Morphable.ObjectBag(formatTransforms({})) + .to(formatTransforms(transforms)) + .controller(this.controller) + + return this.queue(function() {}, function (pos) { + el.pushRightTransform(new Matrix(morpher.at(pos))) + }) + } + + + // what is left is affine morphing for SVG.Matrix and absolute transformations with TransformBag + // also non affine direct and relative morhing with SVG.Matrix + // the following cases are covered here: + // - false, true with SVG.Matrix + // - false, true with SVG.TransformBag + // - true, false with SVG.Matrix + // - false, false with SVG.Matrix + + // 1. define the final state (T) and decompose it (once) t = [tx, ty, the, lam, sy, sx] + var morpher = (transforms.a && !affine) + ? new SVG.Matrix().to(transforms) + : new SVG.Morphable.TransformBag().to(transforms) + + morpher.controller(this.controller) + + // create identity Matrix for relative not affine Matrix transformation + morpher.from() + + this.queue(function() {}, function (pos) { + + // 2. on every frame: pull the current state of all previous transforms (M - m can change) + var curr = el.currentTransform() + if(!relative) morpher.from(curr) + + // 3. Find the interpolated matrix I(pos) = m + pos * (t - m) + // - Note I(0) = M + // - Note I(1) = T + var matrix = morpher.at(pos) + + if(!relative) { + // 4. Now you get the delta matrix as a result: D = I * inv(M) + var delta = matrix.multiply(curr.inverse()) + el.pushLeftTransform(delta) + } else { + el.pushRightTransform(matrix) + } + }) + + return this + }, + + // Animatable x-axis + x: function (x, relative) { + var morpher = new SVG.Number().to(x) + + /* + if (this.target() instanceof SVG.G) { + this.transform({x: x}, relative) + return this + } + */ + + this.queue(function () { + var from = this._element.x() + morpher.from(from) + if(relative) morpher.to(from + x) + }, function (pos) { + this._element.x(morpher.at(pos)) + }) + + return this + }, + + // Animatable y-axis + y: function (y, relative) { + var morpher = new SVG.Number().to(y) + + /* + if (this.target() instanceof SVG.G) { + this.transform({y: y}, relative) + return this + } + */ + + this.queue(function () { + var from = this._element.y() + morpher.from(from) + if(relative) morpher.to(from + y) + }, function (pos) { + this._element.y(morpher.at(pos)) + }) + + return this + }, + + _queueObject: function (method, to) { + var morpher = new SVG.Morphable(this.controller).to(to) + + this.queue(function () { + morpher.from(this._element[method]()) + }, function () { + this._element[method](morpher.at(pos)) + }) + + return this + }, + + _queueNumber: function (method, value) { + return this._queueObject(method, new Number(value)) + }, + + // Animatable center x-axis + cx: function (x) { + return this._queueNumber('cx', x) + }, + + // Animatable center y-axis + cy: function (y) { + return this._queueNumber('cy', x) + }, + + // Add animatable move + move: function (x, y) { + return this.x(x).y(y) + }, + + // Add animatable center + center: function (x, y) { + return this.cx(x).cy(y) + }, + + // Add animatable size + size: function (width, height) { + // animate bbox based size for all other elements + var box + + if (!width || !height) { + box = this._element().bbox() + } + + if (!width) { + width = box.width / box.height * height + } + + if (!height) { + height = box.height / box.width * width + } + + return this + .width(width) + .height(height) + }, + + // Add animatable width + width: function (width) { + return this._queueNumber('width', width) + }, + + // Add animatable height + height: function (height) { + return this._queueNumber('height', height) + }, + + // Add animatable plot + plot: function (a, b, c, d) { + // Lines can be plotted with 4 arguments + if (arguments.length === 4) { + return this.plot([a, b, c, d]) + } + + return this._queueObject('plot', new this._element.morphArray(a)) + + /*var morpher = this._element.morphArray().to(a) + + this.queue(function () { + morpher.from(this._element.array()) + }, function (pos) { + this._element.plot(morpher.at(pos)) + }) + + return this*/ + }, + + // Add leading method + leading: function (value) { + return this._element.leading + ? this._queueNumber('leading', value) + : this + }, + + // Add animatable viewbox + viewbox: function (x, y, width, height) { + if (this._element instanceof SVG.Container) { + this._queueObject('viewbox', new SVG.Box(x, y, width, height)) + + /*var morpher = new SVG.Box().to(x, y, width, height) + + this.queue(function () { + morpher.from(this._element.viewbox()) + }, function (pos) { + this._element.viewbox(morpher.at(pos)) + }) + + return this*/ + } + + return this + }, + update: function (o) { + if (this._element instanceof SVG.Stop) { + if (typeof o !== 'object') { + return this.update({ + offset: arguments[0], + color: arguments[1], + opacity: arguments[2] + }) + } + + if (o.opacity != null) this.attr('stop-opacity', o.opacity) + if (o.color != null) this.attr('stop-color', o.color) + if (o.offset != null) this.attr('offset', o.offset) + } + + return this + } +}) |