2012-01-02 31 views
3

我已經使用stackoverflow.com作爲靈感和解決問題的來源,現在已經有好幾個月了。到目前爲止,我從未遇到過沒有解決方案的問題,這就是爲什麼我想先介紹一下自己的原因,並與所有感興趣的人分享我的問題。動畫剪輯區域<canvas>元素

在過去的幾個星期裏,我嘗試在畫布元素上爲某些形狀和線條製作動畫,以創建一些有趣的效果 - 例如手寫或類似效果。
爲了達到這個目的,我使用了一些利用canvas元素的.clip()命令的技術,來隱藏和逐漸揭示預渲染圖像(表單,行...)「等待」的區域。 我在這裏遇到的問題與確定畫布元素中裁剪區域的變量有關。它似乎有一些奇怪的問題,增加(但不是減少)動畫中的值。
因爲所有這些聽起來很奇怪,我知道這裏是我正在談論的代碼的相關部分。

$(document).ready(function() { 
    var ctx = $("#canvas")[0].getContext("2d"); 
    ctx.fillStyle = "#a00"; 
    var recW = 200; 

    function animate() { 
     ctx.clearRect(0,0,400,400); 

     ctx.beginPath(); 
     ctx.rect(50,50,recW,100); 
     ctx.clip(); 

     ctx.beginPath(); 
     ctx.arc(250,100,90,0,Math.PI*2,true); 
     ctx.fill(); 

     recW--; 

     if (recW == 150) clearInterval(run); 
    } 
    var run = setInterval(function() { animate(); },60); 
}); 

上面的代碼工作得很好。它在400 * 400的畫布中繪製一個矩形,將其用作裁剪區域,之後繪製圓形,然後相應裁剪該圓形。通過動畫間隔,裁剪矩形的長度然後減少到150的測試值。到目前爲止,那麼好。但在這裏談到這已經讓我困擾的幾個小時就結束部分:

$(document).ready(function() { 
    var ctx = $("#canvas")[0].getContext("2d"); 
    ctx.fillStyle = "#a00"; 
    var recW = 150; 

    function animate() { 
     ctx.clearRect(0,0,400,400); 

     ctx.beginPath(); 
     ctx.rect(50,50,recW,100); 
     ctx.clip(); 

     ctx.beginPath(); 
     ctx.arc(250,100,90,0,Math.PI*2,true); 
     ctx.fill(); 

     recW++; 

     if (recW == 200) clearInterval(run); 
    } 
    var run = setInterval(function() { animate(); },60); 
}); 

如果我把整個動漫周邊,開始與150的剪裁矩形的寬度,並與RECW增加它++到測試200的值,突然動畫不再起作用。變量的逐漸增加沒有問題,但可見剪輯區域不增長。

我懷疑,我可能只是俯瞰這裏的明顯,但我似乎根本無法找到錯誤,我會很感激,如果有人可以點我到正確的方向;)

謝謝很多
Tricon

+1

一個偉大的地方開始將在網上發佈你的全部代碼。轉到[jsfiddle](http://jsfiddle.net/)並放置代碼,以便我們可以更好地運行它並與其一起玩 – puk 2012-01-03 07:09:44

+0

毫無疑問,一旦將它放在那裏,請給我們一個指向您的代碼段的鏈接的代碼=) – puk 2012-01-03 07:10:08

+0

請參閱下面的解決方案。此外,在回覆評論時,請使用&符號「@」,然後輸入用戶名。在這種情況下,你的迴應的第一行應該是'@ puk'。這是一個例外,當你回答提出問題/答案的實際人員時,那麼你不需要這樣做 – puk 2012-01-03 08:20:10

回答

1

你的問題是一個棘手的調試,除非你有很多經驗(我也沒有)。

你可以將形狀變得更小而不是變得更大的原因是,我懷疑你正在將這些剪輯合併在一起。因此,當它們變小時,由於您期望最小的剪輯區域,因此一切看起來都很好。但是,當剪輯變大時,您正在與原始小剪輯區域進行「與」操作,因此看起來好像沒有動畫。

要解決此問題,您需要在剪輯末尾放置restore()調用。但是,爲了實現這一點,您還需要在剪輯的開頭調用save()。最後,我添加了一個邊界框來指示剪輯的確切位置,由於這是一個填充和一個筆劃,我放置了另一個beginPath語句,以便不在剪輯區域(我們剛剛恢復)外面描邊。

以下是完整的jsFiddle code

var canvas = document.getElementById('canvas'); 
var ctx = canvas.getContext('2d'); 

ctx.fillStyle = "#a00"; 
var recW = 150; 

