aboutsummaryrefslogtreecommitdiffstats
path: root/src/timeline.js
diff options
context:
space:
mode:
authorSaivan <savian@me.com>2018-05-01 23:06:43 +1000
committerSaivan <savian@me.com>2018-05-01 23:06:43 +1000
commitb5d2b9d1429f01ba992ff6900d9d7a7388ae6dbd (patch)
tree9d38ee839284ae9e1b4676fb6de1288524f289b4 /src/timeline.js
parent6d46e412ae2aeb9c8c75896f74496137f0016cc3 (diff)
downloadsvg.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.js434
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
+ }
+})