【问题标题】:Sync timing with two animations using RaphaelJS使用 RaphaelJS 与两个动画同步时间
【发布时间】:2012-11-12 09:37:38
【问题描述】:

使用 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,但是如果有人对如何分解虚线脚本和代码所采取的步骤有任何见解,那将非常有益于我。

我关于堆栈溢出的第一篇文章(是的,是时候了)希望我足够具体!

【问题讨论】:

  • 我没用过Raphael,但是有没有带回调的动画方法? jQuery 的 animate 函数有一个回调,这意味着您可以为每个刻度应用动画,使一切完美同步。例如,请参阅我的答案:stackoverflow.com/a/13501577/362536也许有适合您情况的类似方法?

标签: javascript path drawing raphael graphael


【解决方案1】:

@adamRenny's answer 简洁,非常简单的修改(他打败了我,他提交时我还在写我的答案)。但它似乎不涉及真正的排队。必须手动计算时间以完全同步动画。但是,我在下面的回答将彻底更改代码。可能需要也可能不需要。

要做的第一件事是将虚线路径分成几条线(在你的情况下分成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);
    };
};

 

最困难的部分来了。管理动画队列。您实际上可以使用其他库,例如 jQuery Deferred。然而,经过数小时的绝望,我决定编写自己的(非常简单的)队列系统。

// 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 函数调整动画
  • 队列类非常简单,无需任何检查,并且未经生产测试。我创建它的唯一目的是回答这个线程。适用标准预防措施
  • 这是我第一次接触 Raphael,欢迎指正
  • 循环没有通过 JSLint,因为我调用了一个创建另一个函数的 factory 函数。在循环中这样做似乎是一件坏事
  • 因为我在回答之前创建了 jsFiddle,所以这里的代码比 jsFiddle 更好地注释

【讨论】:

    【解决方案2】:

    我自己从未使用过 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% 同步的)版本here

    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 渲染工具。

    希望这会有所帮助!

    【讨论】:

      猜你喜欢
      • 2018-04-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多