簡體   English   中英

使畫布的 clearRect() 工作得更快

[英]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 ;
}

您需要重構一下代碼:

  • 將所有全局變量(例如canvas和context)移到循環函數之外
  • 在循環內部,在開始時清除整個畫布,重新繪制正弦
  • 使用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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM