使用RaphaelJS与两个动画同步计时



使用RaphaelJS,我改编了几个脚本来创建两个我想组合的动画:

  1. 首先,绘制坐标的虚线http://jsfiddle.net/jbirthler/CvhKx/2/

    var canvas = Raphael('canvas_container', 322, 273);
    var set = canvas.set(canvas.circle(110, 265, 7), canvas.circle(110, 7, 7), canvas.circle(7, 151, 7)).attr({
    stroke: "none",
    fill: "#666" });
    var pathstr = "M 109 255 l 0 -245 l -103 141 l 265 0";
    var path = dashline(canvas, pathstr, 4000, {
    stroke: '#828282',
    'stroke-dasharray': "--",
    'stroke-linecap': "butt",
    'stroke-width': 1,
    'fill-opacity': 0 }, 1000);
    function dashline(canvas, pathstr, duration, attr) {
    var guide_path = canvas.path(pathstr).attr({
    stroke: "none",
    fill: "none"
    });
    var path = canvas.path(guide_path.getSubpath(0, 1)).attr(attr);
    var total_length = guide_path.getTotalLength(guide_path);
    var start_time = new Date().getTime();
    var interval_length = 20;
    var interval_id = setInterval(function() {
    var elapsed_time = new Date().getTime() - start_time;
    var this_length = elapsed_time / duration * total_length;
    var subpathstr = guide_path.getSubpath(0, this_length);
    attr.path = subpathstr;
    path.animate(attr, interval_length);
    }, interval_length);
    return path;
    }​;​
    

    并且,在到达坐标时缓和路径并设置圆的动画http://jsfiddle.net/jbirthler/KqjHh/1/

    var canvas = Raphael("holder", 322, 273);
    var set = canvas.set(canvas.circle(110, 265, 7),canvas.circle(110, 7, 7), canvas.circle(7, 151, 7)).attr({stroke:"none", fill: "#666"});
    var c = canvas.circle(110, 265, 10).attr({stroke: "#ddd", "stroke-width": 4});
    var fade = function (id) {
    return function () {
    set[id].attr({fill: "#fff", r: 12}).animate({fill: "#77bf00", r: 8}, 500);
    };
    };
    var run = animateCirc();
    function animateCirc() {
    var easex = ">",
    easey = ">";
    c.stop().animate({
    "0%":  {cy: 265, easing: easey, callback: fade(0)},
    "40%": {cy: 7, easing: easey, callback: fade(1)},
    "60%": {cy: 151, easing: easey, callback: fade(2)},
    "100%": {cy: 151, easing: easey, callback: fade(3)}
    }, 3000).animate({
    "0%":  {cx: 110, easing: easex},
    "40%": {cx: 110, easing: easex},
    "60%": {cx: 7, easing: easex},
    "100%": {cx: 300, easing: easex}
    }, 3000);
    return run;                
    };​
    

我希望在虚线路径到达它们的坐标时为圆设置动画。如果我能找到一条使用宽松政策的捷径,那将是一个好处,但最重要的是,我只是想把两者合二为一。

我读javascript的能力比写自己的脚本要好,但如果有人对如何分解虚线脚本和代码所采取的步骤有任何见解,那对我来说将是非常有益的。

我的第一篇关于堆栈溢出的帖子(yeesh,大约是时间)希望我足够具体!

我自己从未使用过Raphael,但我发现以下是您的解决方案:

你的第一个动画在4(4000毫秒)秒内运行,你可以在这个块中看到:

var path = dashline(canvas, pathstr, 4000, {
stroke: '#828282',
'stroke-dasharray': "--",
'stroke-linecap': "butt",
'stroke-width': 1,
'fill-opacity': 0
}, 1000);

下一步是识别渲染圆的块,在这里给它3秒钟的运行时间,可以通过将最后一个参数更改为4000来解决。接下来,您将注意到百分比。这些应该包括转换计算,以将毫秒(4000)转换为每个动画点的百分比。

我观察了动画点,但结尾代码看起来像这样:

function animateCirc() {
var easex = ">",
easey = ">";
c.stop().animate({
"0%":  {cy: 265, easing: easey, callback: fade(0)},
"35%": {cy: 7, easing: easey, callback: fade(1)},
"60%": {cy: 151, easing: easey, callback: fade(2)},
"100%": {cy: 151, easing: easey, callback: fade(3)}
}, 4000).animate({
"0%":  {cx: 110, easing: easex},
"35%": {cx: 110, easing: easex},
"60%": {cx: 7, easing: easex},
"100%": {cx: 300, easing: easex}
}, 4000);
return run;                
};

您可以在这里看到更新的(但不是100%同步的)版本。

