diff options
Diffstat (limited to 'dist/svg.js')
-rw-r--r-- | dist/svg.js | 699 |
1 files changed, 331 insertions, 368 deletions
diff --git a/dist/svg.js b/dist/svg.js index 16bffe3..8ed57fc 100644 --- a/dist/svg.js +++ b/dist/svg.js @@ -1,12 +1,12 @@ /*! * svg.js - A lightweight library for manipulating and animating SVG. -* @version 2.2.4 +* @version 3.0.0 * http://www.svgjs.com * * @copyright Wout Fierens <wout@impinc.co.uk> * @license MIT * -* BUILT: Sat Dec 12 2015 00:53:00 GMT+0100 (Mitteleuropäische Zeit) +* BUILT: Sun Dec 13 2015 00:50:41 GMT+0100 (Mitteleuropäische Zeit) */; (function(root, factory) { if (typeof define === 'function' && define.amd) { @@ -1238,467 +1238,430 @@ SVG.Element = SVG.invent({ } }) -SVG.FX = SVG.invent({ - // Initialize FX object - create: function(element) { - // store target element - this.target = element - } +SVG.easing = { + '-': function(pos){return pos} +, '<>':function(pos){return -Math.cos(pos * Math.PI) / 2 + 0.5} +, '>': function(pos){return Math.sin(pos * Math.PI / 2)} +, '<': function(pos){return -Math.cos(pos * Math.PI / 2) + 1} +} - // Add class methods -, extend: { - // Add animation parameters and start animation - animate: function(d, ease, delay) { - var akeys, skeys, key - , element = this.target - , fx = this - - // dissect object if one is passed - if (typeof d == 'object') { - delay = d.delay - ease = d.ease - d = d.duration - } +SVG.Situation = SVG.invent({ - // ensure default duration and easing - d = d == '=' ? d : d == null ? 1000 : new SVG.Number(d).valueOf() - ease = ease || '<>' - - // process values - fx.at = function(pos) { - var i - - // normalise pos - pos = pos < 0 ? 0 : pos > 1 ? 1 : pos - - // collect attribute keys - if (akeys == null) { - akeys = [] - for (key in fx.attrs) - akeys.push(key) - - // make sure morphable elements are scaled, translated and morphed all together - if (element.morphArray && (fx.destination.plot || akeys.indexOf('points') > -1)) { - // get destination - var box - , p = new element.morphArray(fx.destination.plot || fx.attrs.points || element.array()) - - // add size - if (fx.destination.size) - p.size(fx.destination.size.width.to, fx.destination.size.height.to) - - // add movement - box = p.bbox() - if (fx.destination.x) - p.move(fx.destination.x.to, box.y) - else if (fx.destination.cx) - p.move(fx.destination.cx.to - box.width / 2, box.y) - - box = p.bbox() - if (fx.destination.y) - p.move(box.x, fx.destination.y.to) - else if (fx.destination.cy) - p.move(box.x, fx.destination.cy.to - box.height / 2) - - // reset destination values - fx.destination = { - plot: element.array().morph(p) - } - } - } + create: function(o) { - // collect style keys - if (skeys == null) { - skeys = [] - for (key in fx.styles) - skeys.push(key) - } + if(o instanceof SVG.Situation) return o - // apply easing - pos = ease == '<>' ? - (-Math.cos(pos * Math.PI) / 2) + 0.5 : - ease == '>' ? - Math.sin(pos * Math.PI / 2) : - ease == '<' ? - -Math.cos(pos * Math.PI / 2) + 1 : - ease == '-' ? - pos : - typeof ease == 'function' ? - ease(pos) : - pos - - // run plot function - if (fx.destination.plot) { - element.plot(fx.destination.plot.at(pos)) + o = o || {} - } else { - // run all x-position properties - if (fx.destination.x) - element.x(fx.destination.x.at(pos)) - else if (fx.destination.cx) - element.cx(fx.destination.cx.at(pos)) - - // run all y-position properties - if (fx.destination.y) - element.y(fx.destination.y.at(pos)) - else if (fx.destination.cy) - element.cy(fx.destination.cy.at(pos)) - - // run all size properties - if (fx.destination.size) - element.size(fx.destination.size.width.at(pos), fx.destination.size.height.at(pos)) - } + this._duration = o.duration || 1000 + this._delay = o.delay || 0 + this._start = +new Date + this._delay + this._end = this._start + this._duration - // run all viewbox properties - if (fx.destination.viewbox) - element.viewbox( - fx.destination.viewbox.x.at(pos) - , fx.destination.viewbox.y.at(pos) - , fx.destination.viewbox.width.at(pos) - , fx.destination.viewbox.height.at(pos) - ) - - // run leading property - if (fx.destination.leading) - element.leading(fx.destination.leading.at(pos)) - - // animate attributes - for (i = akeys.length - 1; i >= 0; i--) - element.attr(akeys[i], at(fx.attrs[akeys[i]], pos)) - - // animate styles - for (i = skeys.length - 1; i >= 0; i--) - element.style(skeys[i], at(fx.styles[skeys[i]], pos)) - - // callback for each keyframe - if (fx.situation.during) - fx.situation.during.call(element, pos, function(from, to) { - return at({ from: from, to: to }, pos) - }) - } + this.easing = SVG.easing[o.easing || '-'] || o.easing // when easing is a function, its not in SVG.easing + this.pos = 0 // position before easing + this.paused = false + this.finished = false + this.active = false + this._fx = null - if (typeof d === 'number') { - // delay animation - this.timeout = setTimeout(function() { - var start = new Date().getTime() - - // initialize situation object - fx.situation.start = start - fx.situation.play = true - fx.situation.finish = start + d - fx.situation.duration = d - fx.situation.ease = ease - - // render function - fx.render = function() { - - if (fx.situation.play === true) { - // calculate pos - var time = new Date().getTime() - , pos = time > fx.situation.finish ? 1 : (time - fx.situation.start) / d - - // reverse pos if animation is reversed - if (fx.situation.reversing) - pos = -pos + 1 - - // process values - fx.at(pos) - - // finish off animation - if (time > fx.situation.finish) { - if (fx.destination.plot) - element.plot(new SVG.PointArray(fx.destination.plot.destination).settle()) - - if (fx.situation.loop === true || (typeof fx.situation.loop == 'number' && fx.situation.loop > 0)) { - // register reverse - if (fx.situation.reverse) - fx.situation.reversing = !fx.situation.reversing - - if (typeof fx.situation.loop == 'number') { - // reduce loop count - if (!fx.situation.reverse || fx.situation.reversing) - --fx.situation.loop - - // remove last loop if reverse is disabled - if (!fx.situation.reverse && fx.situation.loop == 1) - --fx.situation.loop - } - - fx.animate(d, ease, delay) - } else { - fx.situation.after ? fx.situation.after.apply(element, [fx]) : fx.stop() - } - - } else { - fx.animationFrame = requestAnimationFrame(fx.render) - } - } else { - fx.animationFrame = requestAnimationFrame(fx.render) - } + this.animations = { + // functionToCall: [morphable object, destination value] + // e.g. x: [SVG.Number, 5] + // this way its assured, that the start value is set correctly + } - } + } - // start animation - fx.render() +, extend: { - }, new SVG.Number(delay).valueOf()) - } + // returns the position at a given time + timeToPos: function(timestamp){ + return (timestamp - this._start) / (this._duration) + } - return this + // returns the timestamp from a given positon + , posToTime: function(pos){ + return this._duration * pos + this._start } - // Get bounding box of target element - , bbox: function() { - return this.target.bbox() + + , startAnimFrame: function(){ + this.animationFrame = requestAnimationFrame(function(){ this.step() }.bind(this)) } - // Add animatable attributes - , attr: function(a, v) { - // apply attributes individually - if (typeof a == 'object') { - for (var key in a) - this.attr(key, a[key]) - } else { - // get the current state - var from = this.target.attr(a) + , stopAnimFrame: function(){ + cancelAnimationFrame(this.animationFrame) + } - // detect format - if (a == 'transform') { - // merge given transformation with an existing one - if (this.attrs[a]) - v = this.attrs[a].destination.multiply(v) + , start: function(){ - // prepare matrix for morphing - this.attrs[a] = (new SVG.Matrix(this.target)).morph(v) - // add parametric rotation values - if (this.param) { - // get initial rotation - v = this.target.transform('rotation') - // add param - this.attrs[a].param = { - from: this.target.param || { rotation: v, cx: this.param.cx, cy: this.param.cy } - , to: this.param - } - } + if(this.fx().current() == this){ + // morph values from the current position to the destination - maybe move this to another place + for(var i in this.animations){ + if(this.animations[i] instanceof Array){ - } else { - this.attrs[a] = SVG.Color.isColor(v) ? - // prepare color for morphing - new SVG.Color(from).morph(v) : - SVG.regex.unit.test(v) ? - // prepare number for morphing - new SVG.Number(from).morph(v) : - // prepare for plain morphing - { from: from, to: v } + this.animations[i] = new this.animations[i][0](this.fx().target[i]()).morph(this.animations[i][1]) + console.log(i, this.animations[i]) + } } } - return this - } - // Add animatable styles - , style: function(s, v) { - if (typeof s == 'object') - for (var key in s) - this.style(key, s[key]) + // dont start if already started + if(!this.active && this.fx().current() == this){ + this._start = +new Date + this._delay + this._end = this._start + this._duration + this.active = true - else - this.styles[s] = { from: this.target.style(s), to: v } + this.timeout = setTimeout(function(){ this.startAnimFrame() }.bind(this), this.delay) + } return this } - // Animatable x-axis - , x: function(x) { - this.destination.x = new SVG.Number(this.target.x()).morph(x) + + , stop: function(){ + if(!this.active) return false + this.active = false + this.stopAnimFrame() + clearTimeout(this.timeout) return this } - // Animatable y-axis - , y: function(y) { - this.destination.y = new SVG.Number(this.target.y()).morph(y) + , seek: function(pos){ + this.pos = pos + this._start = -pos * this.duration + new Date + this._end = this._start + this._duration return this } - // Animatable center x-axis - , cx: function(x) { - this.destination.cx = new SVG.Number(this.target.cx()).morph(x) + , speed: function(speed){ + this.speed = speed + this.duration = this.duration * this.pos + (1-this.pos) * this.duration / speed + this._end = this._start + this._duration return this } - // Animatable center y-axis - , cy: function(y) { - this.destination.cy = new SVG.Number(this.target.cy()).morph(y) + , pause: function(){ + this.paused = true + this.stopAnimFrame() + clearTimeout(this.timeout) return this } - // 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) { - if (this.target instanceof SVG.Text) { - // animate font size for Text elements - this.attr('font-size', width) - - } else { - // animate bbox based size for all other elements - var box = this.target.bbox() - this.destination.size = { - width: new SVG.Number(box.width).morph(width) - , height: new SVG.Number(box.height).morph(height) - } + , play: function(){ + if(this.paused){ + this.seek(this.pos) + this.paused = false + this.startAnimFrame() } return this } - // Add animatable plot - , plot: function(p) { - this.destination.plot = p + , push: function(method, args){ + this.animations[method] = args return this } - // Add leading method - , leading: function(value) { - if (this.target.destination.leading) - this.destination.leading = new SVG.Number(this.target.destination.leading).morph(value) + , pop: function(method){ + var ret = this.animations[method] + this.drop(method) + return ret + } + + , drop: function(method){ + delete this.animations[method] return this } - // Add animatable viewbox - , viewbox: function(x, y, width, height) { - if (this.target instanceof SVG.Container) { - var box = this.target.viewbox() - this.destination.viewbox = { - x: new SVG.Number(box.x).morph(x) - , y: new SVG.Number(box.y).morph(y) - , width: new SVG.Number(box.width).morph(width) - , height: new SVG.Number(box.height).morph(height) - } + , get: function(method){ + return this.animations[method] + } + + , getAt: function(method){ + return this.get(method).at(this.easing(this.pos)) + } + + , step: function(){ + + if(this.paused) return this + + this.pos = this.timeToPos(+new Date) + + if(this.pos > 1) this.pos = 1 + if(this.pos < 0) this.pos = 0 + + this.eachAt(function(method, args){ + this.fx().target[method].apply(this.fx().target, args) + }) + + if(this.pos == 1){ + this.finished = true + this.active = false + var next = this.fx().next() + if(next)next.start() + cancelAnimationFrame(this.animationFrame) + }else{ + this.startAnimFrame() } return this + } - // Add animateable gradient update - , update: function(o) { - if (this.target instanceof SVG.Stop) { - 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', new SVG.Number(o.offset)) + + , eachAt: function(block){ + + for(var i in this.animations){ + + var at = [].concat(this.animations[i]).map(function(el){ + if(el.at) return el.at(this.easing(this.pos)) + return el + }.bind(this)) + + block.call(this, i, at) + } - return this } - // Add callback for each keyframe - , during: function(during) { - this.situation.during = during - return this + , fx: function(fx){ + if(fx){ + this._fx = fx + return this + } + + return this._fx } - // Callback after animation - , after: function(after) { - this.situation.after = after + } + +}) + +SVG.FX = SVG.invent({ + // Initialize FX object + create: function(element) { + // store target element + this.target = element + this._queue = [] + this._current = 0 + } + + // Add class methods +, extend: { + // pushs a new situation to the queue + enqueue: function(o) { + this.queue().push(new SVG.Situation(o).fx(this)) return this } - // Make loopable - , loop: function(times, reverse) { - // store current loop and total loops - this.situation.loop = this.situation.loops = times || true - // make reversable - this.situation.reverse = !!reverse + // returns the queue + , queue: function() { + return this._queue; + } - return this + , current: function() { + return this.get(this._current) + } + + , last: function() { + return this.get(this.queue().length-1) + } + + , first: function() { + return this.get(0) } - // Stop running animation - , stop: function(fulfill) { - // fulfill animation - if (fulfill === true) { - this.animate(0) + , search: function(attr) { + var current = this.queue().length-1 - if (this.situation.after) - this.situation.after.apply(this.target, [this]) + while(situation = this.get(--current)){ + + // get method of situation if present + var attr = situation.get(attr) + if(!attr) continue + + // if not yet morphed we extract the destination from the array + if(attr instanceof Array) return attr[1] + + // otherwise from the morphed object + return attr.destination - } else { - // stop current animation - clearTimeout(this.timeout) - cancelAnimationFrame(this.animationFrame); - - // reset storage for properties - this.attrs = {} - this.styles = {} - this.situation = {} - this.destination = {} } - return this + // return the elements attribute as fallback + return this.target[attr]() + } - // Pause running animation + + , prev: function() { + return this.get(--this._current) + } + + , next: function() { + return this.get(++this._current) + } + + , get: function(i) { + if(!this._queue[i]) return null + return this._queue[i] + } + , pause: function() { - if (this.situation.play === true) { - this.situation.play = false - this.situation.pause = new Date().getTime() - } + this.active = false + } + + , play: function() { + this.reverse = false + this.active = true + } + + , resume: function() { + this.active = true + } + , finish: function() { + this.active = false + return this.progress(1) + } + + , reverse: function() { + this.reverse = true return this } - // Play running animation - , play: function() { - if (this.situation.play === false) { - var pause = new Date().getTime() - this.situation.pause - this.situation.finish += pause - this.situation.start += pause - this.situation.play = true - } + , progress: function(pos) { + this.get(this._current).progress(pos) + return this + } + , totalProgress: function(pos) { + if(pos == null) return this.total + this.total = pos return this } - } + , time: function(d) { + return this.progress(this.duration / d) + } - // Define parent class -, parent: SVG.Element + , totalTime: function(d) { + return this.totalProgress(this.duration / d) + } - // Add method to parent elements -, construct: { - // Get fx module or create a new one, then animate with given duration and ease - animate: function(d, ease, delay) { - return (this.fx || (this.fx = new SVG.FX(this))).stop().animate(d, ease, delay) + , timeScale: function(factor) { + this.scale = factor + return this } - // Stop current animation; this is an alias to the fx instance - , stop: function(fulfill) { - if (this.fx) - this.fx.stop(fulfill) + // Animatable x-axis + , x: function(x) { + //this.last().push('x', [SVG.Number, x]).start() + this.last().push('x', new SVG.Number(this.search('x')).morph(x)).start() return this } - // Pause current animation - , pause: function() { - if (this.fx) - this.fx.pause() + // Animatable y-axis + , y: function(y) { + //this.last().push('y', [SVG.Number, y]).start() + this.last().push('y', new SVG.Number(this.search('y')).morph(y)).start() return this } - // Play paused current animation - , play: function() { - if (this.fx) - this.fx.play() + // Animatable center x-axis + , cx: function(x) { + //this.last().push('cx', [SVG.Number, x]).start() + this.last().push('cx', new SVG.Number(this.search('cx')).morph(x)).start() return this } + // Animatable center y-axis + , cy: function(y) { + //this.last().push('cy', [SVG.Number, y]).start() + this.last().push('cy', new SVG.Number(this.search('cy')).morph(y)).start() + + return this + } + // 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) + } + , dx: function(x) { + return this.x(this.search('x') + x) + } + , dy: function(y) { + return this.y(this.search('y') + y) + } + // Relative move over x and y axes + , dmove: function(x, y) { + return this.dx(x).dy(y) + } + /*, attr: function(a, v) { + // apply attributes individually + if (typeof a == 'object') { + for (var key in a) + this.attr(key, a[key]) + + } else { + // get the current state + //var from = this.target.attr(a) + + // detect format + if (a == 'transform') { + // merge given transformation with an existing one + if (this.attrs[a]) + v = this.attrs[a].destination.multiply(v) + + // prepare matrix for morphing + this.attrs[a] = (new SVG.Matrix(this.target)).morph(v) + + // add parametric rotation values + if (this.param) { + // get initial rotation + v = this.target.transform('rotation') + + // add param + this.attrs[a].param = { + from: this.target.param || { rotation: v, cx: this.param.cx, cy: this.param.cy } + , to: this.param + } + } + + } else { + this.attrs[a] = SVG.Color.isColor(v) ? + // prepare color for morphing + new SVG.Color(from).morph(v) : + SVG.regex.unit.test(v) ? + // prepare number for morphing + new SVG.Number(from).morph(v) : + // prepare for plain morphing + { from: from, to: v } + } + } + + return this + }*/ } -}) + // Define parent class +, parent: SVG.Element + + // Add method to parent elements +, construct: { + // Get fx module or create a new one, then animate with given duration and ease + animate: function(o) { + return (this.fx || (this.fx = new SVG.FX(this))).enqueue(o) + } + , delay: function(delay){ + return (this.fx || (this.fx = new SVG.FX(this))).enqueue({delay:delay}) + } + } +}) SVG.BBox = SVG.invent({ // Initialize create: function(element) { |