Markers, Animation of colour, transform and path, mouse and touch events
parent
2270a96f12
commit
8766ae2c79
142
demojs.html
142
demojs.html
|
@ -13,6 +13,8 @@
|
|||
<script src="elemental.js"></script>
|
||||
<script src="svg.js"></script>
|
||||
<script src="savage.set.js"></script>
|
||||
<script src="savage.equal.js"></script>
|
||||
<script src="savage.mouse.js"></script>
|
||||
<script type="text/x-svg" id="svg">
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
@ -30,66 +32,88 @@
|
|||
</script>
|
||||
<script>
|
||||
window.onload = function () {
|
||||
// 1
|
||||
var s = Savage("100%", 600);
|
||||
// 2
|
||||
var c = s.circle(100, 100, 50);
|
||||
// 3
|
||||
c.attr({
|
||||
fill: "#bada55",
|
||||
stroke: "#000",
|
||||
"stroke-width": 5
|
||||
});
|
||||
// 4
|
||||
var c2 = s.circle(70, 100, 40);
|
||||
var g = s.group(c2, s.circle(130, 100, 40));
|
||||
g.attr({
|
||||
fill: "#fff"
|
||||
});
|
||||
// 5
|
||||
c.attr({
|
||||
mask: g
|
||||
});
|
||||
// 6
|
||||
c2.animate({r: 25}, 1000);
|
||||
// 7
|
||||
g.select("circle:nth-child(2)").animate({r: 25}, 1000);
|
||||
// 8
|
||||
var p = s.path("M10-5-10,15M15,0,0,15M0-5-20,15").attr({
|
||||
fill: "none",
|
||||
stroke: "#bada55",
|
||||
strokeWidth: 5
|
||||
}).pattern(0, 0, 10, 10);
|
||||
c.attr({
|
||||
fill: p
|
||||
});
|
||||
// 9
|
||||
g.attr({
|
||||
fill: Savage(document.getElementById("pattern"))
|
||||
});
|
||||
// 10
|
||||
g.attr({fill: "r()#fff-#000"});
|
||||
// 11
|
||||
g.attr({fill: "R(100, 100, 50)#fff-#000"});
|
||||
// 12
|
||||
var svg = document.getElementById("svg").text;
|
||||
var f = Savage.fragment(svg);
|
||||
f.select("path[fill='#D40000']").attr({fill: "#bada55"});
|
||||
g = s.group(f.selectAll("path"));
|
||||
s.append(g);
|
||||
// 13
|
||||
g.attr({
|
||||
transform: "r45t100,0s.5"
|
||||
});
|
||||
// 1;
|
||||
// var s = Savage("100%", 600);
|
||||
//
|
||||
// 2;
|
||||
// var c = s.circle(100, 100, 50);
|
||||
//
|
||||
// 3;
|
||||
// c.attr({
|
||||
// fill: "#bada55",
|
||||
// stroke: "#000",
|
||||
// "stroke-width": 5
|
||||
// });
|
||||
//
|
||||
// 4;
|
||||
// var c2 = s.circle(70, 100, 40);
|
||||
// var g = s.group(c2, s.circle(130, 100, 40));
|
||||
// g.attr({
|
||||
// fill: "#fff"
|
||||
// });
|
||||
//
|
||||
// 5;
|
||||
// c.attr({
|
||||
// mask: g
|
||||
// });
|
||||
//
|
||||
// 6;
|
||||
// c2.animate({r: 25}, 1000);
|
||||
//
|
||||
// 7;
|
||||
// g.select("circle:nth-child(2)").animate({r: 25}, 1000);
|
||||
//
|
||||
// 8;
|
||||
// var p = s.path("M10-5-10,15M15,0,0,15M0-5-20,15").attr({
|
||||
// fill: "none",
|
||||
// stroke: "#bada55",
|
||||
// strokeWidth: 5
|
||||
// }).pattern(0, 0, 10, 10);
|
||||
// c.attr({
|
||||
// fill: p
|
||||
// });
|
||||
//
|
||||
// 9;
|
||||
// g.attr({
|
||||
// fill: Savage(document.getElementById("pattern"))
|
||||
// });
|
||||
//
|
||||
// 10;
|
||||
// g.attr({fill: "r()#fff-#000"});
|
||||
//
|
||||
// 11;
|
||||
// g.attr({fill: "R(100, 100, 50)#fff-#000"});
|
||||
//
|
||||
// 12;
|
||||
// p.select("path").animate({stroke: "#f00"}, 1000);
|
||||
//
|
||||
// 13;
|
||||
// var svg = document.getElementById("svg").text;
|
||||
// var f = Savage.fragment(svg);
|
||||
// // f.select("path[fill='#D40000']").attr({fill: "#bada55"});
|
||||
// g = s.group(f.selectAll("path"));
|
||||
// s.append(g);
|
||||
//
|
||||
// 14;
|
||||
// g.attr({
|
||||
// transform: "r45t100,0s.5"
|
||||
// });
|
||||
//
|
||||
// 15;
|
||||
// console.log(g.attr("transform"));
|
||||
// 14
|
||||
s.text(200, 100, "SAVAGE");
|
||||
// 15
|
||||
var t = s.text(200, 120, "SAVAGE".split(""));
|
||||
t.selectAll("tspan tspan:nth-child(odd)").attr({
|
||||
fill: "#900",
|
||||
"font-size": "20px"
|
||||
});
|
||||
//
|
||||
// 16;
|
||||
// g.drag();
|
||||
//
|
||||
// 17;
|
||||
// s.text(200, 100, "SAVAGE");
|
||||
//
|
||||
// 18;
|
||||
// var t = s.text(200, 120, "SAVAGE".split(""));
|
||||
// t.selectAll("tspan tspan:nth-child(odd)").attr({
|
||||
// fill: "#900",
|
||||
// "font-size": "20px"
|
||||
// });
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
|
|
29
mina.js
29
mina.js
|
@ -21,7 +21,18 @@ window.mina = (function () {
|
|||
function (callback) {
|
||||
setTimeout(callback, 16);
|
||||
},
|
||||
isArray = Array.isArray || function (a) {
|
||||
return a instanceof Array ||
|
||||
Object.prototype.toString.call(a) == "[object Array]";
|
||||
},
|
||||
diff = function (a, b, A, B) {
|
||||
if (isArray(a)) {
|
||||
res = [];
|
||||
for (var i = 0, ii = a.length; i < ii; i++) {
|
||||
res[i] = diff(a[i], b, A[i], B);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
var dif = (A - a) / (B - b);
|
||||
return function (bb) {
|
||||
return a + dif * (bb - b);
|
||||
|
@ -31,12 +42,24 @@ window.mina = (function () {
|
|||
return +new Date;
|
||||
},
|
||||
frame = function () {
|
||||
var value, one;
|
||||
for (var i = 0; i < animations.length; i++) {
|
||||
var a = animations[i],
|
||||
gen = a.b + (a.gen() - a.b) * a["*"] + a["+"],
|
||||
value = a.dif(gen),
|
||||
gen = a.b + (a.gen() - a.b) * a["*"] + a["+"];
|
||||
if (isArray(a.a)) {
|
||||
value = [];
|
||||
for (var j = 0, jj = a.a.length; j < jj; j++) {
|
||||
value[j] = a.dif[j](gen);
|
||||
one = a.A[j] - a.a[j];
|
||||
value[j] = one ?
|
||||
a.a[j] + a.easing((value[j] - a.a[j]) / one) * one :
|
||||
a.a[j];
|
||||
}
|
||||
} else {
|
||||
value = a.dif(gen);
|
||||
one = a.A - a.a;
|
||||
value = a.a + a.easing((value - a.a) / one) * one;
|
||||
value = a.a + a.easing((value - a.a) / one) * one;
|
||||
}
|
||||
try {
|
||||
if (a.stopper(gen)) {
|
||||
if (--a.iterations) {
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
Savage.plugin(function (Savage, Element, Paper, glob) {
|
||||
var names = {},
|
||||
reUnit = /[a-z]+$/i,
|
||||
Str = String;
|
||||
names.stroke = names.fill = "colour";
|
||||
function getEmpty(item) {
|
||||
var l = item[0];
|
||||
switch (l.toLowerCase()) {
|
||||
case "t": return [l, 0, 0];
|
||||
case "m": return [l, 1, 0, 0, 1, 0, 0];
|
||||
case "r": if (item.length == 4) {
|
||||
return [l, 0, item[2], item[3]];
|
||||
} else {
|
||||
return [l, 0];
|
||||
}
|
||||
case "s": if (item.length == 5) {
|
||||
return [l, 1, 1, item[3], item[4]];
|
||||
} else if (item.length == 3) {
|
||||
return [l, 1, 1];
|
||||
} else {
|
||||
return [l, 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
function equaliseTransform(t1, t2) {
|
||||
t2 = Str(t2).replace(/\.{3}|\u2026/g, t1);
|
||||
t1 = Savage.parseTransformString(t1) || [];
|
||||
t2 = Savage.parseTransformString(t2) || [];
|
||||
var maxlength = Math.max(t1.length, t2.length),
|
||||
from = [],
|
||||
to = [],
|
||||
i = 0, j, jj,
|
||||
tt1, tt2;
|
||||
for (; i < maxlength; i++) {
|
||||
tt1 = t1[i] || getEmpty(t2[i]);
|
||||
tt2 = t2[i] || getEmpty(tt1);
|
||||
if ((tt1[0] != tt2[0]) ||
|
||||
(tt1[0].toLowerCase() == "r" && (tt1[2] != tt2[2] || tt1[3] != tt2[3])) ||
|
||||
(tt1[0].toLowerCase() == "s" && (tt1[3] != tt2[3] || tt1[4] != tt2[4]))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
from[i] = [];
|
||||
to[i] = [];
|
||||
for (j = 0, jj = Math.max(tt1.length, tt2.length); j < jj; j++) {
|
||||
j in tt1 && (from[i][j] = tt1[j]);
|
||||
j in tt2 && (to[i][j] = tt2[j]);
|
||||
}
|
||||
}
|
||||
return {
|
||||
from: path2array(from),
|
||||
to: path2array(to),
|
||||
f: getPath(from)
|
||||
};
|
||||
}
|
||||
function getNumber(val) {
|
||||
return val;
|
||||
}
|
||||
function getUnit(unit) {
|
||||
return function (val) {
|
||||
return +val.toFixed(3) + unit;
|
||||
};
|
||||
}
|
||||
function getColour(clr) {
|
||||
return Savage.rgb(clr[0], clr[1], clr[2]);
|
||||
}
|
||||
function getPath(path) {
|
||||
var k = 0, i, ii, j, jj, out, a, b = [];
|
||||
for (i = 0, ii = path.length; i < ii; i++) {
|
||||
out = "[";
|
||||
a = ['"' + path[i][0] + '"'];
|
||||
for (j = 1, jj = path[i].length; j < jj; j++) {
|
||||
a[j] = "val[" + (k++) + "]";
|
||||
}
|
||||
out += a + "]";
|
||||
b[i] = out;
|
||||
}
|
||||
return Function("val", "return Savage.path2string.call([" + b + "]);");
|
||||
}
|
||||
function path2array(path) {
|
||||
var out = [];
|
||||
for (var i = 0, ii = path.length; i < ii; i++) {
|
||||
for (var j = 1, jj = path[i].length; j < jj; j++) {
|
||||
out.push(path[i][j]);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
Element.prototype.equal = function (name, b) {
|
||||
var A, B, a = Str(this.attr(name) || "");
|
||||
if (a == +a && b == +b) {
|
||||
return {
|
||||
from: +a,
|
||||
to: +b,
|
||||
f: getNumber
|
||||
};
|
||||
}
|
||||
if (names[name] == "colour") {
|
||||
A = Savage.color(a);
|
||||
B = Savage.color(b);
|
||||
return {
|
||||
from: [A.r, A.g, A.b],
|
||||
to: [B.r, B.g, B.b],
|
||||
f: getColour
|
||||
};
|
||||
}
|
||||
if (name == "transform" || name == "gradientTransform" || name == "patternTransform") {
|
||||
// TODO: b could be an SVG transform string or matrix
|
||||
return equaliseTransform(a.local, b);
|
||||
}
|
||||
if (name == "d" || name == "path") {
|
||||
A = Savage.path2curve(a, b);
|
||||
return {
|
||||
from: path2array(A[0]),
|
||||
to: path2array(A[1]),
|
||||
f: getPath(A[0])
|
||||
};
|
||||
}
|
||||
var aUnit = a.match(reUnit),
|
||||
bUnit = b.match(reUnit);
|
||||
if (aUnit && aUnit == bUnit) {
|
||||
return {
|
||||
from: parseFloat(a),
|
||||
to: parseFloat(b),
|
||||
f: getUnit(aUnit)
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
from: this.asPX(name),
|
||||
to: this.asPX(name, b),
|
||||
f: getNumber
|
||||
};
|
||||
}
|
||||
};
|
||||
});
|
|
@ -0,0 +1,540 @@
|
|||
Savage.plugin(function (Savage, Element, Paper, glob) {
|
||||
var elproto = Element.prototype,
|
||||
has = "hasOwnProperty",
|
||||
supportsTouch = "createTouch" in glob.doc,
|
||||
events = [
|
||||
"click", "dblclick", "mousedown", "mousemove", "mouseout",
|
||||
"mouseover", "mouseup", "touchstart", "touchmove", "touchend",
|
||||
"touchcancel"
|
||||
],
|
||||
touchMap = {
|
||||
mousedown: "touchstart",
|
||||
mousemove: "touchmove",
|
||||
mouseup: "touchend"
|
||||
},
|
||||
getScroll = function (xy) {
|
||||
var name = xy == "y" ? "scrollTop" : "scrollLeft";
|
||||
return glob.doc.documentElement[name] || glob.doc.body[name];
|
||||
},
|
||||
preventDefault = function () {
|
||||
this.returnValue = false;
|
||||
},
|
||||
preventTouch = function () {
|
||||
return this.originalEvent.preventDefault();
|
||||
},
|
||||
stopPropagation = function () {
|
||||
this.cancelBubble = true;
|
||||
},
|
||||
stopTouch = function () {
|
||||
return this.originalEvent.stopPropagation();
|
||||
},
|
||||
addEvent = (function () {
|
||||
if (glob.doc.addEventListener) {
|
||||
return function (obj, type, fn, element) {
|
||||
var realName = supportsTouch && touchMap[type] ? touchMap[type] : type,
|
||||
f = function (e) {
|
||||
var scrollY = getScroll("y"),
|
||||
scrollX = getScroll("x"),
|
||||
x = e.clientX + scrollX,
|
||||
y = e.clientY + scrollY;
|
||||
if (supportsTouch && touchMap[has](type)) {
|
||||
for (var i = 0, ii = e.targetTouches && e.targetTouches.length; i < ii; i++) {
|
||||
if (e.targetTouches[i].target == obj) {
|
||||
var olde = e;
|
||||
e = e.targetTouches[i];
|
||||
e.originalEvent = olde;
|
||||
e.preventDefault = preventTouch;
|
||||
e.stopPropagation = stopTouch;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return fn.call(element, e, x, y);
|
||||
};
|
||||
obj.addEventListener(realName, f, false);
|
||||
return function () {
|
||||
obj.removeEventListener(realName, f, false);
|
||||
return true;
|
||||
};
|
||||
};
|
||||
} else if (glob.doc.attachEvent) {
|
||||
return function (obj, type, fn, element) {
|
||||
var f = function (e) {
|
||||
e = e || glob.win.event;
|
||||
var scrollY = getScroll("y"),
|
||||
scrollX = getScroll("x"),
|
||||
x = e.clientX + scrollX,
|
||||
y = e.clientY + scrollY;
|
||||
e.preventDefault = e.preventDefault || preventDefault;
|
||||
e.stopPropagation = e.stopPropagation || stopPropagation;
|
||||
return fn.call(element, e, x, y);
|
||||
};
|
||||
obj.attachEvent("on" + type, f);
|
||||
var detacher = function () {
|
||||
obj.detachEvent("on" + type, f);
|
||||
return true;
|
||||
};
|
||||
return detacher;
|
||||
};
|
||||
}
|
||||
})(),
|
||||
drag = [],
|
||||
dragMove = function (e) {
|
||||
var x = e.clientX,
|
||||
y = e.clientY,
|
||||
scrollY = getScroll("y"),
|
||||
scrollX = getScroll("x"),
|
||||
dragi,
|
||||
j = drag.length;
|
||||
while (j--) {
|
||||
dragi = drag[j];
|
||||
if (supportsTouch) {
|
||||
var i = e.touches.length,
|
||||
touch;
|
||||
while (i--) {
|
||||
touch = e.touches[i];
|
||||
if (touch.identifier == dragi.el._drag.id) {
|
||||
x = touch.clientX;
|
||||
y = touch.clientY;
|
||||
(e.originalEvent ? e.originalEvent : e).preventDefault();
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
e.preventDefault();
|
||||
}
|
||||
var node = dragi.el.node,
|
||||
o,
|
||||
next = node.nextSibling,
|
||||
parent = node.parentNode,
|
||||
display = node.style.display;
|
||||
// glob.win.opera && parent.removeChild(node);
|
||||
// node.style.display = "none";
|
||||
// o = dragi.el.paper.getElementByPoint(x, y);
|
||||
// node.style.display = display;
|
||||
// glob.win.opera && (next ? parent.insertBefore(node, next) : parent.appendChild(node));
|
||||
// o && eve("savage.drag.over." + dragi.el.id, dragi.el, o);
|
||||
x += scrollX;
|
||||
y += scrollY;
|
||||
eve("savage.drag.move." + dragi.el.id, dragi.move_scope || dragi.el, x - dragi.el._drag.x, y - dragi.el._drag.y, x, y, e);
|
||||
}
|
||||
},
|
||||
dragUp = function (e) {
|
||||
Savage.unmousemove(dragMove).unmouseup(dragUp);
|
||||
var i = drag.length,
|
||||
dragi;
|
||||
while (i--) {
|
||||
dragi = drag[i];
|
||||
dragi.el._drag = {};
|
||||
eve("savage.drag.end." + dragi.el.id, dragi.end_scope || dragi.start_scope || dragi.move_scope || dragi.el, e);
|
||||
}
|
||||
drag = [];
|
||||
};
|
||||
/*\
|
||||
* Element.click
|
||||
[ method ]
|
||||
**
|
||||
* Adds event handler for click for the element.
|
||||
> Parameters
|
||||
- handler (function) handler for the event
|
||||
= (object) @Element
|
||||
\*/
|
||||
/*\
|
||||
* Element.unclick
|
||||
[ method ]
|
||||
**
|
||||
* Removes event handler for click for the element.
|
||||
> Parameters
|
||||
- handler (function) handler for the event
|
||||
= (object) @Element
|
||||
\*/
|
||||
|
||||
/*\
|
||||
* Element.dblclick
|
||||
[ method ]
|
||||
**
|
||||
* Adds event handler for double click for the element.
|
||||
> Parameters
|
||||
- handler (function) handler for the event
|
||||
= (object) @Element
|
||||
\*/
|
||||
/*\
|
||||
* Element.undblclick
|
||||
[ method ]
|
||||
**
|
||||
* Removes event handler for double click for the element.
|
||||
> Parameters
|
||||
- handler (function) handler for the event
|
||||
= (object) @Element
|
||||
\*/
|
||||
|
||||
/*\
|
||||
* Element.mousedown
|
||||
[ method ]
|
||||
**
|
||||
* Adds event handler for mousedown for the element.
|
||||
> Parameters
|
||||
- handler (function) handler for the event
|
||||
= (object) @Element
|
||||
\*/
|
||||
/*\
|
||||
* Element.unmousedown
|
||||
[ method ]
|
||||
**
|
||||
* Removes event handler for mousedown for the element.
|
||||
> Parameters
|
||||
- handler (function) handler for the event
|
||||
= (object) @Element
|
||||
\*/
|
||||
|
||||
/*\
|
||||
* Element.mousemove
|
||||
[ method ]
|
||||
**
|
||||
* Adds event handler for mousemove for the element.
|
||||
> Parameters
|
||||
- handler (function) handler for the event
|
||||
= (object) @Element
|
||||
\*/
|
||||
/*\
|
||||
* Element.unmousemove
|
||||
[ method ]
|
||||
**
|
||||
* Removes event handler for mousemove for the element.
|
||||
> Parameters
|
||||
- handler (function) handler for the event
|
||||
= (object) @Element
|
||||
\*/
|
||||
|
||||
/*\
|
||||
* Element.mouseout
|
||||
[ method ]
|
||||
**
|
||||
* Adds event handler for mouseout for the element.
|
||||
> Parameters
|
||||
- handler (function) handler for the event
|
||||
= (object) @Element
|
||||
\*/
|
||||
/*\
|
||||
* Element.unmouseout
|
||||
[ method ]
|
||||
**
|
||||
* Removes event handler for mouseout for the element.
|
||||
> Parameters
|
||||
- handler (function) handler for the event
|
||||
= (object) @Element
|
||||
\*/
|
||||
|
||||
/*\
|
||||
* Element.mouseover
|
||||
[ method ]
|
||||
**
|
||||
* Adds event handler for mouseover for the element.
|
||||
> Parameters
|
||||
- handler (function) handler for the event
|
||||
= (object) @Element
|
||||
\*/
|
||||
/*\
|
||||
* Element.unmouseover
|
||||
[ method ]
|
||||
**
|
||||
* Removes event handler for mouseover for the element.
|
||||
> Parameters
|
||||
- handler (function) handler for the event
|
||||
= (object) @Element
|
||||
\*/
|
||||
|
||||
/*\
|
||||
* Element.mouseup
|
||||
[ method ]
|
||||
**
|
||||
* Adds event handler for mouseup for the element.
|
||||
> Parameters
|
||||
- handler (function) handler for the event
|
||||
= (object) @Element
|
||||
\*/
|
||||
/*\
|
||||
* Element.unmouseup
|
||||
[ method ]
|
||||
**
|
||||
* Removes event handler for mouseup for the element.
|
||||
> Parameters
|
||||
- handler (function) handler for the event
|
||||
= (object) @Element
|
||||
\*/
|
||||
|
||||
/*\
|
||||
* Element.touchstart
|
||||
[ method ]
|
||||
**
|
||||
* Adds event handler for touchstart for the element.
|
||||
> Parameters
|
||||
- handler (function) handler for the event
|
||||
= (object) @Element
|
||||
\*/
|
||||
/*\
|
||||
* Element.untouchstart
|
||||
[ method ]
|
||||
**
|
||||
* Removes event handler for touchstart for the element.
|
||||
> Parameters
|
||||
- handler (function) handler for the event
|
||||
= (object) @Element
|
||||
\*/
|
||||
|
||||
/*\
|
||||
* Element.touchmove
|
||||
[ method ]
|
||||
**
|
||||
* Adds event handler for touchmove for the element.
|
||||
> Parameters
|
||||
- handler (function) handler for the event
|
||||
= (object) @Element
|
||||
\*/
|
||||
/*\
|
||||
* Element.untouchmove
|
||||
[ method ]
|
||||
**
|
||||
* Removes event handler for touchmove for the element.
|
||||
> Parameters
|
||||
- handler (function) handler for the event
|
||||
= (object) @Element
|
||||
\*/
|
||||
|
||||
/*\
|
||||
* Element.touchend
|
||||
[ method ]
|
||||
**
|
||||
* Adds event handler for touchend for the element.
|
||||
> Parameters
|
||||
- handler (function) handler for the event
|
||||
= (object) @Element
|
||||
\*/
|
||||
/*\
|
||||
* Element.untouchend
|
||||
[ method ]
|
||||
**
|
||||
* Removes event handler for touchend for the element.
|
||||
> Parameters
|
||||
- handler (function) handler for the event
|
||||
= (object) @Element
|
||||
\*/
|
||||
|
||||
/*\
|
||||
* Element.touchcancel
|
||||
[ method ]
|
||||
**
|
||||
* Adds event handler for touchcancel for the element.
|
||||
> Parameters
|
||||
- handler (function) handler for the event
|
||||
= (object) @Element
|
||||
\*/
|
||||
/*\
|
||||
* Element.untouchcancel
|
||||
[ method ]
|
||||
**
|
||||
* Removes event handler for touchcancel for the element.
|
||||
> Parameters
|
||||
- handler (function) handler for the event
|
||||
= (object) @Element
|
||||
\*/
|
||||
for (var i = events.length; i--;) {
|
||||
(function (eventName) {
|
||||
Savage[eventName] = elproto[eventName] = function (fn, scope) {
|
||||
if (Savage.is(fn, "function")) {
|
||||
this.events = this.events || [];
|
||||
this.events.push({
|
||||
name: eventName,
|
||||
f: fn,
|
||||
unbind: addEvent(this.shape || this.node || glob.doc, eventName, fn, scope || this)
|
||||
});
|
||||
}
|
||||
return this;
|
||||
};
|
||||
Savage["un" + eventName] = elproto["un" + eventName] = function (fn) {
|
||||
var events = this.events || [],
|
||||
l = events.length;
|
||||
while (l--) if (events[l].name == eventName && events[l].f == fn) {
|
||||
events[l].unbind();
|
||||
events.splice(l, 1);
|
||||
!events.length && delete this.events;
|
||||
return this;
|
||||
}
|
||||
return this;
|
||||
};
|
||||
})(events[i]);
|
||||
}
|
||||
|
||||
/*\
|
||||
* Element.data
|
||||
[ method ]
|
||||
**
|
||||
* Adds or retrieves given value asociated with given key.
|
||||
**
|
||||
* See also @Element.removeData
|
||||
> Parameters
|
||||
- key (string) key to store data
|
||||
- value (any) #optional value to store
|
||||
= (object) @Element
|
||||
* or, if value is not specified:
|
||||
= (any) value
|
||||
> Usage
|
||||
| for (var i = 0, i < 5, i++) {
|
||||
| paper.circle(10 + 15 * i, 10, 10)
|
||||
| .attr({fill: "#000"})
|
||||
| .data("i", i)
|
||||
| .click(function () {
|
||||
| alert(this.data("i"));
|
||||
| });
|
||||
| }
|
||||
\*/
|
||||
elproto.data = function (key, value) {
|
||||
var data = eldata[this.id] = eldata[this.id] || {};
|
||||
if (arguments.length == 1) {
|
||||
if (Savage.is(key, "object")) {
|
||||
for (var i in key) if (key[has](i)) {
|
||||
this.data(i, key[i]);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
eve("savage.data.get." + this.id, this, data[key], key);
|
||||
return data[key];
|
||||
}
|
||||
data[key] = value;
|
||||
eve("savage.data.set." + this.id, this, value, key);
|
||||
return this;
|
||||
};
|
||||
/*\
|
||||
* Element.removeData
|
||||
[ method ]
|
||||
**
|
||||
* Removes value associated with an element by given key.
|
||||
* If key is not provided, removes all the data of the element.
|
||||
> Parameters
|
||||
- key (string) #optional key
|
||||
= (object) @Element
|
||||
\*/
|
||||
elproto.removeData = function (key) {
|
||||
if (key == null) {
|
||||
eldata[this.id] = {};
|
||||
} else {
|
||||
eldata[this.id] && delete eldata[this.id][key];
|
||||
}
|
||||
return this;
|
||||
};
|
||||
/*\
|
||||
* Element.hover
|
||||
[ method ]
|
||||
**
|
||||
* Adds event handlers for hover for the element.
|
||||
> Parameters
|
||||
- f_in (function) handler for hover in
|
||||
- f_out (function) handler for hover out
|
||||
- icontext (object) #optional context for hover in handler
|
||||
- ocontext (object) #optional context for hover out handler
|
||||
= (object) @Element
|
||||
\*/
|
||||
elproto.hover = function (f_in, f_out, scope_in, scope_out) {
|
||||
return this.mouseover(f_in, scope_in).mouseout(f_out, scope_out || scope_in);
|
||||
};
|
||||
/*\
|
||||
* Element.unhover
|
||||
[ method ]
|
||||
**
|
||||
* Removes event handlers for hover for the element.
|
||||
> Parameters
|
||||
- f_in (function) handler for hover in
|
||||
- f_out (function) handler for hover out
|
||||
= (object) @Element
|
||||
\*/
|
||||
elproto.unhover = function (f_in, f_out) {
|
||||
return this.unmouseover(f_in).unmouseout(f_out);
|
||||
};
|
||||
var draggable = [];
|
||||
/*\
|
||||
* Element.drag
|
||||
[ method ]
|
||||
**
|
||||
* Adds event handlers for drag of the element.
|
||||
> Parameters
|
||||
- onmove (function) handler for moving
|
||||
- onstart (function) handler for drag start
|
||||
- onend (function) handler for drag end
|
||||
- mcontext (object) #optional context for moving handler
|
||||
- scontext (object) #optional context for drag start handler
|
||||
- econtext (object) #optional context for drag end handler
|
||||
* Additionaly following `drag` events will be triggered: `drag.start.<id>` on start,
|
||||
* `drag.end.<id>` on end and `drag.move.<id>` on every move. When element will be dragged over another element
|
||||
* `drag.over.<id>` will be fired as well.
|
||||
*
|
||||
* Start event and start handler will be called in specified context or in context of the element with following parameters:
|
||||
o x (number) x position of the mouse
|
||||
o y (number) y position of the mouse
|
||||
o event (object) DOM event object
|
||||
* Move event and move handler will be called in specified context or in context of the element with following parameters:
|
||||
o dx (number) shift by x from the start point
|
||||
o dy (number) shift by y from the start point
|
||||
o x (number) x position of the mouse
|
||||
o y (number) y position of the mouse
|
||||
o event (object) DOM event object
|
||||
* End event and end handler will be called in specified context or in context of the element with following parameters:
|
||||
o event (object) DOM event object
|
||||
= (object) @Element
|
||||
\*/
|
||||
elproto.drag = function (onmove, onstart, onend, move_scope, start_scope, end_scope) {
|
||||
if (!arguments.length) {
|
||||
var origTransform;
|
||||
return this.drag(function (dx, dy) {
|
||||
this.attr({
|
||||
transform: origTransform + (origTransform ? "T" : "t") + [dx, dy]
|
||||
});
|
||||
}, function () {
|
||||
origTransform = this.transform().local;
|
||||
});
|
||||
}
|
||||
function start(e) {
|
||||
(e.originalEvent || e).preventDefault();
|
||||
var scrollY = getScroll("y"),
|
||||
scrollX = getScroll("x");
|
||||
this._drag.x = e.clientX + scrollX;
|
||||
this._drag.y = e.clientY + scrollY;
|
||||
this._drag.id = e.identifier;
|
||||
!drag.length && Savage.mousemove(dragMove).mouseup(dragUp);
|
||||
drag.push({el: this, move_scope: move_scope, start_scope: start_scope, end_scope: end_scope});
|
||||
onstart && eve.on("savage.drag.start." + this.id, onstart);
|
||||
onmove && eve.on("savage.drag.move." + this.id, onmove);
|
||||
onend && eve.on("savage.drag.end." + this.id, onend);
|
||||
eve("savage.drag.start." + this.id, start_scope || move_scope || this, e.clientX + scrollX, e.clientY + scrollY, e);
|
||||
}
|
||||
this._drag = {};
|
||||
draggable.push({el: this, start: start});
|
||||
this.mousedown(start);
|
||||
return this;
|
||||
};
|
||||
/*\
|
||||
* Element.onDragOver
|
||||
[ method ]
|
||||
**
|
||||
* Shortcut for assigning event handler for `drag.over.<id>` event, where id is id of the element (see @Element.id).
|
||||
> Parameters
|
||||
- f (function) handler for event, first argument would be the element you are dragging over
|
||||
\*/
|
||||
elproto.onDragOver = function (f) {
|
||||
f ? eve.on("savage.drag.over." + this.id, f) : eve.unbind("savage.drag.over." + this.id);
|
||||
};
|
||||
/*\
|
||||
* Element.undrag
|
||||
[ method ]
|
||||
**
|
||||
* Removes all drag event handlers from given element.
|
||||
\*/
|
||||
elproto.undrag = function () {
|
||||
var i = draggable.length;
|
||||
while (i--) if (draggable[i].el == this) {
|
||||
this.unmousedown(draggable[i].start);
|
||||
draggable.splice(i, 1);
|
||||
eve.unbind("savage.drag.*." + this.id);
|
||||
}
|
||||
!draggable.length && Savage.unmousemove(dragMove).unmouseup(dragUp);
|
||||
};
|
||||
});
|
247
svg.js
247
svg.js
|
@ -37,6 +37,7 @@ var has = "hasOwnProperty",
|
|||
colourRegExp = /^\s*((#[a-f\d]{6})|(#[a-f\d]{3})|rgba?\(\s*([\d\.]+%?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+%?(?:\s*,\s*[\d\.]+%?)?)\s*\)|hsba?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\)|hsla?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\))\s*$/i,
|
||||
isnan = {"NaN": 1, "Infinity": 1, "-Infinity": 1},
|
||||
bezierrg = /^(?:cubic-)?bezier\(([^,]+),([^,]+),([^,]+),([^\)]+)\)/,
|
||||
reURLValue = /^url\(#?([^)]+)\)$/,
|
||||
spaces = "\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029",
|
||||
separator = new RegExp("[," + spaces + "]+"),
|
||||
whitespace = new RegExp("[" + spaces + "]", "g"),
|
||||
|
@ -860,15 +861,105 @@ function box(x, y, width, height) {
|
|||
};
|
||||
}
|
||||
// Transformations
|
||||
function path2string() {
|
||||
var path2string = Savage.path2string = function () {
|
||||
return this.join(",").replace(p2s, "$1");
|
||||
}
|
||||
};
|
||||
function pathClone(pathArray) {
|
||||
var res = clone(pathArray);
|
||||
res.toString = path2string;
|
||||
return res;
|
||||
}
|
||||
function parseTransformString(TString) {
|
||||
|
||||
// http://schepers.cc/getting-to-the-point
|
||||
function catmullRom2bezier(crp, z) {
|
||||
var d = [];
|
||||
for (var i = 0, iLen = crp.length; iLen - 2 * !z > i; i += 2) {
|
||||
var p = [
|
||||
{x: +crp[i - 2], y: +crp[i - 1]},
|
||||
{x: +crp[i], y: +crp[i + 1]},
|
||||
{x: +crp[i + 2], y: +crp[i + 3]},
|
||||
{x: +crp[i + 4], y: +crp[i + 5]}
|
||||
];
|
||||
if (z) {
|
||||
if (!i) {
|
||||
p[0] = {x: +crp[iLen - 2], y: +crp[iLen - 1]};
|
||||
} else if (iLen - 4 == i) {
|
||||
p[3] = {x: +crp[0], y: +crp[1]};
|
||||
} else if (iLen - 2 == i) {
|
||||
p[2] = {x: +crp[0], y: +crp[1]};
|
||||
p[3] = {x: +crp[2], y: +crp[3]};
|
||||
}
|
||||
} else {
|
||||
if (iLen - 4 == i) {
|
||||
p[3] = p[2];
|
||||
} else if (!i) {
|
||||
p[0] = {x: +crp[i], y: +crp[i + 1]};
|
||||
}
|
||||
}
|
||||
d.push(["C",
|
||||
(-p[0].x + 6 * p[1].x + p[2].x) / 6,
|
||||
(-p[0].y + 6 * p[1].y + p[2].y) / 6,
|
||||
(p[1].x + 6 * p[2].x - p[3].x) / 6,
|
||||
(p[1].y + 6*p[2].y - p[3].y) / 6,
|
||||
p[2].x,
|
||||
p[2].y
|
||||
]);
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
/*\
|
||||
* Savage.parsePathString
|
||||
[ method ]
|
||||
**
|
||||
* Utility method
|
||||
**
|
||||
* Parses given path string into an array of arrays of path segments.
|
||||
> Parameters
|
||||
- pathString (string|array) path string or array of segments (in the last case it will be returned straight away)
|
||||
= (array) array of segments.
|
||||
\*/
|
||||
Savage.parsePathString = function (pathString) {
|
||||
if (!pathString) {
|
||||
return null;
|
||||
}
|
||||
var pth = paths(pathString);
|
||||
if (pth.arr) {
|
||||
return pathClone(pth.arr);
|
||||
}
|
||||
|
||||
var paramCounts = {a: 7, c: 6, h: 1, l: 2, m: 2, r: 4, q: 4, s: 4, t: 2, v: 1, z: 0},
|
||||
data = [];
|
||||
if (is(pathString, "array") && is(pathString[0], "array")) { // rough assumption
|
||||
data = pathClone(pathString);
|
||||
}
|
||||
if (!data.length) {
|
||||
Str(pathString).replace(pathCommand, function (a, b, c) {
|
||||
var params = [],
|
||||
name = b.toLowerCase();
|
||||
c.replace(pathValues, function (a, b) {
|
||||
b && params.push(+b);
|
||||
});
|
||||
if (name == "m" && params.length > 2) {
|
||||
data.push([b].concat(params.splice(0, 2)));
|
||||
name = "l";
|
||||
b = b == "m" ? "l" : "L";
|
||||
}
|
||||
if (name == "r") {
|
||||
data.push([b].concat(params));
|
||||
} else while (params.length >= paramCounts[name]) {
|
||||
data.push([b].concat(params.splice(0, paramCounts[name])));
|
||||
if (!paramCounts[name]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
data.toString = path2string;
|
||||
pth.arr = pathClone(data);
|
||||
return data;
|
||||
};
|
||||
var parseTransformString = Savage.parseTransformString = function (TString) {
|
||||
if (!TString) {
|
||||
return null;
|
||||
}
|
||||
|
@ -889,7 +980,7 @@ function parseTransformString(TString) {
|
|||
}
|
||||
data.toString = path2string;
|
||||
return data;
|
||||
}
|
||||
};
|
||||
function svgTransform2string(tstr) {
|
||||
var res = [];
|
||||
tstr = tstr.replace(/(?:^|\s)(\w+)\(([^)]+)\)/g, function (all, name, params) {
|
||||
|
@ -1086,7 +1177,7 @@ function ellipsePath(x, y, rx, ry, a) {
|
|||
res.toString = path2string;
|
||||
return res;
|
||||
}
|
||||
function unit2px(el) {
|
||||
function unit2px(el, name, value) {
|
||||
var defs = el.paper.defs,
|
||||
out = {},
|
||||
mgr = el.paper.measurer;
|
||||
|
@ -1115,28 +1206,57 @@ function unit2px(el) {
|
|||
$(mgr, {height: val});
|
||||
return mgr.getBBox().height;
|
||||
}
|
||||
function set(nam, f) {
|
||||
if (name == null) {
|
||||
out[nam] = f(el.attr(nam));
|
||||
} else if (nam == name) {
|
||||
out = f(value == null ? el.attr(nam) : value);
|
||||
}
|
||||
}
|
||||
switch (el.type) {
|
||||
case "rect":
|
||||
out.rx = getW(el.attr("rx"));
|
||||
out.ry = getH(el.attr("ry"));
|
||||
set("rx", getW);
|
||||
set("ry", getH);
|
||||
case "image":
|
||||
out.width = getW(el.attr("width"));
|
||||
out.height = getH(el.attr("height"));
|
||||
set("width", getW);
|
||||
set("height", getH);
|
||||
case "text":
|
||||
out.x = getW(el.attr("x"));
|
||||
out.y = getH(el.attr("y"));
|
||||
set("x", getW);
|
||||
set("y", getH);
|
||||
break;
|
||||
case "circle":
|
||||
out.cx = getW(el.attr("cx"));
|
||||
out.cy = getH(el.attr("cy"));
|
||||
out.r = getW(el.attr("r"));
|
||||
set("cx", getW);
|
||||
set("cy", getH);
|
||||
set("r", getW);
|
||||
break;
|
||||
case "ellipse":
|
||||
out.cx = getW(el.attr("cx"));
|
||||
out.cy = getH(el.attr("cy"));
|
||||
out.rx = getW(el.attr("rx"));
|
||||
out.ry = getH(el.attr("ry"));
|
||||
set("cx", getW);
|
||||
set("cy", getH);
|
||||
set("rx", getW);
|
||||
set("ry", getH);
|
||||
break;
|
||||
case "line":
|
||||
set("x1", getW);
|
||||
set("x2", getW);
|
||||
set("y1", getH);
|
||||
set("y2", getH);
|
||||
break;
|
||||
case "marker":
|
||||
set("refX", getW);
|
||||
set("markerWidth", getW);
|
||||
set("refY", getH);
|
||||
set("markerHeight", getH);
|
||||
break;
|
||||
case "radialGradient":
|
||||
set("fx", getW);
|
||||
set("fy", getH);
|
||||
break;
|
||||
case "tspan":
|
||||
set("dx", getW);
|
||||
set("dy", getH);
|
||||
break;
|
||||
default:
|
||||
out = null;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
@ -1223,7 +1343,7 @@ var pathDimensions = function (path) {
|
|||
return pathClone(pth.rel);
|
||||
}
|
||||
if (!is(pathArray, array) || !is(pathArray && pathArray[0], array)) { // rough assumption
|
||||
pathArray = parsePathString(pathArray);
|
||||
pathArray = Savage.parsePathString(pathArray);
|
||||
}
|
||||
var res = [],
|
||||
x = 0,
|
||||
|
@ -1302,7 +1422,7 @@ var pathDimensions = function (path) {
|
|||
return pathClone(pth.abs);
|
||||
}
|
||||
if (!is(pathArray, "array") || !is(pathArray && pathArray[0], "array")) { // rough assumption
|
||||
pathArray = parsePathString(pathArray);
|
||||
pathArray = Savage.parsePathString(pathArray);
|
||||
}
|
||||
if (!pathArray || !pathArray.length) {
|
||||
return [["M", 0, 0]];
|
||||
|
@ -1565,7 +1685,7 @@ var pathDimensions = function (path) {
|
|||
max: {x: mmax.apply(0, x), y: mmax.apply(0, y)}
|
||||
};
|
||||
}),
|
||||
path2curve = cacher(function (path, path2) {
|
||||
path2curve = Savage.path2curve = cacher(function (path, path2) {
|
||||
var pth = !path2 && paths(path);
|
||||
if (!path2 && pth.curve) {
|
||||
return pathClone(pth.curve);
|
||||
|
@ -1764,17 +1884,9 @@ function arrayFirstValue(arr) {
|
|||
}
|
||||
return box(_.bbox);
|
||||
};
|
||||
function prop(name) {
|
||||
return function () {
|
||||
return this[name];
|
||||
};
|
||||
}
|
||||
function always(x) {
|
||||
return function () {
|
||||
return x;
|
||||
};
|
||||
}
|
||||
var propString = prop("string");
|
||||
var propString = function () {
|
||||
return this.local;
|
||||
};
|
||||
elproto.transform = function (tstr) {
|
||||
var _ = this._;
|
||||
if (tstr == null) {
|
||||
|
@ -1864,6 +1976,9 @@ function arrayFirstValue(arr) {
|
|||
}
|
||||
return set;
|
||||
};
|
||||
elproto.asPX = function (attr, value) {
|
||||
return unit2px(this, attr, value);
|
||||
};
|
||||
elproto.use = function () {
|
||||
var use,
|
||||
id = this.node.id;
|
||||
|
@ -1939,17 +2054,22 @@ function arrayFirstValue(arr) {
|
|||
return p;
|
||||
};
|
||||
// animation
|
||||
function applyAttr(el, key) {
|
||||
function applyAttr(el, key, f) {
|
||||
var at = {};
|
||||
return function (value) {
|
||||
at[key] = value;
|
||||
at[key] = f ? f(value) : value;
|
||||
el.attr(at);
|
||||
};
|
||||
}
|
||||
elproto.animate = function (attrs, ms, callback) {
|
||||
var anims = [];
|
||||
var anims = [], eq;
|
||||
for (var key in attrs) if (attrs[has](key)) {
|
||||
anims.push(mina(+this.attr(key), +attrs[key], ms, applyAttr(this, key)));
|
||||
if (this.equal) {
|
||||
eq = this.equal(key, Str(attrs[key]));
|
||||
anims.push(mina(eq.from, eq.to, ms, applyAttr(this, key, eq.f)));
|
||||
} else {
|
||||
anims.push(mina(+this.attr(key), +attrs[key], ms, applyAttr(this, key)));
|
||||
}
|
||||
}
|
||||
};
|
||||
}(Element.prototype));
|
||||
|
@ -2436,6 +2556,17 @@ eve.on("savage.util.grad.parse", function parseGrad(string) {
|
|||
};
|
||||
});
|
||||
|
||||
eve.on("savage.util.attr.d", function (value) {
|
||||
if (is(value, "array") && is(value[0], "array")) {
|
||||
value = path2string.call(value);
|
||||
}
|
||||
value = Str(value);
|
||||
if (value.match(/[ruo]/i)) {
|
||||
value = pathToAbsolute(value);
|
||||
}
|
||||
$(this.node, {d: value});
|
||||
eve.stop();
|
||||
});
|
||||
eve.on("savage.util.attr.path", function (value) {
|
||||
this.attr({d: value});
|
||||
eve.stop();
|
||||
|
@ -2528,6 +2659,50 @@ eve.on("savage.util.getattr.transform", function () {
|
|||
eve.stop();
|
||||
return this.transform();
|
||||
})(-1);
|
||||
// Markers
|
||||
(function () {
|
||||
function getter(end) {
|
||||
return function () {
|
||||
eve.stop();
|
||||
var style = glob.doc.defaultView.getComputedStyle(this.node, null).getPropertyValue("marker-" + end);
|
||||
if (style == "none") {
|
||||
return style;
|
||||
} else {
|
||||
return Savage(glob.doc.getElementById(style.match(reURLValue)[1]));
|
||||
}
|
||||
};
|
||||
}
|
||||
function setter(end) {
|
||||
return function (value) {
|
||||
eve.stop();
|
||||
var name = "marker" + end.charAt(0).toUpperCase() + end.substring(1);
|
||||
if (value == "" || !value) {
|
||||
this.node.style[name] = "none";
|
||||
return;
|
||||
}
|
||||
if (value.type == "marker") {
|
||||
var id = value.node.id;
|
||||
if (!id) {
|
||||
$(value.node, {id: value.id});
|
||||
}
|
||||
this.node.style[name] = "url(#" + id + ")";
|
||||
return;
|
||||
}
|
||||
};
|
||||
}
|
||||
eve.on("savage.util.getattr.marker-end", getter("end"))(-1);
|
||||
eve.on("savage.util.getattr.markerEnd", getter("end"))(-1);
|
||||
eve.on("savage.util.getattr.marker-start", getter("start"))(-1);
|
||||
eve.on("savage.util.getattr.markerStart", getter("start"))(-1);
|
||||
eve.on("savage.util.getattr.marker-mid", getter("mid"))(-1);
|
||||
eve.on("savage.util.getattr.markerMid", getter("mid"))(-1);
|
||||
eve.on("savage.util.attr.marker-end", setter("end"))(-1);
|
||||
eve.on("savage.util.attr.markerEnd", setter("end"))(-1);
|
||||
eve.on("savage.util.attr.marker-start", setter("start"))(-1);
|
||||
eve.on("savage.util.attr.markerStart", setter("start"))(-1);
|
||||
eve.on("savage.util.attr.marker-mid", setter("mid"))(-1);
|
||||
eve.on("savage.util.attr.markerMid", setter("mid"))(-1);
|
||||
}());
|
||||
eve.on("savage.util.getattr.r", function () {
|
||||
if (this.type == "rect" && $(this.node, "rx") == $(this.node, "ry")) {
|
||||
eve.stop();
|
||||
|
|
Loading…
Reference in New Issue