function animate2() { 
    ctx.clearRect(50,50,canvas.width,recW - 1); 

    ctx.save(); 

    ctx.beginPath(); 
    ctx.rect(50, 50, recW, recW); 
    ctx.clip(); 

    ctx.beginPath(); 
    ctx.arc(250,100,90,0,Math.PI*2,true); 
    ctx.fill(); 

    ctx.restore(); 
    ctx.beginPath(); 
    ctx.rect(50 - 1, 50 - 1, recW + 2, recW + 2); 
    ctx.lineWidth = 10; 
    ctx.stroke(); 
    console.log(recW); 
    recW++; 

    if (recW == 300) clearInterval(run); 
} 
var run = setInterval(function() { animate2(); },5); 
+0

感謝一噸的快速回復。我目前正在檢查代碼,save()和restore()命令,因爲我之前沒有積極使用過這些代碼) – tricon 2012-01-03 08:45:16

+0

@tricon我在Canvas中編寫了50,000多行代碼,而且我仍然需要google,即使是最簡單的(即它是'ctx.lineWidth'或'ctx.linewidth'或'ctx.lw' ......) – puk 2012-01-03 08:51:56

+0

@tricon除了'clip','globalCompositeOperation'也非常酷。 [見這裏](https://developer.mozilla.org/en/Canvas_tutorial/Compositing) – puk 2012-01-03 08:52:59

1

下面是關於如何animate a clipping path.你的問題的一個很好的指南,一旦剪輯而成,你要拿走它才能成長,所以你用保存狀態和畫布擦拭來達到這個效果。

以下是一些示例代碼。

<canvas id="slide29Canvas2" width="970px" height="600px"></canvas> 

<script> 
    // Grabs the canvas element we made above 
var ca1=document.getElementById("slide29Canvas1"); 

// Defines the 2d thing, standard for making a canvas 
var c1=ca1.getContext("2d"); 

// Creates an image variable to hold and preload our image (can't do animations on an image unless its fully loaded) 
var img1 = document.createElement('IMG'); 

// Loads image link into the img element we created above 
img1.src = "http://tajvirani.com/wp-content/uploads/2012/03/slide29-bg_1.png"; 

// Creates the first save event, this gives us a base to clear our clipping/mask to since you can't just delete elements. 
c1.save(); 

// Our function for when the image loads 
img1.onload = function() { 

    // First call to our canvas drawing function, the thing that is going to do all the work for us. 
     // You can just call the function but I did it through a timer 
    setTimeout(function() { drawc1r(0); },5); 

     // The function that is doing all the heavy lifting. The reason we are doing a function is because 
     // to make an animation we have to draw the circle (or element) frame by frame, to do this manually would be to time 
     // intensive so we are just going to create a loop to do it. 'i' stands for the radius of our border 
     // so over time our radius is going to get bigger and bigger. 
    function drawc1r(i) { 

     // Creates a save state. Imagine a save state like an array, when you clear one it pops last element in the array off the stack 
     // When you save, it creates an element at the top of the stack. So if we cleared without making new ones, we would end up with nothing on our stage. 
    c1.save(); 

     // This clears everything off the stage, I do this because our image has transparency, and restore() (the thing that pops one off the stack) 
     // Doesn't clear off images, and so if we stick an image on the stage over and over, the transparency will stack on top of each other and 
     // That isn't quite what we want. 
    c1.clearRect(0, 0, ca1.width, ca1.height); 

     // Adds one to the radius making the circle a little bigger with every step 
    i++; 

     // Tells canvas we are going to start creating an item on the stage - it can be a line, a rectangle or any shape you draw, but whatever 
     // after this path will be added to the clip when its called. I can have 3 rectangles drawn and that would make a clip. 
    c1.beginPath(); 

     // Can't make a true circle, so we make an arced line that happens to trace a circle - 'i' is used to define our radius. 
    c1.arc(853, 320, i, 0, 2 * Math.PI, false); 

     // After everything is defined, we make a clip area out of it. 
    c1.clip(); 

     // Now that we have the clip added to it, we are going to add the image to the clip area. 
    c1.drawImage(img1, 0, 0); 

     // This pops one off the stack which gets rid of the clip so we can enlarge it and make it again on the next pass 
    c1.restore(); 

     // Here is the final size of the circle, I want it to grow the circle until it hits 800 so we set a timeout to run this function again 
     // until we get the size we want. The time in milliseconds pretty much defines your framerate for growing the circle. There are other 
     // methods for doing this that give you better frame rates, but I didn't have much luck with them so I'm not going to include them. 
    if(i < 800) { 
     setTimeout(function() { drawc1r(i); },5); 
    } 

}