diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/morph.js | 112 | ||||
-rw-r--r-- | src/runner.js | 188 |
2 files changed, 109 insertions, 191 deletions
diff --git a/src/morph.js b/src/morph.js index 18ed6b5..6a9e651 100644 --- a/src/morph.js +++ b/src/morph.js @@ -8,7 +8,6 @@ SVG.Morphable = SVG.invent({ this._to = null this._type = null this._context = null - this.modifier = function(arr) { return arr } this._morphObj = null }, @@ -106,21 +105,11 @@ SVG.Morphable = SVG.invent({ at: function (pos) { var _this = this - // for(var i = 0, len = this._from.length; i < len; ++i) { - // arr.push(this.stepper(this._from[i], this._to[i])) - // } - return this._morphObj.fromArray( - this.modifier( - this._from.map(function (i, index) { - return _this._stepper.step(i, _this._to[index], pos, _this._context[index], _this._context) - }) - ) + this._from.map(function (i, index) { + return _this._stepper.step(i, _this._to[index], pos, _this._context[index], _this._context) + }) ) - }, - - valueOf: function () { - return this._value } } }) @@ -142,6 +131,42 @@ SVG.Morphable.NonMorphable = SVG.invent({ } }) +SVG.Morphable.TransformBag2 = SVG.invent({ + create: function (obj) { + if(Array.isArray(obj)) { + obj = { + scaleX: obj[0], + scaleY: obj[1], + shear: obj[2], + rotate: obj[3], + translateX: obj[4], + translateY: obj[5], + originX: obj[6], + originY: obj[7] + } + } + + Object.assign(this, obj) + }, + + extend: { + toArray: function (){ + var v = this + + return [ + v.scaleX, + v.scaleY, + v.shear, + v.rotate, + v.translateX, + v.translateY, + v.originX, + v.originY, + ] + } + } +}) + SVG.Morphable.TransformBag = SVG.invent({ inherit: SVG.Matrix, create: function (obj) { @@ -239,6 +264,7 @@ SVG.MorphableTypes = [ SVG.PathArray, SVG.Morphable.NonMorphable, SVG.Morphable.TransformBag, + SVG.Morphable.TransformBag2, SVG.Morphable.ObjectBag, ] @@ -256,29 +282,6 @@ SVG.extend(SVG.MorphableTypes, { }) -// animate().ease(function(pos) { return pos}) -// function Ease (func) { -// return function eased (fromOrCurr, to, pos) { -// return fromOrCurr + func(pos) * (to - fromOrCurr) // normal easing -// } -// } - - -/// -/// el.animate() -/// .fill('#00f') -/// ---->> timeline.fill -/// val = new Morphable().to('#0ff').stepper(stepper) -/// func init() { -/// val.from(el.fill()) -/// } -/// func run (pos) { -/// curr = val.at(pos) -/// el.fill(curr) -/// } -/// this.queue(init, run) - - // - Objects are just variable bags @@ -304,42 +307,7 @@ SVG.extend(SVG.MorphableTypes, { - -// C R x = D C x = A x -// -// (C R inv(C)) C x -// -// -// C R = D C -// D = C R inv(C) - - -/* -absolute -> start at current - {affine params} -relative -> start at 0 always - {random stuff} -*/ - - - - - /** - INIT - - save the current transformation - - ELEMENT TIMELINE (one timeline per el) - - Reads the current transform and save it to the transformation stack - - Runs all available runners, runners will: - - Modify their transformation on the stack - - Mark their transformation as complete - - After each runner, we group the matrix (not for now) - - After running the runners, we bundle all contiguous transformations into - a single transformation - - - - transformtionstack is like this: [RunnerB, Matrix, RunnerC] - - skip merging for now (premature blabla) - el.loop({times: 5, swing: true, wait: [20, 50]}) diff --git a/src/runner.js b/src/runner.js index da494d3..ceb42fe 100644 --- a/src/runner.js +++ b/src/runner.js @@ -160,7 +160,7 @@ SVG.Runner = SVG.invent({ this._queue.push({ initialiser: initFn || SVG.void, runner: runFn || SVG.void, - isTransform: !!isTransform, + isTransform: isTransform, initialised: false, finished: false, }) @@ -364,7 +364,6 @@ SVG.Runner = SVG.invent({ // Save a morpher to the morpher list so that we can retarget it later _rememberMorpher: function (method, morpher) { - if (method == 'transform' && !this._isDeclarative) return this._history[method] = { morpher: morpher, caller: this._queue[this._queue.length - 1], @@ -374,9 +373,25 @@ SVG.Runner = SVG.invent({ // Try to set the target for a morpher if the morpher exists, otherwise // do nothing and return false _tryRetarget: function (method, target) { - if (method == 'transform' && !this._isDeclarative) return false if(this._history[method]) { - this._history[method].morpher.to(target) + + // if the last method wasnt even initialised, throw it away + if (!this._history[method].caller.initialised) { + let index = this._queue.indexOf(this._history[method].caller) + this._queue.splice(index, 1) + return false + } + + // for the case of transformations, we use the special retarget function + // which has access to the outer scope + if (this._history[method].caller.isTransform) { + this._history[method].caller.isTransform(target) + + // for everything else a simple morpher change is sufficient + } else { + this._history[method].morpher.to(target) + } + this._history[method].caller.finished = false var timeline = this.timeline() timeline && timeline._continue() @@ -480,9 +495,8 @@ function mergeTransforms () { let netTransform = runners .map(runner => runner.transforms) .reduce((last, curr) => last.lmultiply(curr)) - this.transform(netTransform) - this._currentMatrix = runners[0] + this.transform(netTransform) // Merge any two transformations in a row that are done let lastRunner = null @@ -490,7 +504,7 @@ function mergeTransforms () { if (lastRunner && runner.done && lastRunner.done) { delete runners[runner.id+1] runners[lastRunner.id+1] = { - transforms: runner.transforms.lmultiply(lastRunner.transforms), + transforms: runner.transforms.multiply(lastRunner.transforms), done: true, id: lastRunner.id, } @@ -499,7 +513,7 @@ function mergeTransforms () { lastRunner = runner }) - if (!lastRunner) { + if (lastRunner == runners[0]) { this._frameId = null } } @@ -621,154 +635,90 @@ SVG.extend(SVG.Runner, { ? transforms.affine : (affine != null ? affine : !isMatrix) - /** - 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 - var morpher - var origin - var current - var element - if(relative && !isMatrix) { - morpher = new SVG.Morphable().type(SVG.Morphable.ObjectBag) - .stepper(this._stepper) - - this.queue(function() { - element = this.element() - element.addRunner(this) - - if (origin == null) { - origin = new SVG.Point(getOrigin (transforms, element)) - transforms = {...transforms, origin: [origin.x, origin.y]} - morpher.to(formatTransforms(transforms)) - } - - let from = current || {origin} - morpher.from(formatTransforms(from)) - - }, function (pos) { - let currentMatrix = element._currentTransform(this) - let {x, y} = origin.transform(currentMatrix) - - - /* - 1. Transform the origin by figuring out the delta - - - At the start, we had: - - let Sinv = new SVG.Matrix(element).inverse() - let origin = getOrigin(element) - - - At a particular frame we have: - - let C = Matrix(element) - let newOrigin = origin.transform(S.inv).transform(C) - */ - - // this is an ugly hack to update the origins in the morpher - let index = morpher._from.indexOf('ox') - morpher._from.splice(index, 4, 'ox', x, 'oy', y) - morpher._to.splice(index, 4, 'ox', x, 'oy', y) - - current = morpher.at(pos).valueOf() - let matrix = new SVG.Matrix(current) - this.addTransform(matrix) - - return morpher.done() - }, true) - this._isDeclarative && this._rememberMorpher('transform', morpher) - return this - } - - // 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 + const morpher = new SVG.Morphable().type( + affine ? SVG.Morphable.TransformBag2 : SVG.Matrix + ).stepper(this._stepper) - var morphType = (isMatrix && !affine) - ? SVG.Matrix - : SVG.Morphable.TransformBag + let origin + let element + let current + let currentAngle + var u = 0 + this.queue(function () { - morpher = new SVG.Morphable().type(morphType) - morpher.stepper(this._stepper) - morpher.to(transforms) + // make sure element and origin is defined + element = element || this.element() + origin = origin || getOrigin(transforms, element) - this.queue(function() { - element = this.element() + // add the runner to the element so it can merge transformations element.addRunner(this) - if (!origin && affine) { - origin = new SVG.Point(getOrigin (transforms, element)) - transforms = {...transforms, origin: [origin.x, origin.y]} - morpher.to(transforms) - } - if (!relative) { // Deactivate all transforms that have run so far if we are absolute element._clearTransformRunnersBefore(this) } - // FIXME: This should be current = current || ... - // Define the starting point for the morpher - let startMatrix = new SVG.Matrix(relative ? undefined : element) + let target = new SVG.Matrix({...transforms, origin}) + let start = current || new SVG.Matrix(relative ? undefined : element) - // make sure to add an origin if we morph affine if (affine) { - startMatrix.origin = origin + target = target.decompose(origin[0], origin[1]) + start = start.decompose(origin[0], origin[1]) // Get the current and target angle as it was set - const rTarget = morpher._to[3] - const rCurrent = startMatrix.decompose(origin[0], origin[1]).rotate + const rTarget = target.rotate + const rCurrent = start.rotate // Figure out the shortest path to rotate directly const possibilities = [rTarget - 360, rTarget, rTarget + 360] const distances = possibilities.map( a => Math.abs(a - rCurrent) ) const shortest = Math.min(...distances) const index = distances.indexOf(shortest) - const target = possibilities[index] + target.rotate = possibilities[index] + } - // HACK: We directly replace the rotation so that its faster - morpher._to.splice(3, 1, target) + if (relative) { + target.rotate = transforms.rotate || 0 + start.rotate = currentAngle || start.rotate } - // make sure morpher starts at the current matrix for declarative - // this happens only when the init function is called multiple times - // which is only true for declarative - morpher.from(current || startMatrix) + morpher.from(start) + morpher.to(target) + }, function (pos) { + // clear all other transforms before this in case something is saved + // on this runner. We are absolute. We dont need these! if (!relative) this.clearTransform() - // Retarget the origin if we are in an + // fix the origin so is in the right space if (affine) { let currentMatrix = element._currentTransform(this) - let {x, y} = origin.transform(currentMatrix) + let {x, y} = new SVG.Point(origin).transform(currentMatrix) morpher._from.splice(-2, 2, x, y) morpher._to.splice(-2, 2, x, y) } - current = morpher.at(pos) + let affineParameters = morpher.at(pos) + currentAngle = affineParameters.rotate + current = new SVG.Matrix(affineParameters) + this.addTransform(current) + return morpher.done() - }, true) + }, function (newTransforms) { + + // only get a new origin if it changed since the last call + if ((newTransforms.origin || 'center').toString() != (transforms.origin || 'center').toString()) { + origin = getOrigin (transforms, element) + } + + // overwrite the old transformations with the new ones + transforms = {...newTransforms, origin} + }) + this._isDeclarative && this._rememberMorpher('transform', morpher) |