最近在使用snap.svg.js做一个仿真的实验,该框架的主要作用在于利用其提供的API,可以非常方便地绘图,同时操作其中的元素。不得不说,个人觉得Snap.animate是SVG所有动画的基础,对于我们了解这个框架和深入研究有着非常重要的作用,先看一下实验的效果吧。
1. animate方法
注意到,这里我们设置了一个参照物,即一共有两个相互重叠且大小相等的矩形,他们惟一的区别在于颜色不同,下层的矩形的颜色为白色,上层的矩形的颜色为棕色。利用参照矩形,可以使我们更加精确地定位。
svgObj.animate有两种用法,分别如下所示。
用法一:obj.animate();
svgObj.animate({
fill: colors[clonum]
}, 0, mina.bounce);
用法二:Snap.animate();
Snap.animate(old_y + old_height, old_y, function (val) {
svgObj.attr({
y: val,
height: old_y + old_height - val
});
}, time);
对比可以发现,用法一较为简单,但是相对于用法二,不易于理解。因此,个人更习惯使用用法二,大家可以选择。
2. 实现思路
注意到,在注油和抽油过程中,由于x和width在整个过程中始终保持不变。因此,我们只需要动态地更改y和height的坐标即可。首先,我们需要获取到参照物对象,然后获取到对应的参考坐标。接着,计算y值的变化范围:[old_y, old_y+old_height]。这里,old_y代表参考坐标y,old_height代表参考高度height。再然后,根据old_y、old_height以及val的值,计算出此时新矩形的高度。其中val的值可以是从old_y到old_y+old_height,也可以从old_y+old_height到old_y。val更新的步长由old_height/time指定。
//注油
function oilInput(groupObj, clonum, time) {
var refObj = groupObj.element[1]; //参照物
var svgObj = groupObj.element[2];
svgObj.animate({
fill: colors[clonum]
}, 0, mina.bounce);
var locations = refObj.getBBox();
var old_x = locations.x;
var old_y = locations.y;
var old_width = locations.width;
var old_height = locations.height;
Snap.animate(old_y + old_height, old_y, function (val) {
svgObj.attr({
y: val,
height: old_y + old_height - val
});
}, time);
}
//抽油
function oilOutput(groupObj, clonum, time) {
var refObj = groupObj.element[1]; //参照物
var svgObj = groupObj.element[2];
svgObj.animate({
fill: colors[clonum]
}, 0, mina.bounce);
var locations = refObj.getBBox();
var old_x = locations.x;
var old_y = locations.y;
var old_width = locations.width;
var old_height = locations.height;
Snap.animate(old_y, old_y + old_height, function (val) {
svgObj.attr({
y: val,
height: old_y + old_height - val
});
}, time);
}
完整实现如下:
<!DOCTYPE html>
<html>
<head>
<title>绘制解码图</title>
<!--引用-->
<script src="js/snap.svg.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<svg id="svg" width="1200" height="700"></svg>
<input id="button1" type="button" class="zxx_api_button" value="注油">
<input id="button2" type="button" class="zxx_api_button" value="抽油">
<script>
var colors = ['#f05b72', '#905a3d',
'#f58220', '#7fb80e',
'#494e8f', '#aa363d',
'#8552a1', '#aa2116',
'#dea32c', '#73b9a2',
'#ba8448', '#f15a22'
];
//绘制油罐
function getTank(svg, x, y, width, height) {
var d = 5; //罐口直径
var len = 20; //罐口长度
var t = 5; //罐的厚度
var c1 = svg.paper.rect(x, y, width, height, 10).attr({
fill: "grey"
});
var c2 = svg.paper.rect(x + t, y + t, width - 2 * t, height - 2 * t, 10).attr({
fill: "white"
});
var c3 = svg.paper.rect(x + t, y + t, width - 2 * t, height - 2 * t, 10).attr({
fill: "white"
});
var l1 = svg.paper.line(x + width / 2, y - len, x + width / 2, y).attr({
stroke: "#000",
strokeWidth: d
});
var l2 = svg.paper.line(x + width / 2, y + height, x + width / 2, y + height + len).attr({
stroke: "#000",
strokeWidth: d
});
var t1 = svg.paper.text(x + (width - d) / 2 + 10, y, "IN");
var t2 = svg.paper.text(x + (width - d) / 2 + 10, y + height + 12, "OUT");
return svg.paper.g(c1, c2, c3, t1, t2, l1, l2); // 注意这里元素的顺序是不一样的
}
//绘制蒸馏塔
function getDS(svg, x, y, width, height) {
var d = 5; //塔口直径
var len = 20; //塔口长度
var t = 5; //塔的厚度
var d1 = svg.paper.rect(x, y, width, height, 10).attr({
fill: "grey"
});
var d2 = svg.paper.rect(x + t, y + t, width - 2 * t, height - 2 * t, 10).attr({
fill: "white"
});
var e = svg.paper.line(x + width / 2, y - len, x + width / 2, y).attr({
stroke: "#000",
strokeWidth: 5
});
var tag = svg.paper.text(x + (width - d) / 2 + 10, y, "IN");
return svg.paper.g(d1, d2, tag, e); // 注意这里元素的顺序是不一样的
}
//绘制储油罐
function getFP(svg, x, y, width, height) {
var d = 5; //塔口直径
var len = 20; //塔口长度
var t = 5; //塔的厚度
var d1 = svg.paper.rect(x, y, width, height, 10).attr({
fill: "grey"
});
var d2 = svg.paper.rect(x + t, y + t, width - 2 * t, height - 2 * t, 10).attr({
fill: "white"
});
var e = svg.paper.line(x + width / 2, y + height, x + width / 2, y + height + len).attr({
stroke: "#000",
strokeWidth: 5
});
var tag = svg.paper.text(x + (width - d) / 2 + 10, y + height + 12, "OUT");
return svg.paper.g(d1, d2, tag, e); // 注意这里元素的顺序是不一样的
}
//绘制管道
function getPipeline(svg, x, y, width, height) {
var bus1 = svg.paper.line(x, y, x + width, y).attr({
stroke: "#000",
strokeWidth: 5
});
var bus2 = svg.paper.line(x + width / 2, y, x + width / 2, y + height).attr({
stroke: "#000",
strokeWidth: 10
});
var bus3 = svg.paper.line(x, y + height, x + width, y + height).attr({
stroke: "#000",
strokeWidth: 5
});
return svg.paper.g(bus1, bus2, bus3);
}
//该方法主要是为了简化旋转操作
function getRenderObject(svg, type, x, y, centX, centY) {
var result = {};
var width = (centX - x) * 2;
var height = (centY - y) * 2;
if (type == 'tank') {
result.element = getTank(svg, x, y, width, height);
} else if (type == 'ds') {
result.element = getDS(svg, x, y, width, height);
} else if (type == 'pipe') {
result.element = getPipeline(svg, x, y, width, height);
} else if (type == 'fp') {
result.element = getFP(svg, x, y, width, height);
}
result.type = type;
result.x = x;
result.y = y;
result.centX = x + width / 2;
result.centY = y + height / 2;
return result;
}
//复制对象(和原始对象重叠)
function cloneRenderObject(svg, obj) {
return getRenderObject(svg, obj.type, obj.x, obj.y, obj.centX, obj.centY); //obj.element.clone();
}
//移动
function moveRenderObject(obj, right, down) {
var m = new Snap.Matrix();
m.translate(obj.x + right, obj.y + down);
obj.element.transform(m);
obj.x = obj.x + right;
obj.y = obj.y + down;
obj.centX = obj.centX + right;
obj.centY = obj.centY + down;
}
//按照类型查找对象
function selectObjectsByType(collection, type) {
var result = [];
for (i = 0; i < collection.length; i++) {
if (collection[i].type == type) {
result.push(collection[i]);
}
}
return result;
}
//注油
function oilInput(groupObj, clonum, time) {
var refObj = groupObj.element[1]; //参照物
var svgObj = groupObj.element[2];
svgObj.animate({
fill: colors[clonum]
}, 0, mina.bounce);
var locations = refObj.getBBox();
var old_x = locations.x;
var old_y = locations.y;
var old_width = locations.width;
var old_height = locations.height;
Snap.animate(old_y + old_height, old_y, function (val) {
svgObj.attr({
y: val,
height: old_y + old_height - val
});
}, time);
}
//抽油
function oilOutput(groupObj, clonum, time) {
var refObj = groupObj.element[1]; //参照物
var svgObj = groupObj.element[2];
svgObj.animate({
fill: colors[clonum]
}, 0, mina.bounce);
var locations = refObj.getBBox();
var old_x = locations.x;
var old_y = locations.y;
var old_width = locations.width;
var old_height = locations.height;
Snap.animate(old_y, old_y + old_height, function (val) {
svgObj.attr({
y: val,
height: old_y + old_height - val
});
}, time);
}
var svg = Snap("#svg");
var collection = [];
var fp = getRenderObject(svg, 'fp', 0, 0, 30, 50);
var tank = getRenderObject(svg, 'tank', 50, 50, 90, 125);
var ds = getRenderObject(svg, 'ds', 100, 200, 160, 300);
var pipe = getRenderObject(svg, 'pipe', 40, 0, 540, 15);
collection.push(fp);
collection.push(tank);
collection.push(ds);
collection.push(pipe);
//FP2~11
var offset = 95;
for (i = 1; i <= 10; i++) {
var tmp = i * offset;
var cloneObj = cloneRenderObject(svg, fp);
moveRenderObject(cloneObj, tmp, 0);
collection.push(cloneObj);
}
//TK2~9
var offset = 120;
for (i = 1; i <= 8; i++) {
var tmp = i * offset;
var cloneObj = cloneRenderObject(svg, tank); //复制
moveRenderObject(cloneObj, tmp, 0); //平移
collection.push(cloneObj);
}
//pipe2
var pipe2 = cloneRenderObject(svg, pipe);
moveRenderObject(pipe2, 0, 220);
collection.push(pipe2);
//DS2~DS4
var offset = 225;
for (i = 1; i <= 3; i++) {
var tmp = i * offset;
var cloneObj = cloneRenderObject(svg, ds); //复制
moveRenderObject(cloneObj, tmp, 0); //平移
collection.push(cloneObj);
}
//获取group对象
var tanks = selectObjectsByType(collection, 'tank');
var dss = selectObjectsByType(collection, 'ds');
var pipes = selectObjectsByType(collection, 'pipe');
var fps = selectObjectsByType(collection, 'fp');
for (i = 0; i < fps.length; i++) {
moveRenderObject(fps[i], 115, 50);
}
for (i = 0; i < tanks.length; i++) {
moveRenderObject(tanks[i], 0, 120);
}
for (i = 0; i < pipes.length; i++) {
moveRenderObject(pipes[i], 40, 170);
}
for (i = 0; i < dss.length; i++) {
moveRenderObject(dss[i], 0, 40);
}
//事件
document.getElementById("button1").onclick = function () {
var obj = tanks[0];
oilInput(obj, 1, 10000);
};
document.getElementById("button2").onclick = function () {
var obj = tanks[0];
oilOutput(obj, 1, 10000);
};
</script>
</body>
更多内容,可以移步至:https://www.zhangxinxu.com/GitHub/demo-Snap.svg/demo/basic/Snap.animate.php