[英]Make clearRect() of canvas work faster
我正在嘗試用 JavaScript 設計一個正弦波,但設計看起來很慢。 主要瓶頸是用於清除畫布的clearRect()
。
我該如何解決這個問題?
此外,我正在通過ctx.fillRect(x, y,1,1)
繪制像素,但是當我使用clearRect(x, y,1,1)
清除時,它會留下一些足跡。 相反,我必須執行clearRect(x, y,5,5)
以獲得正確的清除。 可以解決什么問題?
/******************************/
var x = 0;
var sineval = [];
var offset = 0;
var animFlag;
function init() {
for(var i=0; i<=1000; ++i){
sineval[i] = Math.sin(i*Math.PI/180);
}
// Call the sineWave() function repeatedly every 1 microseconds
animFlag = setInterval(sineWave, 1);
//sineWave();
}
function sineWave()
{ //console.log('Drawing Sine');
var canvas = document.getElementById("canvas");
if (canvas.getContext) {
var ctx = canvas.getContext("2d");
}
for(x=0 ; x<1000 ;++x){
// Find the sine of the angle
//var i = x % 361;
var y = sineval[x+offset];
// If the sine value is positive, map it above y = 100 and change the colour to blue
if(y >= 0)
{
y = 100 - (y-0) * 70;
ctx.fillStyle = "green";
}
// If the sine value is negative, map it below y = 100 and change the colour to red
if( y < 0 )
{
y = 100 + (0-y) * 70;
ctx.fillStyle = "green";
}
// We will use the fillRect method to draw the actual wave. The length and breath of the
if(x == 0) ctx.clearRect(0,y-1,5,5);
else ctx.clearRect(x,y,5,5);
ctx.fillRect(x, y,1,1 /*Math.sin(x * Math.PI/180) * 5, Math.sin(x * Math.PI/180 * 5)*/);
}
offset = (offset > 360) ? 0 : ++offset ;
}
您需要重構一下代碼:
requestAnimationFrame
代替setInterval
rect()
替換fillRect()
並在內部for循環外執行單個fill()
使用1毫秒的超時值可能會導致瀏覽器阻塞,或者至少使其運行速度顯着降低。 考慮到監視器更新僅每16.7ms發生一次,這當然會浪費周期。 如果要降低/增加正弦波的速度,則可以降低/增加增量步長。
在本質上:
var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d"); var sineval = []; var offset = 0; init(); function init() { for (var i = 0; i <= 1000; ++i) { sineval.push(Math.sin(i * Math.PI / 180)); } // Call the sineWave() function sineWave(); } function sineWave() { ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); ctx.beginPath(); ctx.fillStyle = "green"; // draw positive part of sine wave here for (var x = 0; x < 1000; x++) { var y = sineval[x + offset]; if (y >= 0) { y = 100 - (y - 0) * 70; ctx.rect(x, y, 2, 2); } } ctx.fill(); ctx.beginPath(); ctx.fillStyle = "red"; // draw negative part of sine wave here for (var x = 0; x < 1000; x++) { var y = sineval[x + offset]; if (y < 0) { y = 100 - (y - 0) * 70; ctx.rect(x, y, 2, 2); } } ctx.fill(); offset = (offset > 360) ? 0 : ++offset; requestAnimationFrame(sineWave); }
<canvas id="canvas" width=800 height=500></canvas>
當然,如果將腳本加載到<head>
,則需要將其包裝在window.onload
塊中,以便canvas元素可用。 或者,如果您還沒有的話,只需將腳本放在頁面底部。
一些加速和奇怪的結局:
在 init 中,設置一次正弦波像素值。
對這些使用類型化數組,因為如果可能的話,堅持使用整數比使用浮點數更快。
我們將直接操作像素數據,而不是使用填充和清除。 首先,在 init 中我們調用 ctx.getImageData 一次。 我們也只是一次最大化所有像素的 alpha 值,因為默認的 0 值是透明的,我們希望完全不透明度為 255。
像以前一樣使用 setInterval 。 我們希望以穩定的速度更新像素。
使用“adj”作為旋鈕來調整正弦波在屏幕上移動的速度。 實際值(小數)將取決於繪圖幀速率。 我們使用 Date.now() 調用來跟蹤跨幀消耗的毫秒數。 所以毫秒的調整是mod 360來設置'offset'變量。 因此偏移值不是每幀增加1,而是根據時間消耗來決定。 如果需要,可以稍后將 adj 值連接到 gui。
在工作結束時(在 sineWave 函數中),我們調用 requestAnimationFrame 只是為了將 ctx.putImageData 執行到畫布,屏幕同步以避免撕裂。 注意'paintit' 函數既快速又簡單。 還要注意,我們仍然需要 setInterval 來保持穩定的步伐。
在設置偏移量和調用 requestAnimationFrame 之間,我們做了兩個循環。 第一個有效地使我們從前一幀(設置為 0)中繪制的精確像素變黑。 第二個循環繪制新的正弦波。 波浪的上半部分是綠色的(將像素 rgba 中的 G 設置為 255)。 下半部分為紅色(將 R 像素 rgba 設置為 255)。
使用 .data 數組繪制像素,並使用 4 x + 4 y*canvas.width 將其索引到像素。 如果想要綠色值而不是紅色值,則再添加 1。 無需觸摸藍色值(字節偏移量 2)或已設置的 alpha(字節偏移量 3)。
在某些地方使用的 >>>0 會將受影響的值轉換為無符號整數(如果它還沒有的話)。 它也可以用來代替 Math.ceil。 我認為 .data 類型已經是 Array 了。
這個答案來得太晚了,但它解決了評論中提出的一些問題或尚未解決的一些問題。 在谷歌搜索期間出現了這個問題。
代碼尚未分析。 有可能一些加速沒有加速任何東西; 但是,在調整結束時,firefox 的 CPU 消耗非常輕。 它設置為以 40 fps 運行。 使“延遲”變小以加快速度並對 CPU 征稅。
var sineval; var offset = 0; var animFlag; var canvas; var ctx; var obj; var milli; var delay=25; var adj=1/delay; // .04 or so for 25 delay function init() { canvas = document.getElementById("canvas"); ctx = canvas.getContext("2d"); obj=ctx.getImageData(0,0,canvas.width,canvas.height); for (let i=0; i<obj.data.length; i+=4) { obj.data[i+3]=255; //set all alpha to full one time only needed. } sineval=new Uint8Array(1400); //set up byte based table of final pixel sine values.. 1400 degrees total for (let i=0; i<=1400; ++i) { //1400 sineval[i] = (100-70*Math.sin(i*Math.PI/180))>>>0; } animFlag = setInterval(sineWave, delay); //do processing once every 25 milli milli=Date.now()>>>0; //start time in milli } function sineWave() { let m=((Date.now()-milli)*adj)>>>0; let oldoff = offset; offset=(m % 360)>>>0; //offset,frequency tuned with adj param. for(x=0 ; x<1000 ;++x) { //draw sine wave across canvas length of 1000 let y=sineval[x+oldoff]; obj.data [0+x*4+y*4*canvas.width]=0; //black the reds obj.data [1+x*4+y*4*canvas.width]=0; //black the greens } for(x=0 ; x<1000 ;++x) { //draw sine wave across canvas length of 1000 let y=sineval[x+offset]; if (y<100) { obj.data [1+x*4+y*4*canvas.width]=255; //rGba //green for top half } else { obj.data [0+x*4+y*4*canvas.width]=255; //Rgba //red for bottom half } } requestAnimationFrame(paintit); //at end of processing try to paint next frame boundary } function paintit() { ctx.putImageData(obj,0,0); } init();
<canvas id="canvas" height=300 width=1000></canvas>
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.