2010-05-08 89 views
68

我想要做的是在緩衝區上繪製圖形,然後將其原樣複製到畫布上,這樣我就可以進行動畫並避免閃爍。但我找不到這個選項。任何人都知道我可以如何去做這件事?HTML5/Canvas支持雙緩衝嗎?

+12

我的經驗是,帆布畫是由瀏覽器合併,使動畫順利。你能分享一些閃爍的代碼嗎? – eyelidlessness 2010-05-08 19:10:42

+2

我注意到IE在使用'explorercanvas'時可能會閃爍,但這當然不是HTML5,並且是僅由VML模擬的'canvas'元素。我從來沒有見過其他瀏覽器這樣做。 – cryo 2010-05-19 10:16:54

+3

相關http://stackoverflow.com/questions/11777483/ – julien 2012-11-29 14:48:12

回答

18

下有用的鏈接,除了展示的例子,並使用雙緩衝的優勢,展示了使用HTML5的Canvas元素其他一些性能提示。它包含jsPerf測試的鏈接,它將瀏覽器的測試結果彙總到一個Browserscope數據庫中。這可確保性能提示得到驗證。

http://www.html5rocks.com/en/tutorials/canvas/performance/

爲方便起見,我已包括在文章中描述的有效的雙緩衝的最小例子。

// canvas element in DOM 
var canvas1 = document.getElementById('canvas1'); 
var context1 = canvas1.getContext('2d'); 

// buffer canvas 
var canvas2 = document.createElement('canvas'); 
canvas2.width = 150; 
canvas2.height = 150; 
var context2 = canvas2.getContext('2d'); 

// create something on the canvas 
context2.beginPath(); 
context2.moveTo(10,10); 
context2.lineTo(10,30); 
context2.stroke(); 

//render the buffered canvas onto the original canvas element 
context1.drawImage(canvas2, 0, 0); 
3

而不是滾動您自己,你可能會使用現有的庫,以獲得最佳里程爲創造清潔,無閃爍的JavaScript動畫:

這裏有一個流行的一種:http://processingjs.org

+91

正是!爲什麼打擾並寫出自己的代碼10行,當你可以使用一個完整的275KB庫而沒有絲毫的線索!?是的,我正在諷刺。 – Tom 2010-05-23 15:33:34

+8

Yeesh,苛刻多少? – notJim 2011-01-28 04:54:30

80

一非常簡單的方法是在同一屏幕位置放置兩個畫布元素,併爲您需要顯示的緩衝區設置可見性。當你完成後,在隱藏處畫畫並翻轉。

某些代碼:

CSS:

canvas { border: 2px solid #000; position:absolute; top:0;left:0; 
visibility: hidden; } 

翻轉在JS:

Buffers[1-DrawingBuffer].style.visibility='hidden'; 
Buffers[DrawingBuffer].style.visibility='visible'; 

DrawingBuffer=1-DrawingBuffer; 

在此代碼陣列 '緩衝器[]' 同時擁有畫布對象。所以,當你要開始繪製你仍然需要獲得上下文:

var context = Buffers[DrawingBuffer].getContext('2d'); 
+0

不錯的解決方案:D – 2011-07-05 13:36:21

+0

偏題:我個人喜歡用'

17

瀏覽器我測試過的所有,直到吸引你的框架代碼完成時不重畫在畫布上處理這個緩衝你。請參閱WHATWG郵件列表:http://www.mail-archive.com/[email protected]/msg19969.html

+9

好吧,我注意到閃爍或屏幕撕裂。不知道如何描述它。在Linux上使用最新的Chrome。 – grom 2012-07-27 04:45:17

5

網頁瀏覽器中沒有閃爍!他們已經使用dbl緩衝來渲染它們。 Js引擎將在展示之前進行所有渲染。此外,上下文保存和恢復僅堆棧轉換矩陣數據等,而不是畫布內容本身。 所以,你不需要或不需要DBL緩衝!

+8

你能提供一些證據來支持你的說法嗎? – ricksuggs 2013-10-11 13:54:59

+0

@ricksuggs我發現在動畫中沒有使用數據庫結果動不動,我還沒有嘗試過數據庫 – FutuToad 2014-08-18 14:19:00

2