var canvas = Raphael('canvas_container', 322, 273);
var set = canvas.set(canvas.circle(110, 265, 7), canvas.circle(110, 7, 7), canvas.circle(7, 151, 7)).attr({
stroke: "none",
fill: "#666"
});
var c = canvas.circle(110, 265, 10).attr({stroke: "#999", "stroke-width": 0});
var fade = function (id) {
return function () {
set[id].attr({fill: "#fff", r: 12}).animate({fill: "#77bf00", r: 8}, 500);
};
};
var pathstr = "M 109 255 l 0 -245 l -103 141 l 265 0";
var path = dashline(canvas, pathstr, 4000, {
stroke: '#828282',
'stroke-dasharray': "--",
'stroke-linecap': "butt",
'stroke-width': 1,
'fill-opacity': 0
}, 1000);
function dashline(canvas, pathstr, duration, attr) {
var guide_path = canvas.path(pathstr).attr({
stroke: "none",
fill: "none"
});
var path = canvas.path(guide_path.getSubpath(0, 1)).attr(attr);
var total_length = guide_path.getTotalLength(guide_path);
var start_time = new Date().getTime();
var interval_length = 20;
var interval_id = setInterval(function() {
var elapsed_time = new Date().getTime() - start_time;
var this_length = elapsed_time / duration * total_length;
var subpathstr = guide_path.getSubpath(0, this_length);
attr.path = subpathstr;
path.animate(attr, interval_length);
}, interval_length);
return path;
}
var run = animateCirc();
function animateCirc() {
var easex = ">",
easey = ">";
c.stop().animate({
"0%":  {cy: 265, easing: easey, callback: fade(0)},
"35%": {cy: 7, easing: easey, callback: fade(1)},
"60%": {cy: 151, easing: easey, callback: fade(2)},
"100%": {cy: 151, easing: easey, callback: fade(3)}
}, 4000).animate({
"0%":  {cx: 110, easing: easex},
"35%": {cx: 110, easing: easex},
"60%": {cx: 7, easing: easex},
"100%": {cx: 300, easing: easex}
}, 4000);
return run;                
};

​请注意,您可以真正使用Raphael、Easel、Kinetic或任何类型的Canvas/SVG渲染工具。

希望这能有所帮助!

@adamRenny的答案很简洁,非常简单的修改(他打败了我,当他提交他的答案时,我还在写我的答案)。但它似乎并不涉及真正的排队。必须手动计算计时才能完全同步动画。但是,我在下面的回答将极大地更改代码。可能是,也可能不是。

要做的第一件事是将虚线路径拆分为直线(在您的情况下,拆分为3个单独的线段),并在队列中设置它们的动画。也许可以对单个(组合)路径进行排队,但我还没有尝试过

为了简化这个过程,我提取了所有的路径坐标,因为它们也被圆使用。这样我们就可以把所有元素画成一个循环。

var canvas = Raphael('canvas_container', 322, 273),
// here are the coordinates
points = [ [110,265], [110,7], [7,151], [300,151] ],
mCircle = canvas.circle(points[0][0],points[0][1],10).attr({stroke: "#999", "stroke-width": 4}),
path = [],
circles = [];
// draw the dots and (starting point of) lines
// note the lines are of 0 length so it's invisible, we only mark its starting point
for (var i = 0; i < points.length - 1; i++) {
circles[i] = canvas.circle(points[i][0],points[i][1],7).attr({stroke: "none", fill: "#666"});
path[i] = canvas.path('M'+points[i][0]+' '+points[i][1]).attr({
stroke: '#828282',
'stroke-dasharray': "--",
'stroke-linecap': "butt",
'stroke-width': 1,
'fill-opacity': 0
});

请注意,循环计数为points.length - 1,因为最后一个坐标只由移动的圆使用,所以我们在这一点上不画任何东西。

然后我创建一个工厂函数来生成每组动画

// function to generate each set of animation
var fn = function(index) {
var cPath = path[index], cCircle = circles[index],
x1 = points[index][0], 
x2 = points[index+1][0], 
y1 = points[index][1], 
y2 = points[index+1][1];
return function(cb) {
cPath.animate({path:'M'+x1+' '+y1+' L'+x2+' '+y2},500,'easeOut');
mCircle.animate({cx:x2,cy:y2},500,'easeOut');
cCircle.attr({fill: "#fff", r: 12}).animate({fill: "#77bf00", r: 8}, 500, cb);
};
};

 

下面是最难的部分。管理动画队列。实际上,您可以使用其他库,如jQueryDeferred。然而,在经历了几个小时的绝望之后,我决定编写自己的(非常简单的)队列系统。

// my custom Queue class
var Queue = function() {
this.actionCount = 0;
this.actions = [];
var self = this;
self._cb = function() {
if (++self.actionCount != self.actions.length) self.run();
};
};
Queue.prototype.run = function() {
this.actions[this.actionCount](this._cb);
};
Queue.prototype.add = function(fn) {
this.actions.push(fn);
};

在循环中,我们可以将生成的每组动画注册到队列中。并在循环完成后运行队列

for (var i = 0; i < points.length - 1; i++) {
circles[i] = canvas.circle(points[i][0],points[i][1],7).attr({stroke: "none", fill: "#666"});
path[i] = canvas.path(/*...*/);
queue.add(fn(i));
}
queue.run();

这是jsFiddle

 

需要注意的事项

  • 虚线路径动画得到缓和:p
  • 添加更多要设置动画的点就像将它们添加到points数组中一样简单,其余的都是自动完成的-jsFiddle
  • 您应该能够从fn函数调整动画
  • 队列类非常简单,没有任何检查,并且未经过生产测试。我创建它的唯一目的是回答这个问题。适用标准预防措施
  • 这是我第一次和拉斐尔在一起,所以欢迎更正
  • 循环没有通过JSLint,因为我调用了一个创建另一个函数的工厂函数。在循环中这样做似乎是件坏事
  • 因为我在回答之前创建了jsFiddle,所以这里的代码比jsFiddl注释得更好

最新更新