2012-02-14 152 views
17

我對畫布有點新鮮,如果這是一個微不足道的問題,那麼原諒。html5畫布 - 按路徑動畫對象

我想能夠跟隨一個路徑(定義爲貝塞爾路徑),但我不知道該怎麼做。

我已經看過拉斐爾了,但是我無法弄清楚隨着時間的推移如何跟隨路徑。

Cake JS在演示中看起來很有希望,但我真的在掙扎文檔,或者在這種情況下缺乏文檔。

有沒有人有這樣的工作例子?

回答

20

使用this related question中的代碼on my website,但不是在回調中更改.style.left等,而是使用新位置處的項目(可選擇旋轉)擦除並重新繪製畫布。請注意,這使用SVG內部很容易沿bézier曲線插入點,但是您可以使用它爲您提供的任意點(包括在畫布上繪圖)。

如果我的網站已關閉,這裏的圖書館的當前快照:

function CurveAnimator(from,to,c1,c2){ 
    this.path = document.createElementNS('http://www.w3.org/2000/svg','path'); 
    if (!c1) c1 = from; 
    if (!c2) c2 = to; 
    this.path.setAttribute('d','M'+from.join(',')+'C'+c1.join(',')+' '+c2.join(',')+' '+to.join(',')); 
    this.updatePath(); 
    CurveAnimator.lastCreated = this; 
} 
CurveAnimator.prototype.animate = function(duration,callback,delay){ 
    var curveAnim = this; 
    // TODO: Use requestAnimationFrame if a delay isn't passed 
    if (!delay) delay = 1/40; 
    clearInterval(curveAnim.animTimer); 
    var startTime = new Date; 
    curveAnim.animTimer = setInterval(function(){ 
    var now = new Date; 
    var elapsed = (now-startTime)/1000; 
    var percent = elapsed/duration; 
    if (percent>=1){ 
     percent = 1; 
     clearInterval(curveAnim.animTimer); 
    } 
    var p1 = curveAnim.pointAt(percent-0.01), 
     p2 = curveAnim.pointAt(percent+0.01); 
    callback(curveAnim.pointAt(percent),Math.atan2(p2.y-p1.y,p2.x-p1.x)*180/Math.PI); 
    },delay*1000); 
}; 
CurveAnimator.prototype.stop = function(){ 
    clearInterval(this.animTimer); 
}; 
CurveAnimator.prototype.pointAt = function(percent){ 
    return this.path.getPointAtLength(this.len*percent); 
}; 
CurveAnimator.prototype.updatePath = function(){ 
    this.len = this.path.getTotalLength(); 
}; 
CurveAnimator.prototype.setStart = function(x,y){ 
    var M = this.path.pathSegList.getItem(0); 
    M.x = x; M.y = y; 
    this.updatePath(); 
    return this; 
}; 
CurveAnimator.prototype.setEnd = function(x,y){ 
    var C = this.path.pathSegList.getItem(1); 
    C.x = x; C.y = y; 
    this.updatePath(); 
    return this; 
}; 
CurveAnimator.prototype.setStartDirection = function(x,y){ 
    var C = this.path.pathSegList.getItem(1); 
    C.x1 = x; C.y1 = y; 
    this.updatePath(); 
    return this; 
}; 
CurveAnimator.prototype.setEndDirection = function(x,y){ 
    var C = this.path.pathSegList.getItem(1); 
    C.x2 = x; C.y2 = y; 
    this.updatePath(); 
    return this; 
}; 

...這是如何使用它:

var ctx = document.querySelector('canvas').getContext('2d'); 
ctx.fillStyle = 'red'; 

var curve = new CurveAnimator([50, 300], [350, 300], [445, 39], [1, 106]); 

curve.animate(5, function(point, angle) { 
    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); 
    ctx.fillRect(point.x-10, point.y-10, 20, 20); 
});​ 

在行動:http://jsfiddle.net/Z2YSt/

+0

hmmmm,有趣... – Ben 2012-02-14 04:17:58

+0

這實際上適用於任何svg路徑...只需要將this.path.setAttribute更改爲具有給定路徑的init .. – Ben 2012-02-14 04:54:53

+0

@Ben當然它。 :)你可能對[這是我的相關頁面]感興趣(http://phrogz.net/svg/convert_path_to_polygon.xhtml)。 – Phrogz 2012-02-14 04:56:10

1

我不會使用Canvas,除非你真的必須這樣做。 SVG在內置的路徑中有動畫.Canvas需要相當多的數學才能使其工作。

Here's one example of SVG animating along a path

下面是一些關於它的討論,爲拉斐爾:SVG animation along path with Raphael

請注意,拉斐爾使用SVG和 HTML5畫布。沿着在畫布貝塞爾路徑動畫


一種方法是連續平分貝塞爾曲線,recoring中點,直到你有很多的點(比如說,每曲線50分),你可以沿着動畫的對象的點的列表。搜索bisecting貝塞爾和相關的數學相似的查詢。

+0

感謝您的輸入。大多數的SVG動畫的例子,我發現看...笨重 – Ben 2012-02-14 04:17:48

8

所以,這裏是詳細的版本:

t是0和1之間的任何數字,代表時間;所述p0p1p2p3對象是開始點時,第一控制點時,第二控制點分別是所述終點

var at = 1 - t; 
var green1x = p0.x * t + p1.x * at; 
var green1y = p0.y * t + p1.y * at; 
var green2x = p1.x * t + p2.x * at; 
var green2y = p1.y * t + p2.y * at; 
var green3x = p2.x * t + p3.x * at; 
var green3y = p2.y * t + p3.y * at; 
var blue1x = green1x * t + green2x * at; 
var blue1y = green1y * t + green2y * at; 
var blue2x = green2x * t + green3x * at; 
var blue2y = green2y * t + green3y * at; 
var finalx = blue1x * t + blue2x * at; 
var finaly = blue1y * t + blue2y * at; 

Here is a ball using <canvas> following a path in JSfiddle

變量的名稱來自這個GIF至極是貝塞爾曲線的最佳闡釋:http://en.wikipedia.org/wiki/File:Bezier_3_big.gif

代碼的短版,功能準備複製/粘貼內:

var calcBezierPoint = function (t, p0, p1, p2, p3) { 
    var data = [p0, p1, p2, p3]; 
    var at = 1 - t; 
    for (var i = 1; i < data.length; i++) { 
     for (var k = 0; k < data.length - i; k++) { 
      data[k] = { 
       x: data[k].x * at + data[k + 1].x * t, 
       y: data[k].y * at + data[k + 1].y * t 
      }; 
     } 
    } 
    return data[0]; 
}; 



相關的東西:

+1

感謝這個晚期的貢獻。但是沒有必要這麼傲慢。@ phrogz的答案非常棒,可以在任何路徑上工作。 – Ben 2013-02-15 06:05:20

+0

離真棒很遠;它使用一個專有的庫,它有一個人的代碼太多,它使用的SVG速度較慢,在Android和其他設備上不受支持。 ---- 這也適用於任何路徑;只需更改p0,p1,p2和p3的值即可。如果您需要幫助獲取正確的數字,您可以使用「相關內容」中的第一個鏈接。 – 2013-02-15 08:22:29

+0

想要在沒有畫布的情況下看到它嗎?這裏是:http://jsfiddle.net/aVwYL/1/ – 2013-02-15 08:31:56