Opera 9.10非常慢並顯示繪圖過程。如果你想看到瀏覽器不使用雙緩衝,請嘗試Opera 9.10。

有些人建議瀏覽器以某種方式確定繪圖過程何時結束,但您能否解釋這可以如何工作?即使畫圖很慢,我也沒有注意到Firefox,Chrome或IE9中有任何明顯的閃爍,所以看起來這就是他們正在做的事情,但是如何實現這一點對我來說是個謎。在更多的繪圖指令被執行之前,瀏覽器如何知道它正在刷新顯示?你認爲他們只是計時嗎?如果超過5毫秒的時間間隔沒有執行畫布繪圖指令,它會認爲它可以安全地交換緩衝區?

2

需要2個帆布:(注意CSS的z-index和位置:絕對)

<canvas id="layer1" width="760" height="600" style=" position:absolute; top:0;left:0; 
visibility: visible; z-index: 0; solid #c3c3c3;"> 
Your browser does not support the canvas element. 
</canvas> 
<canvas id="layer2" width="760" height="600" style="position:absolute; top:0;left:0; 
visibility: visible; z-index: 1; solid #c3c3c3;"> 
Your browser does not support the canvas element. 
</canvas> 

,你可以看到,第一畫布上是可見的,它是隱藏它借鑑隱藏的想法第二之後,我們將隱藏可見並使隱藏的畫布可見。當它是隱藏的「清除隱藏的帆布

<script type="text/javascript"> 
var buff=new Array(2); 
buff[0]=document.getElementById("layer1"); 
buff[1]=document.getElementById("layer2"); 

ctx[0]=buff[0].getContext("2d"); 
ctx[1]=buff[1].getContext("2d"); 
var current=0; 
// draw the canvas (ctx[ current ]); 
buff[1- current ].style.visibility='hidden'; 
buff[ current ].style.visibility='visible'; 
ctx[1-current].clearRect(0,0,760,600); 
current =1-current; 
6

對於不信的人,這裏有一些閃爍的代碼。請注意,我明確地清除了以前的圈子。

http://coderextreme.net/basketball2.htmlhttp://jsfiddle.net/GzSWJ/

<!DOCTYPE html> 
<html> 
<head><title>Basketball</title></head> 
<body> 
<canvas id="myCanvas" width="400" height="400" style="border:1px solid #c3c3c3;"> 
Your browser does not support the canvas element. 
</canvas> 
<script> 
var c = document.getElementById("myCanvas"); 
var ctx = c.getContext("2d"); 

function draw_ball(ball) { 
    ctx.clearRect(0, 0, 400, 400); 
    ctx.fillStyle = "#FF0000"; 
    ctx.beginPath(); 
    ctx.arc(ball.x, ball.y, 30, 0, Math.PI * 2, true); 
    ctx.closePath(); 
    ctx.fill(); 
} 

var deltat = 1; 
var ball = {}; 
ball.y = 0; 
ball.x = 200; 
ball.vy = 0; 
ball.vx = 10; 
ball.ay = 9.8; 
ball.ax = 0.1; 

function compute_position() { 
    if (ball.y > 370 && ball.vy > 0) { 
     ball.vy = -ball.vy * 84/86; 
    } 
    if (ball.x < 30) { 
     ball.vx = -ball.vx; 
     ball.ax = -ball.ax; 
    } else if (ball.x > 370) { 
     ball.vx = -ball.vx; 
     ball.ax = -ball.ax; 
    } 
    ball.ax = ball.ax/2; 
    ball.vx = ball.vx * 185/186; 
    ball.y = ball.y + ball.vy * deltat + ball.ay * deltat * deltat/2 
    ball.x = ball.x + ball.vx * deltat + ball.ax * deltat * deltat/2 
    ball.vy = ball.vy + ball.ay * deltat 
    ball.vx = ball.vx + ball.ax * deltat 
    draw_ball(ball); 
} 

setInterval(compute_position, 40); 
</script></body></html> 
+3

對我來說,似乎並沒有閃爍,至少在球沒有移動超過其每半幀的半徑時。鍍鉻/ Windows操作系統。 – Jules 2012-07-16 12:01:28

+8

我在http://jsfiddle.net/GzSWJ/28上添加了一個對requestAnimFrame的調用 - 參見[這裏](http://paulirish.com/2011/requestanimationframe-for-smart-animating/) - 你的例子/ - 它不再閃爍 – rhu 2012-09-18 10:41:52

