【snap.svg.js】SVG动画之Snap.animate

最近在使用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

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值