Skip to content

Commit

Permalink
Release 3.0.4
Browse files Browse the repository at this point in the history
  • Loading branch information
Fuzzyma committed Dec 7, 2018
1 parent 5161dfd commit 7b02d60
Show file tree
Hide file tree
Showing 10 changed files with 158 additions and 59 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ The document follows the conventions described in [“Keep a CHANGELOG”](http:

====

## [3.0.4] - 2018-12-07

### Fixed
- fixed `zoom` which was added correctly and is animatable now
- fixed `Runner` which merges transformations on the correct frame and in the correct way now
- fixed condition on which transforms get deleted from an element when animating
- fixed `Timeline` which executes Runner in the correct order now
- fixed `Svg` which correctly deletes the defs reference on `clear()`

## [3.0.3] - 2018-12-05

### Fixed
Expand Down Expand Up @@ -745,6 +754,7 @@ The document follows the conventions described in [“Keep a CHANGELOG”](http:


<!-- Headings above link to the releases listed here -->
[3.0.4]: https://github.com/svgdotjs/svg.js/releases/tag/3.0.4
[3.0.3]: https://github.com/svgdotjs/svg.js/releases/tag/3.0.3
[3.0.2]: https://github.com/svgdotjs/svg.js/releases/tag/3.0.2
[3.0.1]: https://github.com/svgdotjs/svg.js/releases/tag/3.0.1
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@svgdotjs/svg.js",
"version": "3.0.3",
"version": "3.0.4",
"description": "A lightweight library for manipulating and animating SVG.",
"url": "https://svgdotjs.github.io/",
"homepage": "https://svgdotjs.github.io/",
Expand Down
19 changes: 18 additions & 1 deletion spec/spec/types/Box.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
import { getMethodsFor } from '../../../src/utils/methods.js'
import { getWindow, withWindow } from '../../../src/utils/window.js'

const viewbox = getMethodsFor('viewbox').viewbox
const { zoom, viewbox} = getMethodsFor('viewbox')

const { any, objectContaining, arrayContaining } = jasmine

Expand Down Expand Up @@ -175,5 +175,22 @@ describe('Box.js', () => {
expect(viewbox.call(canvas).toArray()).toEqual([10, 10, 200, 200])
})
})