7

喬什問(而回)關於瀏覽器如何知道「什麼時候繪製過程結束」,以避免閃爍。我會直接評論他的帖子,但我的代表不夠高。這也只是我的看法。我沒有支持它的事實,但我對此相當有信心,未來讀者可能會對此有所幫助。

我猜測你完成繪圖時瀏覽器並不知道。但是就像大多數JavaScript一樣,只要代碼在不放棄對瀏覽器的控制權的情況下運行,瀏覽器就會被鎖定,並且不會更新/響應其UI。我在猜測,如果你清除畫布並畫出整個畫面而不放棄對瀏覽器的控制,那麼在完成之前它不會實際繪製畫布。

如果您設置了一種情況,即您的渲染跨越多個setTimeout/setInterval/requestAnimationFrame調用,您可以在一次調用中清除畫布,並在接下來的幾次調用中在畫布上繪製元素,每次重複循環5次電話,我願意打賭你會看到閃爍,因爲每次通話後畫布都會更新。

這就是說,我不確定我會相信這一點。我們已經認識到,JavaScript在執行之前會被編譯爲本地機器代碼(至少這是Chrome的V8引擎根據我的理解所做的)。如果瀏覽器在與用戶界面不同的線程中開始運行它們的javascript並同步對UI元素的任何訪問,以允許UI在不訪問UI的JavaScript執行期間更新/響應,我不會感到驚訝。當/如果發生這種情況(並且我知道有許多障礙需要克服,例如事件處理程序在您仍在運行其他代碼時開始運行),我們可能會看到未使用的畫布動畫閃爍某種雙緩衝。

就我個人而言,我喜歡將兩個畫布元素放置在彼此頂部並交替顯示/在每個畫面上繪製的想法。相當不引人注意,可能很容易通過幾行代碼添加到現有應用程序中。

+0

正確。例如,查看JSFiddle http://stackoverflow.com/questions/11777483/do-we-need-to-implement-double-buffering-ourselves-with-canvas。 – Eric 2013-08-01 03:51:27

1

在大多數情況下,您不需要執行此操作,瀏覽器會爲您執行此操作。 但並不總是有用!

當你的繪圖非常複雜時,你仍然需要實現這一點。 大部分的屏幕更新率大約是60Hz,這意味着屏幕每16ms更新一次。瀏覽器的更新率可能接近這個數字。如果你的形狀需要100ms完成,你會看到一個未完成的形狀。所以你可以在這種情況下實現雙緩衝。

我做了一個測試:Clear a rect, wait for some time, then fill with some color.如果我把時間設置爲10ms,我就不會看到閃爍。但是如果我將其設置爲20ms,閃爍就會發生。

7

兩年多後:

沒有必要'手動'實現雙緩衝。 Geary先生在他的書"HTML5 Canvas"中寫了這個。

爲了有效減少閃爍使用requestAnimationFrame()

+0

您如何解釋使用雙緩衝技術所見證的性能改進? http://www.html5rocks.com/en/tutorials/canvas/performance/ – ricksuggs 2013-10-11 13:58:52

+0

@ricksuggs那麼,html5rocks上提到的「雙緩衝」或「離屏渲染」與我想的有點不同。我認爲DB是交換屏幕頁面(在vram中),這實際上只是一個指針操作,而不是複製內存塊。 OP要求使用DB來避免閃爍,這是通過requestAnimationFrame()真正解決的。也許這個[文章](http://tinyurl.com/lpnkfnj)關於屏外渲染可能很有趣。在那裏,我回答了關於使用Sprite Buffer – ohager 2014-06-04 17:21:55

9

你總是可以做 var canvas2 = document.createElement("canvas"); 而不是將它追加到DOM。

只是說,因爲你們似乎很迷戀display:none; 它只是看起來更清潔,模仿雙緩衝方式的想法比只是一個笨拙的無形畫布更準確。

+1

le在4歲麪包上烤麪包的方法將緩衝圖像數據複製並粘貼到屏幕上的問題:-DD – DeadlyBacon 2016-01-16 12:23:46

+1

答案仍然幫助我:D – AME 2017-06-09 07:59:44