Snap静态方法 - bobby169/Snap.svgDoc GitHub Wiki
[TOC]
- width (number|string) width of surface
- height (number|string) height of surface
或:
- DOM (SVGElement) element to be wrapped into Snap structure
或:
- array (array) array of elements (will return set of elements)
或:
- query (string) CSS query selector
Creates a drawing surface or wraps existing SVG element.
function Snap(w, h) {
if (w) {
// 是一个dom元素
if (w.nodeType) {
return wrap(w);
}
if (is(w, "array") && Snap.set) {
return Snap.set.apply(Snap, w);
}
if (w instanceof Element) {
return w;
}
if (h == null) {
try {
// w为id或class
w = glob.doc.querySelector(String(w));
return wrap(w);
} catch (e) {
return null;
}
}
}
w = w == null ? "100%" : w;
h = h == null ? "100%" : h;
return new Paper(w, h);
}var idgen = 0,
idprefix = "S" + (+new Date).toString(36);
var ID = function (el) {
return (el && el.type ? el.type : E) + idprefix + (idgen++).toString(36);
}
function Element(el) {
if (el.snap in hub) {
return hub[el.snap];
}
var svg;
try {
svg = el.ownerSVGElement;
} catch(e) {}
/*\
* Element.node
[ property (object) ]
**
* Gives you a reference to the DOM object, so you can assign event handlers or just mess around.
> Usage
| // draw a circle at coordinate 10,10 with radius of 10
| var c = paper.circle(10, 10, 10);
| c.node.onclick = function () {
| c.attr("fill", "red");
| };
\*/
this.node = el;
if (svg) {
this.paper = new Paper(svg);
}
/*\
* Element.type
[ property (string) ]
**
* SVG tag name of the given element.
\*/
this.type = el.tagName || el.nodeName;
// 如果id不存在,自动生成
var id = this.id = ID(this);
this.anims = {};
this._ = {
transform: []
};
el.snap = id;
hub[id] = this;
if (this.type == "g") {
this.add = add2group;
}
if (this.type in {g: 1, mask: 1, pattern: 1, symbol: 1}) {
for (var method in Paper.prototype) if (Paper.prototype[has](method)) {
this[method] = Paper.prototype[method];
}
}
}function Paper(w, h) {
var res,
desc,
defs,
proto = Paper.prototype;
if (w && w.tagName && w.tagName.toLowerCase() == "svg") {
if (w.snap in hub) {
return hub[w.snap];
}
var doc = w.ownerDocument;
res = new Element(w); // Paper返回的是一个element
desc = w.getElementsByTagName("desc")[0];
defs = w.getElementsByTagName("defs")[0];
// 如何desc标签不存在会自动创建
if (!desc) {
desc = $("desc");
desc.appendChild(doc.createTextNode("Created with Snap"));
res.node.appendChild(desc);
}
if (!defs) {
defs = $("defs");
res.node.appendChild(defs);
}
res.defs = defs;
for (var key in proto) if (proto[has](key)) {
res[key] = proto[key];
}
res.paper = res.root = res;
} else {
// svg不存在,则在document.body上创建一个宽为w,高为h的svg元素
res = make("svg", glob.doc.body);
$(res.node, {
height: h,
version: 1.1,
width: w,
xmlns: xmlns
});
}
return res;
}function Fragment(frag) {
this.node = frag;
}吏上最简plugin写法
Snap.plugin = function (f) {
f(Snap, Element, Paper, glob, Fragment);
};示例:
Snap.plugin(function (Snap, Element, Paper, global, Fragment) {
Snap.newmethod = function () {};
Element.prototype.newmethod = function () {};
Paper.prototype.newmethod = function () {};
});Creates a DOM fragment from a given list of elements or strings
Snap.fragment = function () {
var args = Array.prototype.slice.call(arguments, 0),
f = glob.doc.createDocumentFragment();
for (var i = 0, ii = args.length; i < ii; i++) {
var item = args[i];
if (item.node && item.node.nodeType) {
f.appendChild(item.node);
}
if (item.nodeType) {
f.appendChild(item);
}
if (typeof item == "string") {
f.appendChild(Snap.parse(item).node);
}
}
return new Fragment(f);
};Document.createDocumentFragment() 创建一个新的空白的文档片段( DocumentFragment)
DocumentFragments 是DOM节点。它们不是主DOM树的一部分。通常的用例是创建文档片段,将元素附加到文档片段,然后将文档片段附加到DOM树。在DOM树中,文档片段被其所有的子元素所代替。
因为文档片段存在于内存中,并不在DOM树中,所以将子元素插入到文档片段时不会引起页面回流(对元素位置和几何上的计算)。因此,使用文档片段通常会带来更好的性能。
- url 字符串。URL
- postData 对象或字符串。要post的数据。
- callback 函数。回调函数。
- scope 对象。回调函数的作用域。
Snap.ajax = function (url, postData, callback, scope){
var req = new XMLHttpRequest,
id = ID();
if (req) {
if (is(postData, "function")) {
scope = callback;
callback = postData;
postData = null;
} else if (is(postData, "object")) {
var pd = [];
for (var key in postData) if (postData.hasOwnProperty(key)) {
pd.push(encodeURIComponent(key) + "=" + encodeURIComponent(postData[key]));
}
postData = pd.join("&");
}
req.open(postData ? "POST" : "GET", url, true);
if (postData) {
req.setRequestHeader("X-Requested-With", "XMLHttpRequest");
req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
}
if (callback) {
eve.once("snap.ajax." + id + ".0", callback);
eve.once("snap.ajax." + id + ".200", callback);
eve.once("snap.ajax." + id + ".304", callback);
}
req.onreadystatechange = function() {
if (req.readyState != 4) return;
eve("snap.ajax." + id + "." + req.status, scope, req);
};
if (req.readyState == 4) {
return req;
}
req.send(postData);
return req;
}
};示例
let svg = Snap("#svg");
let c = svg.paper.circle(50,50,40);
document.getElementById("button").onclick = function() {
Snap.ajax("../_ajax/ajax.php", function(response) {
c.attr({
fill: response.responseText
});
});
};- url 字符串。URL
- callback 函数。回调
- scope 对象。回调函数的作用域。
加载外部的SVG文件作为文档片段 @Fragment
Snap.load = function (url, callback, scope) {
Snap.ajax(url, function (req) {
var f = Snap.parse(req.responseText);
scope ? callback.call(scope, f) : callback(f);
});
};示例:
Snap.load("../_ajax/ajax.svg", function(svg) {
this.appendChild(svg.node);
}, document.getElementById("append"));- varargs Svg字符串或svg片段可html元素
根据给定的元素列表或字符串创建DOM片段。
因为文档片段存在于内存中,并不在DOM树中,所以将子元素插入到文档片段时不会引起页面回流(对元素位置和几何上的计算)。因此,使用文档片段通常会带来更好的性能
let glob = {
win: root.window,
doc: root.window.document
};
function Fragment(frag) {
this.node = frag;
}
Snap.fragment = function () {
// Document.createDocumentFragment()
// https://developer.mozilla.org/zh-CN/docs/Web/API/Document/createDocumentFragment
var args = Array.prototype.slice.call(arguments, 0),
f = glob.doc.createDocumentFragment();
for (var i = 0, ii = args.length; i < ii; i++) {
var item = args[i];
if (item.node && item.node.nodeType) {
f.appendChild(item.node);
}
if (item.nodeType) {
f.appendChild(item);
}
if (typeof item == "string") {
f.appendChild(Snap.parse(item).node);
}
}
return new Fragment(f);
};示例:
let svg = new Snap('svg')
let c = Snap.fragment('<circle cx="50" cy="50" r="40"></circle><circle cx="150" cy="50" r="40"></circle>');
svg.append(c)
let b = Snap.fragment(c)
svg.append(b)
let span = document.getElementById('span')
let s = Snap.fragment(span)
svg.append(s)- from (number|array)。动画起始数值或数值数组。
- to (number|array)。动画结束数值或数值数组。
- setter (function)。接受一个数值参数的贴心函数。
- duration (number)。动画持续时间,单位是毫秒。
- easing (function)。来自mina或自定义的缓动函数。
- callback (function)。动画结束时候执行的回调函数。
返回值
- 对象。mina格式的动画对象。
{
id (string) 。动画的id, 你可以认为其只读。
duration (function)。获得或设置动画的持续时间。
easing (function)。缓动函数。
speed (function)。获得或设置动画的速度。
status (function)。获得或设置动画的状态。
stop (function)。停止动画。
}
// naimation.js
Snap.animate = function (from, to, setter, ms, easing, callback) {
if (typeof easing == "function" && !easing.length) {
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);
return anim;
};
// mina.js
mina = function (a, A, b, B, get, set, easing) {
var anim = {
id: ID(),
start: a,
end: A,
b: b,
s: 0,
dur: B - b,
spd: 1,
get: get,
set: set,
easing: easing || mina.linear,
status: sta,
speed: speed,
duration: duration,
stop: stopit,
pause: pause,
resume: resume,
update: update
};
animations[anim.id] = anim;
var len = 0, i;
for (i in animations) if (animations.hasOwnProperty(i)) {
len++;
if (len == 2) {
break;
}
}
len == 1 && frame();
return anim;
};
frame = function (timeStamp) {
// Manual invokation?
if (!timeStamp) {
// Frame loop stopped?
if (!requestID) {
// Start frame loop...
requestID = requestAnimFrame(frame);
}
return;
}
var len = 0;
for (var i in animations) if (animations.hasOwnProperty(i)) {
var a = animations[i],
b = a.get(),
res;
len++;
a.s = (b - a.b) / (a.dur / a.spd);
if (a.s >= 1) {
delete animations[i];
a.s = 1;
len--;
(function (a) {
setTimeout(function () {
eve("mina.finish." + a.id, a);
});
}(a));
}
a.update();
}
requestID = len ? requestAnimFrame(frame) : false;
},示例:
let rect = Snap("#svg").paper.rect(0, 20, 60, 40);
document.getElementById("button").onclick = function() {
Snap.animate(0, 20, function (val) {
rect.attr({
x: val
});
}, 1000);
// 以上代码,类似于下面的效果
// rect.animate({x: 20}, 1000);
// 显然,上面的更强大,更灵活;下面的则是简单易懂
};- attr (object)。最终目标的属性。
- duration (number) 。动画持续的时间,单位为毫秒。
- easing (function)。mina或自定义的缓动函数。
- callback (function)。动画结束的时候执行的回调函数。
返回:
- 对象(object)。动画对象。
创建一个animation对象
let Animation = function (attr, ms, easing, callback) {
if (typeof easing == "function" && !easing.length) {
callback = easing;
easing = mina.linear;
}
this.attr = attr;
this.dur = ms;
easing && (this.easing = easing);
callback && (this.callback = callback);
};
Snap.animation = function (attr, ms, easing, callback) {
return new Animation(attr, ms, easing, callback);
};示例:
var anim = Snap.animation({ foo: "bar" }, 100, mina.easein);
console.info(anim)it("Snap.animation - no easing or callback", function() {
var anim = Snap.animation({ foo: "bar" }, 100);
expect(anim).to.be.an("object");
expect(anim.dur).to.be(100);
expect(anim.attr.foo).to.be("bar");
});
it("Snap.animation - with easing", function() {
var anim = Snap.animation({ foo: "bar" }, 100, mina.easein);
expect(anim).to.be.an("object");
expect(anim.dur).to.be(100);
expect(anim.attr.foo).to.be("bar");
expect(anim.easing).to.be.a("function");
});
it("Snap.animation - with callback", function() {
var cb = function(){};
var anim = Snap.animation({ foo: "bar" }, 100, cb);
expect(anim).to.be.an("object");
expect(anim.dur).to.be(100);
expect(anim.attr.foo).to.be("bar");
expect(anim.callback).to.be.a("function");
});
it("Snap.animation - with easing & callback", function() {
var cb = function(){};
var anim = Snap.animation({ foo: "bar" }, 100, mina.linear, cb);
expect(anim).to.be.an("object");
expect(anim.dur).to.be(100);
expect(anim.attr.foo).to.be("bar");
expect(anim.easing).to.be.a("function");
expect(anim.callback).to.be.a("function");
expect(anim.easing).to.not.be(anim.callback);
});- x (number) x coordinate from the top left corner of the window
- y (number) y coordinate from the top left corner of the window
返回
- (object) Snap element object
Returns you topmost element under given point. 返回当前文档上处于指定坐标位置最顶层的元素, 坐标是相对于包含该文档的浏览器窗口的左上角为原点来计算的, 通常 x 和 y 坐标都应为正数.
Snap.getElementByPoint = function (x, y) {
var paper = this,
svg = paper.canvas,
target = glob.doc.elementFromPoint(x, y);
// https://developer.mozilla.org/zh-CN/docs/Web/API/Document/elementFromPoint
if (glob.win.opera && target.tagName == "svg") {
var so = getOffset(target),
sr = target.createSVGRect();
sr.x = x - so.x;
sr.y = y - so.y;
sr.width = sr.height = 1;
var hits = target.getIntersectionList(sr, null);
if (hits.length) {
target = hits[hits.length - 1];
}
}
if (!target) {
return null;
}
return wrap(target);
};示例:
let svg = Snap("#svg");
let c1 = svg.paper.circle(50,50,40).attr({
fill: "red"
}), c2 = svg.paper.circle(100,50,40).attr({
fill: "green"
});
document.body.addEventListener('click',(e) => {
let target = Snap.getElementByPoint(e.pageX,e.pageY)
target.attr({
stroke: "blue",
strokeWidth: 5
});
})- query (string) CSS selector of the element
返回
- (Element) the current element
Wraps a DOM element specified by CSS selector as @Element
Snap.select = function (query) {
query = Str(query).replace(/([^\\]):/g, "$1\\:");
return wrap(glob.doc.querySelector(query));
};示例:
let s = Snap(10, 10);
let group1 = s.group();
let group2 = s.group();
let group3 = s.group();
let circle1 = s.circle(10, 20, 30).attr({
'class': 'circle-one'
});
let circle2 = s.circle(5, 10, 25).attr({
'class': 'circle-two'
});
group1.add(group2);
group2.add(group3);
group2.add(circle1);
group3.add(circle2);
let c1 = Snap.select('.circle-one');
let c2 = Snap.select('.circle-two');
console.info(c1 === circle1)
console.info(c2 === circle2)- query (string) CSS selector of the element
Snap.selectAll = function (query) {
var nodelist = glob.doc.querySelectorAll(query),
set = (Snap.set || Array)();
for (var i = 0; i < nodelist.length; i++) {
set.push(wrap(nodelist[i]));
}
return set;
};注意:selectAll返回的是一个Set对象,Set就是一个数组
示例
let s = Snap(10, 10);
let group1 = s.group();
let group2 = s.group();
let group3 = s.group();
let circle1 = s.circle(10, 20, 30).attr({
'class': 'circle-one'
});
let circle2 = s.circle(5, 10, 25).attr({
'class': 'circle-two'
});
group1.add(group2);
group2.add(group3);
group2.add(circle1);
group3.add(circle2);
let circles = Snap.selectAll('circle');
console.info(circles.length)- svg (string) SVG string
Parses SVG fragment and converts it into a @Fragment
返回
- (Fragment) the @Fragment
Snap.parse = function (svg) {
var f = glob.doc.createDocumentFragment(),
full = true,
div = glob.doc.createElement("div");
svg = Str(svg);
if (!svg.match(/^\s*<\s*svg(?:\s|>)/)) {
svg = "<svg>" + svg + "</svg>";
full = false;
}
div.innerHTML = svg;
svg = div.getElementsByTagName("svg")[0];
if (svg) {
if (full) {
f = svg;
} else {
while (svg.firstChild) {
f.appendChild(svg.firstChild);
}
}
}
return new Fragment(f);
};示例:
var fragment = Snap.parse('<circle cx="50" cy="50" r="40"></circle>');
Snap("#svg").add(fragment);- token (string) string to format
- json (object) object which properties are used as a replacement
返回
- (string) formatted string
Snap.format = (function () {
var tokenRegex = /\{([^\}]+)\}/g,
objNotationRegex = /(?:(?:^|\.)(.+?)(?=\[|\.|$|\()|\[('|")(.+?)\2\])(\(\))?/g, // matches .xxxxx or ["xxxxx"] to run over object properties
replacer = function (all, key, obj) {
var res = obj;
key.replace(objNotationRegex, function (all, name, quote, quotedName, isFunc) {
name = name || quotedName;
if (res) {
if (name in res) {
res = res[name];
}
typeof res == "function" && isFunc && (res = res());
}
});
res = (res == null || res == obj ? all : res) + "";
return res;
};
return function (str, obj) {
return Str(str).replace(tokenRegex, function (all, key) {
return replacer(all, key, obj);
});
};
})();示例:
let str = Snap.format("M{x},{y}h{dim.width}v{dim.height}h{dim['negative width']}z", {
x: 10,
y: 20,
dim: {
width: 40,
height: 50,
"negative width": -40
}
})
console.info(str)- values (array|number) given array of values or step of the grid
- value (number) 要调整的值
- tolerance (number) 触发snap的距离目标值的最大值,默认10
贴紧网格
let svg = new Snap('#svg')
let gridSize = 50
let orig = {
x: 0,
y: 0
}
let hPath = ''
let vPath = ''
for (let i = 1; i< 100; i++) {
hPath += `M0 ${i*gridSize} h1510 `
vPath += `M${i*gridSize} 0 v1510 `
}
svg.paper.path(hPath).attr({
stroke: "#00f",
strokeWidth: 1
})
svg.paper.path(vPath).attr({
stroke: "#0f0",
strokeWidth: 1
})
let rect = svg.paper.rect(150, 30, 100, 100)
rect.attr({
fill: "red"
})
rect.drag(
//---move/drag---
function (dx, dy, x, y, e) {
this.attr({
x: orig.x + dx,
y: orig.y + dy
})
},
//---mousedown/start
function (x, y, e) {
orig.x = e.toElement.x.baseVal.value;
orig.y = e.toElement.y.baseVal.value;
},
//---mouseup/end: snap to grid---
function (e) {
orig.x = e.toElement.x.baseVal.value;
orig.y = e.toElement.y.baseVal.value;
let xSnap = Snap.snapTo(gridSize, orig.x, 30);
let ySnap = Snap.snapTo(gridSize, orig.y, 30);
this.attr({
x: xSnap,
y: ySnap
})
}
)