In the process of adding useful animation methods + docs

master
Dmitry Baranovskiy 2013-08-20 19:42:07 +10:00
parent 8eacc3ab1a
commit 34c814c1df
4 changed files with 882 additions and 552 deletions

39
mina.js
View File

@ -11,8 +11,8 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
window.mina = (function () { window.mina = (function (eve) {
var animations = [], var animations = {},
requestAnimFrame = window.requestAnimationFrame || requestAnimFrame = window.requestAnimationFrame ||
window.webkitRequestAnimationFrame || window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame || window.mozRequestAnimationFrame ||
@ -71,15 +71,23 @@ window.mina = (function () {
a.s = a.s * val / a.dur; a.s = a.s * val / a.dur;
a.dur = val; a.dur = val;
}, },
stopit = function () {
var a = this;
delete animations[a.id];
eve("mina.stop." + a.id, a);
},
frame = function () { frame = function () {
for (var i = 0, ii = animations.length; i < ii; i++) { var len = 0;
for (var i in animations) if (animations.hasOwnProperty(i)) {
var a = animations[i], var a = animations[i],
b = a.get(), b = a.get(),
res; res;
len++;
a.s = (b - a.b) / (a.dur / a.spd); a.s = (b - a.b) / (a.dur / a.spd);
if (a.s >= 1) { if (a.s >= 1) {
animations.splice(i, 1); delete animations[i];
a.s = 1; a.s = 1;
len--;
} }
if (isArray(a.start)) { if (isArray(a.start)) {
res = []; res = [];
@ -91,11 +99,11 @@ window.mina = (function () {
res = a.start + (a.end - a.start) * a.easing(a.s); res = a.start + (a.end - a.start) * a.easing(a.s);
} }
a.set(res); a.set(res);
if (a.s == 1 && typeof eve != "undefined") { if (a.s == 1) {
eve("mina.finish." + a.id, a); eve("mina.finish." + a.id, a);
} }
} }
animations.length && requestAnimFrame(frame); len && requestAnimFrame(frame);
}, },
mina = function (a, A, b, B, get, set, easing) { mina = function (a, A, b, B, get, set, easing) {
var anim = { var anim = {
@ -111,13 +119,24 @@ window.mina = (function () {
easing: easing || mina.linear, easing: easing || mina.linear,
status: sta, status: sta,
speed: speed, speed: speed,
duration: duration duration: duration,
stop: stopit
}; };
animations.push(anim); animations[anim.id] = anim;
animations.length == 1 && requestAnimFrame(frame); var len = 0, i;
for (i in animations) if (animations.hasOwnProperty(i)) {
len++;
if (len == 2) {
break;
}
}
len == 1 && requestAnimFrame(frame);
return anim; return anim;
}; };
mina.time = timer; mina.time = timer;
mina.getById = function (id) {
return animations[anim.id] || null;
};
mina.linear = function (n) { mina.linear = function (n) {
return n; return n;
@ -178,4 +197,4 @@ window.mina = (function () {
}; };
return mina; return mina;
})(); })(typeof eve == "undefined" ? function () {} : eve);

6
savage-min.js vendored

File diff suppressed because one or more lines are too long

790
savage.js

File diff suppressed because it is too large Load Diff

363
svg.js
View File

@ -246,7 +246,7 @@ function x_y_w_h() {
} }
/*\ /*\
* Raphael.rad * Savage.rad
[ method ] [ method ]
** **
* Transform angle to radians * Transform angle to radians
@ -256,7 +256,7 @@ function x_y_w_h() {
\*/ \*/
Savage.rad = rad; Savage.rad = rad;
/*\ /*\
* Raphael.deg * Savage.deg
[ method ] [ method ]
** **
* Transform angle to degrees * Transform angle to degrees
@ -562,7 +562,7 @@ function Matrix(a, b, c, d, e, f) {
}; };
})(Matrix.prototype); })(Matrix.prototype);
/*\ /*\
* Raphael.Matrix * Savage.Matrix
[ method ] [ method ]
** **
* Utility method * Utility method
@ -988,7 +988,7 @@ Savage.rgb2hsl = function (r, g, b) {
}; };
// Transformations // Transformations
/*\ /*\
* Savage.parsePathString * Savage.parsePathString
[ method ] [ method ]
** **
@ -998,8 +998,8 @@ Savage.rgb2hsl = function (r, g, b) {
> Parameters > Parameters
- pathString (string|array) path string or array of segments (in the last case it will be returned straight away) - pathString (string|array) path string or array of segments (in the last case it will be returned straight away)
= (array) array of segments. = (array) array of segments.
\*/ \*/
Savage.parsePathString = function (pathString) { Savage.parsePathString = function (pathString) {
if (!pathString) { if (!pathString) {
return null; return null;
} }
@ -1041,8 +1041,8 @@ Savage.rgb2hsl = function (r, g, b) {
data.toString = Savage.path.toString; data.toString = Savage.path.toString;
pth.arr = Savage.path.clone(data); pth.arr = Savage.path.clone(data);
return data; return data;
}; };
/*\ /*\
* Savage.parseTransformString * Savage.parseTransformString
[ method ] [ method ]
** **
@ -1052,7 +1052,7 @@ Savage.rgb2hsl = function (r, g, b) {
> Parameters > Parameters
- TString (string|array) transform string or array of transformations (in the last case it will be returned straight away) - TString (string|array) transform string or array of transformations (in the last case it will be returned straight away)
= (array) array of transformations. = (array) array of transformations.
\*/ \*/
var parseTransformString = Savage.parseTransformString = function (TString) { var parseTransformString = Savage.parseTransformString = function (TString) {
if (!TString) { if (!TString) {
return null; return null;
@ -1338,6 +1338,7 @@ function Element(el) {
this.paper = new Paper(svg); this.paper = new Paper(svg);
} }
this.type = el.tagName; this.type = el.tagName;
this.anims = {};
this._ = { this._ = {
transform: [], transform: [],
sx: 1, sx: 1,
@ -1360,6 +1361,29 @@ function arrayFirstValue(arr) {
} }
} }
(function (elproto) { (function (elproto) {
/*\
* Element.attr
[ method ]
**
* Gets or sets given attributes of the element
**
> Parameters
**
- params (object) key-value pairs of attributes you want to set
* or
- param (string) name of the attribute
= (Element)
* or
= (string) value of attribute
> Usage
| el.attr({
| fill: "#fc0",
| stroke: "#000",
| strokeWidth: 2, // CamelCase...
| "fill-opacity": 0.5 // or dash-separated names
| });
| console.log(el.attr("fill")); // “#fc0”
\*/
elproto.attr = function (params) { elproto.attr = function (params) {
var node = this.node; var node = this.node;
if (is(params, "string")) { if (is(params, "string")) {
@ -1372,6 +1396,31 @@ function arrayFirstValue(arr) {
} }
return this; return this;
}; };
/*\
* Element.getBBox
[ method ]
**
* Returns bounding box descriptor for the given element.
**
= (object) bounding box descriptor:
o {
o cx: (number) x of the center,
o cy: (number) x of the center,
o h: (number) height,
o height: (number) height,
o path: (string) path command for the box,
o r0: (number) radius of the circle that will enclose the box,
o r1: (number) radius of the smallest circle that can be enclosed,
o r2: (number) radius of the biggest circle that can be enclosed,
o vb: (string) box as a viewbox command,
o w: (number) width,
o width: (number) width,
o x2: (number) x of the right side,
o x: (number) x of the left side,
o y2: (number) y of the right side,
o y: (number) y of the left side
o }
\*/
elproto.getBBox = function (isWithoutTransform) { elproto.getBBox = function (isWithoutTransform) {
if (this.removed) { if (this.removed) {
return {}; return {};
@ -1400,6 +1449,28 @@ function arrayFirstValue(arr) {
var propString = function () { var propString = function () {
return this.local; return this.local;
}; };
/*\
* Element.transform
[ method ]
**
* Gets or sets transformation of the element
**
> Parameters
**
- tstr (string) transform string in Savage or SVG format
= (Element)
* or
= (object) transformation descriptor:
o {
o string (string) transform string,
o globalMatrix (Matrix) matrix of all transformations applied to element or its parents,
o localMatrix (Matrix) matrix of transformations applied only to the element,
o diffMatrix (Matrix) matrix of difference between global and local transformations,
o global (string) global transformation as string,
o local (string) local transformation as string,
o toString (function) returns `string` property
o }
\*/
elproto.transform = function (tstr) { elproto.transform = function (tstr) {
var _ = this._; var _ = this._;
if (tstr == null) { if (tstr == null) {
@ -1434,9 +1505,28 @@ function arrayFirstValue(arr) {
return this; return this;
}; };
/*\
* Element.parent
[ method ]
**
* Returns parent of the element
**
= (Element) parent
\*/
elproto.parent = function () { elproto.parent = function () {
return Savage(this.node.parentNode); return wrap(this.node.parentNode);
}; };
/*\
* Element.append
[ method ]
**
* Appends given element to current one.
**
> Parameters
**
- el (Element|Set) element to append
= (Element) parent
\*/
elproto.append = function (el) { elproto.append = function (el) {
if (el.type == "set") { if (el.type == "set") {
var it = this; var it = this;
@ -1450,44 +1540,128 @@ function arrayFirstValue(arr) {
el.paper = this.paper; el.paper = this.paper;
return this; return this;
}; };
/*\
* Element.prepend
[ method ]
**
* Prepends given element to current one.
**
> Parameters
**
- el (Element) element to prepend
= (Element) parent
\*/
elproto.prepend = function (el) { elproto.prepend = function (el) {
el = wrap(el); el = wrap(el);
this.node.parentNode.insertBefore(el.node, this.node.firstChild); this.node.parentNode.insertBefore(el.node, this.node.firstChild);
el.paper = this.paper; el.paper = this.paper;
return this; return this;
}; };
/*\
* Element.before
[ method ]
**
* Inserts given element before the current one.
**
> Parameters
**
- el (Element) element to insert
= (Element) parent
\*/
// TODO make it work for sets too
elproto.before = function (el) { elproto.before = function (el) {
el = wrap(el); el = wrap(el);
this.node.parentNode.insertBefore(el.node, this.node); this.node.parentNode.insertBefore(el.node, this.node);
el.paper = this.paper; el.paper = this.paper;
return this; return this;
}; };
/*\
* Element.after
[ method ]
**
* Inserts given element after the current one.
**
> Parameters
**
- el (Element) element to insert
= (Element) parent
\*/
elproto.after = function (el) { elproto.after = function (el) {
el = wrap(el); el = wrap(el);
this.node.parentNode.insertBefore(el.node, this.node.nextSibling); this.node.parentNode.insertBefore(el.node, this.node.nextSibling);
el.paper = this.paper; el.paper = this.paper;
return this; return this;
}; };
/*\
* Element.insertBefore
[ method ]
**
* Inserts the element after the given one.
**
> Parameters
**
- el (Element) element next to whom insert to
= (Element) parent
\*/
elproto.insertBefore = function (el) { elproto.insertBefore = function (el) {
el = wrap(el); el = wrap(el);
el.node.parentNode.insertBefore(this.node, el.node); el.node.parentNode.insertBefore(this.node, el.node);
this.paper = el.paper; this.paper = el.paper;
return this; return this;
}; };
/*\
* Element.insertAfter
[ method ]
**
* Inserts the element after the given one.
**
> Parameters
**
- el (Element) element next to whom insert to
= (Element) parent
\*/
elproto.insertAfter = function (el) { elproto.insertAfter = function (el) {
el = wrap(el); el = wrap(el);
el.node.parentNode.insertBefore(this.node, el.node.nextSibling); el.node.parentNode.insertBefore(this.node, el.node.nextSibling);
this.paper = el.paper; this.paper = el.paper;
return this; return this;
}; };
/*\
* Element.remove
[ method ]
**
* Removes element from the DOM
\*/
elproto.remove = function () { elproto.remove = function () {
this.node.parentNode.removeChild(this.node); this.node.parentNode.removeChild(this.node);
delete this.paper; delete this.paper;
this.removed = true; this.removed = true;
}; };
/*\
* Element.select
[ method ]
**
* Applies CSS selector with the element as a parent and returns the result as an @Element.
**
> Parameters
**
- query (string) CSS selector
= (Element) result of query selection
\*/
elproto.select = function (query) { elproto.select = function (query) {
return wrap(this.node.querySelector(query)); return wrap(this.node.querySelector(query));
}; };
/*\
* Element.selectAll
[ method ]
**
* Applies CSS selector with the element as a parent and returns the result as a set or array of elements.
**
> Parameters
**
- query (string) CSS selector
= (Set|array) result of query selection
\*/
elproto.selectAll = function (query) { elproto.selectAll = function (query) {
var nodelist = this.node.querySelectorAll(query), var nodelist = this.node.querySelectorAll(query),
set = (Savage.set || Array)(); set = (Savage.set || Array)();
@ -1496,9 +1670,32 @@ function arrayFirstValue(arr) {
} }
return set; return set;
}; };
/*\
* Element.asPX
[ method ]
**
* Return given attribute of the element as a `px` value. (Not %, em, etc)
**
> Parameters
**
- attr (string) attribute name
- value (string) #optional attribute value
= (Element) result of query selection
\*/
elproto.asPX = function (attr, value) { elproto.asPX = function (attr, value) {
if (value == null) {
value = this.attr(attr);
}
return unit2px(this, attr, value); return unit2px(this, attr, value);
}; };
/*\
* Element.use
[ method ]
**
* Creates `<use>` element linked to the current element.
**
= (Element) `<use>` element
\*/
elproto.use = function () { elproto.use = function () {
var use, var use,
id = this.node.id; id = this.node.id;
@ -1519,12 +1716,44 @@ function arrayFirstValue(arr) {
}); });
return use; return use;
}; };
/*\
* Element.clone
[ method ]
**
* Creates `<use>` element linked to the current element.
**
= (Element) `<use>` element
\*/
elproto.clone = function () { elproto.clone = function () {
var clone = this.node.cloneNode(true); var clone = wrap(this.node.cloneNode(true));
// TODO replace with this.insertAfter() clone.insertAfter(this);
this.node.parentNode.insertBefore(clone, this.node); return clone;
return wrap(clone);
}; };
/*\
* Element.pattern
[ method ]
**
* Creates `<pattern>` element from the current element.
**
> Parameters
**
* To create a pattern you have to specify the pattern rect:
- x (string|number)
- y (string|number)
- width (string|number)
- height (string|number)
= (Element) `<pattern>` element
* You can use pattern later on as an argument for `fill` attribute:
| var p = paper.path("M10-5-10,15M15,0,0,15M0-5-20,15").attr({
| fill: "none",
| stroke: "#bada55",
| strokeWidth: 5
| }).pattern(0, 0, 10, 10),
| c = paper.circle(200, 200, 100);
| c.attr({
| fill: p
| });
\*/
elproto.pattern = function (x, y, width, height) { elproto.pattern = function (x, y, width, height) {
var p = make("pattern", this.paper.defs); var p = make("pattern", this.paper.defs);
if (x == null) { if (x == null) {
@ -1548,6 +1777,25 @@ function arrayFirstValue(arr) {
p.node.appendChild(this.node); p.node.appendChild(this.node);
return p; return p;
}; };
/*\
* Element.marker
[ method ]
**
* Creates `<marker>` element from the current element.
**
> Parameters
**
* To create a marker you have to specify the bounding rect and reference point:
- x (number)
- y (number)
- width (number)
- height (number)
- refX (number)
- refY (number)
= (Element) `<marker>` element
* You can use pattern later on as an argument for `marker-start` or `marker-end` attributes.
\*/
// TODO add usage for markers
elproto.marker = function (x, y, width, height, refX, refY) { elproto.marker = function (x, y, width, height, refX, refY) {
var p = make("marker", this.paper.defs); var p = make("marker", this.paper.defs);
if (x == null) { if (x == null) {
@ -1557,8 +1805,8 @@ function arrayFirstValue(arr) {
y = x.y; y = x.y;
width = x.width; width = x.width;
height = x.height; height = x.height;
refX = x.refX; refX = x.refX || x.cx;
refY = x.refY; refY = x.refY || x.cy;
x = x.x; x = x.x;
} }
$(p.node, { $(p.node, {
@ -1583,7 +1831,77 @@ function arrayFirstValue(arr) {
return f ? f(res) : res; return f ? f(res) : res;
}; };
} }
elproto.animate = function (attrs, ms, callback) { var Animation = function (attr, ms, easing, callback) {
if (typeof easing == "function") {
callback = easing;
easing = mina.linear;
}
this.attr = attr;
this.dur = ms;
easing && (this.easing = easing);
callback && (this.callback = callback);
};
/*\
* Savage.animation
[ method ]
**
* Creates animation object.
**
> Parameters
**
- attr (object) attributes of final destination
- ms (number) animation duration
- easing (function) #optional one of easing functions of @mina or custom one
- callback (function) #optional callback
= (object) animation object
\*/
Savage.animation = function (attr, ms, easing, callback) {
return new Animation(attr, ms, easing, callback);
};
/*\
* Savage.inAnim
[ method ]
**
* to be continued...
**
= (object)
\*/
elproto.inAnim = function () {
var el = this,
res = [];
for (var id in el.anims) if (el.anims[has](id)) {
(function (a) {
res.push({
anim: new Animation(a._attrs, a.dur, a.easing, a._callback),
curStatus: a.status(),
status: function (val) {
return a.status(val);
}
});
}(el.anims[id]));
}
return res;
};
Savage.animate = function (from, to, setter, ms, easing, callback) {
if (typeof easing == "function") {
callback = easing;
easing = mina.linear;
}
var now = mina.time(),
anim = mina(from, to, now, now + ms, mina.time, setter, easing);
callback && eve.once("mina.finish." + anim.id, callback);
};
elproto.animate = function (attrs, ms, easing, callback) {
if (typeof easing == "function") {
callback = easing;
easing = mina.linear;
}
if (attrs instanceof Animation) {
callback = attrs.callback;
easing = attrs.easing;
ms = easing.dur;
attrs = attrs.attr;
}
var fkeys = [], tkeys = [], keys = {}, from, to, f, eq; var fkeys = [], tkeys = [], keys = {}, from, to, f, eq;
for (var key in attrs) if (attrs[has](key)) { for (var key in attrs) if (attrs[has](key)) {
if (this.equal) { if (this.equal) {
@ -1608,9 +1926,16 @@ function arrayFirstValue(arr) {
attr[key] = keys[key](val); attr[key] = keys[key](val);
} }
el.attr(attr); el.attr(attr);
}, easing);
el.anims[anim.id] = anim;
anim._attrs = attrs;
anim._callback = callback;
eve.once("mina.finish." + anim.id, function () {
delete el.anims[anim.id];
callback && callback.call(el);
}); });
callback && eve.once("mina.finish." + anim.id, function () { eve.once("mina.stop." + anim.id, function () {
callback.call(el); delete el.anims[anim.id];
}); });
}; };
}(Element.prototype)); }(Element.prototype));