describe('zoom()', () => {
it('zooms around the center by default', () => {
const canvas = zoom.call(SVG().size(100, 50).viewbox(0, 0, 100, 50).addTo(container), 2)
expect(canvas.attr('viewBox')).toEqual('25 12.5 50 25')
})

it('zooms around a point', () => {
const canvas = zoom.call(SVG().size(100, 50).viewbox(0, 0, 100, 50).addTo(container), 2, [0, 0])
expect(canvas.attr('viewBox')).toEqual('0 0 50 25')
})

it('gets the zoom', () => {
const canvas = zoom.call(SVG().size(100, 50).viewbox(0, 0, 100, 50).addTo(container), 2)
expect(zoom.call(canvas)).toEqual(2)
})
})
})
})
21 changes: 21 additions & 0 deletions src/animation/Animator.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const Animator = {
nextDraw: null,
frames: new Queue(),
timeouts: new Queue(),
immediates: new Queue(),
timer: () => globals.window.performance || globals.window.Date,
transforms: [],

Expand Down Expand Up @@ -38,6 +39,17 @@ const Animator = {
return node
},

immediate (fn) {
// Add the immediate fn to the end of the queue
var node = Animator.immediates.push(fn)
// Request another animation frame if we need one
if (Animator.nextDraw === null) {
Animator.nextDraw = globals.window.requestAnimationFrame(Animator._draw)
}

return node
},

cancelFrame (node) {
node != null && Animator.frames.remove(node)
},
Expand All @@ -46,6 +58,10 @@ const Animator = {
node != null && Animator.timeouts.remove(node)
},

cancelImmediate (node) {
node != null && Animator.immediates.remove(node)
},

_draw (now) {
// Run all the timeouts we can run, if they are not ready yet, add them
// to the end of the queue immediately! (bad timeouts!!! [sarcasm])
Expand All @@ -70,6 +86,11 @@ const Animator = {
nextFrame.run()
}

var nextImmediate = null
while ((nextImmediate = Animator.immediates.shift())) {
nextImmediate()
}

// If we have remaining timeouts or frames, draw until we don't anymore
Animator.nextDraw = Animator.timeouts.first() || Animator.frames.first()
? globals.window.requestAnimationFrame(Animator._draw)
Expand Down
2 changes: 1 addition & 1 deletion src/animation/Queue.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export default class Queue {
this._first = item
}

// Update the length and return the current item
// Return the current item
return item
}

Expand Down
45 changes: 19 additions & 26 deletions src/animation/Runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ export default class Runner extends EventTarget {
}

after (fn) {
return this.on('finish', fn)
return this.on('finished', fn)
}

/*
Expand Down Expand Up @@ -276,7 +276,8 @@ export default class Runner extends EventTarget {
// Figure out if we just started
var duration = this.duration()
var justStarted = this._lastTime <= 0 && this._time > 0
var justFinished = this._lastTime < this._time && this.time > duration
var justFinished = this._lastTime < duration && this._time >= duration

this._lastTime = this._time
if (justStarted) {
this.fire('start', this)
Expand Down Expand Up @@ -304,15 +305,15 @@ export default class Runner extends EventTarget {
// correct the done flag here
// declaritive animations itself know when they converged
this.done = this.done || (converged && declarative)
if (this.done) {
this.fire('finish', this)
if (justFinished) {
this.fire('finished', this)
}
return this
}

reset () {
if (this._reseted) return this
this.loops(0)
this.time(0)
this._reseted = true
return this
}
Expand Down Expand Up @@ -443,7 +444,7 @@ export default class Runner extends EventTarget {

// TODO: Keep track of all transformations so that deletion is faster
clearTransformsFromQueue () {
if (!this.done || !this._timeline || !this._timeline._order.includes(this)) {
if (!this.done || !this._timeline || !this._timeline._runnerIds.includes(this.id)) {
this._queue = this._queue.filter((item) => {
return !item.isTransform
})
Expand Down Expand Up @@ -530,18 +531,10 @@ class RunnerArray {

add (runner) {
if (this.runners.includes(runner)) return

let id = runner.id + 1

let leftSibling = this.ids.reduce((last, curr) => {
if (curr > last && curr < id) return curr
return last
}, 0)

let index = this.ids.indexOf(leftSibling) + 1

this.ids.splice(index, 0, id)
this.runners.splice(index, 0, runner)
this.runners.push(runner)
this.ids.push(id)

return this
}
Expand All @@ -564,10 +557,11 @@ class RunnerArray {
const condition = lastRunner
&& runner.done && lastRunner.done
// don't merge runner when persisted on timeline
&& (!runner._timeline || !runner._timeline._order.includes(runner.id))
&& (!lastRunner._timeline || !lastRunner._timeline._order.includes(lastRunner.id))
&& (!runner._timeline || !runner._timeline._runnerIds.includes(runner.id))
&& (!lastRunner._timeline || !lastRunner._timeline._runnerIds.includes(lastRunner.id))

if (condition) {
// the +1 happens in the function
this.remove(runner.id)
this.edit(lastRunner.id, runner.mergeWith(lastRunner))
}
Expand All @@ -580,7 +574,7 @@ class RunnerArray {

edit (id, newRunner) {
let index = this.ids.indexOf(id + 1)
this.ids.splice(index, 1, id)
this.ids.splice(index, 1, id + 1)
this.runners.splice(index, 1, newRunner)
return this
}
Expand Down Expand Up @@ -636,11 +630,10 @@ registerMethods({
this._transformationRunners.add(runner)

// Make sure that the runner merge is executed at the very end of
// all Animator functions. Thats why we use setTimeout here
setTimeout(() => {
Animator.cancelFrame(this._frameId)
this._frameId = Animator.frame(mergeTransforms.bind(this))
}, 0)
// all Animator functions. Thats why we use immediate here to execute
// the merge right after all frames are run
Animator.cancelImmediate(this._frameId)
this._frameId = Animator.immediate(mergeTransforms.bind(this))
},

_prepareRunner () {
Expand Down Expand Up @@ -689,7 +682,7 @@ extend(Runner, {
var morpher = new Morphable(this._stepper).to(new SVGNumber(level))

this.queue(function () {
morpher = morpher.from(this.zoom())
morpher = morpher.from(this.element().zoom())
}, function (pos) {
this.element().zoom(morpher.at(pos), point)
return morpher.done()
Expand Down Expand Up @@ -804,8 +797,8 @@ extend(Runner, {
currentAngle = affineParameters.rotate
current = new Matrix(affineParameters)

element._addRunner(this)
this.addTransform(current)
element._addRunner(this)
return morpher.done()
}

Expand Down
79 changes: 52 additions & 27 deletions src/animation/Timeline.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ export default class Timeline extends EventTarget {
this._nextFrame = null
this._paused = true
this._runners = []
this._order = []
this._runnerIds = []
this._lastRunnerId = -1
this._time = 0
this._lastSourceTime = 0
this._lastStepTime = 0
Expand All @@ -45,7 +46,7 @@ export default class Timeline extends EventTarget {
// schedules a runner on the timeline
schedule (runner, delay, when) {
if (runner == null) {
return this._order.map((id) => makeSchedule(this._runners[id]))
return this._runners.map(makeSchedule)
}

// The start time for the next animation can either be given explicitly,
Expand Down Expand Up @@ -80,34 +81,37 @@ export default class Timeline extends EventTarget {
runner.timeline(this)

const persist = runner.persist()

// Save runnerInfo
this._runners[runner.id] = {
const runnerInfo = {
persist: persist === null ? this._persist : persist,
runner: runner,
start: absoluteStartTime + delay
start: absoluteStartTime + delay,
runner
}

// Save order, update Time if needed and continue
this._order.push(runner.id)
this._lastRunnerId = runner.id

this._runners.push(runnerInfo)
this._runners.sort((a, b) => a.start - b.start)
this._runnerIds = this._runners.map(info => info.runner.id)

this.updateTime()._continue()
return this
}

// Remove the runner from this timeline
unschedule (runner) {
var index = this._order.indexOf(runner.id)
var index = this._runnerIds.indexOf(runner.id)
if (index < 0) return this

delete this._runners[runner.id]
this._order.splice(index, 1)
this._runners.splice(index, 1)
this._runnerIds.splice(index, 1)

runner.timeline(null)
return this
}

// Calculates the end of the timeline
getEndTime () {
var lastRunnerInfo = this._runners[this._order[this._order.length - 1]]
var lastRunnerInfo = this._runners[this._runnerIds.indexOf(this._lastRunnerId)]
var lastDuration = lastRunnerInfo ? lastRunnerInfo.runner.duration() : 0
var lastStartTime = lastRunnerInfo ? lastRunnerInfo.start : 0
return lastStartTime + lastDuration
Expand Down Expand Up @@ -200,12 +204,39 @@ export default class Timeline extends EventTarget {
this._lastStepTime = this._time
this.fire('time', this._time)

// This is for the case that the timeline was seeked so that the time
// is now before the startTime of the runner. Thats why we need to set
// the runner to position 0

// FIXME:
// However, reseting in insertion order leads to bugs. Considering the case,
// where 2 runners change the same attriute but in different times,
// reseting both of them will lead to the case where the later defined
// runner always wins the reset even if the other runner started earlier
// and therefore should win the attribute battle
// this can be solved by reseting them backwards
for (var k = this._runners.length; k--;) {
// Get and run the current runner and ignore it if its inactive
let runnerInfo = this._runners[k]
let runner = runnerInfo.runner

// Make sure that we give the actual difference
// between runner start time and now
let dtToStart = this._time - runnerInfo.start

// Dont run runner if not started yet
// and try to reset it
if (dtToStart <= 0) {
runner.reset()
}
}

// Run all of the runners directly
var runnersLeft = false
for (var i = 0, len = this._order.length; i < len; i++) {
for (var i = 0, len = this._runners.length; i < len; i++) {
// Get and run the current runner and ignore it if its inactive
var runnerInfo = this._runners[this._order[i]]
var runner = runnerInfo.runner
let runnerInfo = this._runners[i]
let runner = runnerInfo.runner
let dt = dtTime

// Make sure that we give the actual difference
Expand All @@ -215,11 +246,6 @@ export default class Timeline extends EventTarget {
// Dont run runner if not started yet
if (dtToStart <= 0) {
runnersLeft = true

// This is for the case that the timeline was seeked so that the time
// is now before the startTime of the runner. Thats why we need to set
// the runner to position 0
runner.reset()
continue
} else if (dtToStart < dt) {
// Adjust dt to make sure that animation is on point
Expand All @@ -236,21 +262,20 @@ export default class Timeline extends EventTarget {
// continue
} else if (runnerInfo.persist !== true) {
// runner is finished. And runner might get removed

var endTime = runner.duration() - runner.time() + this._time

if (endTime + this._persist < this._time) {
if (endTime + runnerInfo.persist < this._time) {
// Delete runner and correct index
delete this._runners[this._order[i]]
this._order.splice(i--, 1) && --len
runner.timeline(null)
runner.unschedule()
--i
--len
}
}
}

// 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)) {
if ((runnersLeft && !(this._speed < 0 && this._time === 0)) || (this._runnerIds.length && this._speed < 0 && this._time > 0)) {
this._continue()
} else {
this.fire('finished')
Expand Down
Loading

0 comments on commit 7b02d60

Please sign in to comment.