2014-03-24 85 views
0

這個項目http://biduleohm.free.fr/ledohm/(對不起,用戶界面是法語但代碼是英文)我需要一個角度漸變,但它不存在原生的,所以我已經在一條線上使用線性漸變來實現它,並且我將線畫得越來越長以形成一個三角形。其結果是圖形確定,但速度並不是很好(125個三角形爲1850毫秒)。它位於[Répartition]選項卡中,如果其中一個輸入端存在關鍵事件,則重新繪製,不要害怕明顯的慢,我每2000毫秒限制一次重繪。畫布:用漸變漸變繪製大量元素(模擬角度漸變)

在整個三角形上使用了一個簡單的線性漸變(但這與現實不匹配)並且速度正常之前,它在不到一秒的時間內就繪製了數千個三角形。使用此功能:

drawFrontLightForColor : function(x, y, w, h, color) { 
    var x2 = x - w; 
    var x3 = x + w; 
    var gradient = Distri.frontCanvas.createLinearGradient(x2, y, x3, y); 
    gradient.addColorStop(0, 'rgba(' + color + ', ' + Distri.lightEdgeAlpha + ')'); 
    gradient.addColorStop(0.5, 'rgba(' + color + ', ' + (color == Distri.lightColors.cw ? Distri.lightCenterAlphaCw : Distri.lightCenterAlphaOther) + ')'); 
    gradient.addColorStop(1, 'rgba(' + color + ', ' + Distri.lightEdgeAlpha + ')'); 
    Distri.frontCanvas.fillStyle = gradient; 
    Distri.frontCanvas.beginPath(); 
    Distri.frontCanvas.moveTo(x, y); 
    Distri.frontCanvas.lineTo(x2, (y + h)); 
    Distri.frontCanvas.lineTo(x3, (y + h)); 
    Distri.frontCanvas.lineTo(x, y); 
    Distri.frontCanvas.fill(); 
    Distri.frontCanvas.closePath(); 
}, 

然後我切換到該功能:

drawFrontLightForColor : function(x, y, w, h, centerColor, edgeColor) { 
    var ratio = w/h; 
    var tmpY; 
    var tmpW; 
    var x2; 
    var x3; 
    var gradient; 
    Distri.frontCanvas.lineWidth = 1; 
    for (var tmpH = 0; tmpH < h; tmpH++) { 
     tmpY = y + tmpH; 
     tmpW = Math.round(tmpH * ratio); 
     x2 = x - tmpW; 
     x3 = x + tmpW; 
     gradient = Distri.frontCanvas.createLinearGradient(x2, tmpY, x3, tmpY); 
     gradient.addColorStop(0, edgeColor); 
     gradient.addColorStop(0.5, centerColor); 
     gradient.addColorStop(1, edgeColor); 
     Distri.frontCanvas.beginPath(); 
     Distri.frontCanvas.moveTo(x2, tmpY); 
     Distri.frontCanvas.lineTo(x, tmpY); 
     Distri.frontCanvas.lineTo(x3, tmpY); 
     Distri.frontCanvas.strokeStyle = gradient; 
     Distri.frontCanvas.stroke(); 
     Distri.frontCanvas.closePath(); 
    } 
}, 

你可以找到整個源here

我不能把beginPath方法,中風,closePath出來的因爲每次迭代都會改變漸變的循環(我試過了,但是它對每一行使用了最後一個漸變(具有諷刺意味的是,它與第一個功能相同......)這是可以理解的,但不是我想要的)。

我接受任何建議(包括重做整個功能,並修改他的調用者外包一些代碼),以提高速度讓我們說5倍(理想情況下更多)。

+1

你可以試試'WebGL'。它應該能夠處理數百萬個三角形。 – kirilloid

+0

我在鏈接上找不到「Répartition」選項卡。你能附上一個.png到你的問題。 – markE

+0

是的,但如果可能的話,我想保留畫布。此外,在我看來,WebGL與所有瀏覽器不兼容,但我會檢查,謝謝。 @markE我不能發佈超過2個鏈接,因爲這是我的第一個問題。但是執行Shared。$ tabs._1.click();在控制檯。 – Biduleohm

回答

0

我認爲你從一開始就採取了錯誤的方式:當進行如此多的顏色變化時,最好在像素級別進行操作。
所以是的,這可能是一個webgl像素着色器,但你必須爭取在所有平臺上運行好的樣板(或者讓lib來爲你做)。
無論如何,有一種完美的解決方案可以滿足您的需求,並且足夠快(幾毫秒):使用原始像素數據,用相關函數逐一更新它們,然後繪製結果。

執行此操作的步驟如下:
- 創建一個與畫布相同大小的緩衝區。
- 迭代其像素,記錄點的x,y。
- 標準化座標,以便它們符合您的'空間'。
- 計算您擁有的所有數據中標準化(x,y)的值。
- 寫出一個顏色(在我的例子中,我選擇灰度)超出該值。
- 將整個緩衝區繪製到畫布上。

我做了的jsfiddle,和這裏的有4個數據點的結果:

enter image description here

小提琴是在這裏: http://jsfiddle.net/gamealchemist/KsM9c/3/

var canvas = document.getElementById("canvas"); 
var ctx = canvas.getContext('2d'); 
var width = canvas.width, 
    height = canvas.height; 

// builds an image for the target canvas 
function buildImage(targetCanvas, valueForXY, someData) { 
    var width = targetCanvas.width; 
    var height = targetCanvas.height; 
    var tempImg = ctx.createImageData(width, height); 
    var buffer = tempImg.data; 
    var offset = 0; 
    var xy = [0, 0]; 
    function normalizeXY(xy) { 
     xy[0] = xy[0]/width ; 
     xy[1] = xy[1]/height; 
    } 
    for (var y = 0; y < height; y++) 
    for (var x = 0; x < width; x++, offset += 4) { 
     xy[0] = x; xy[1]=y; 
     normalizeXY(xy); 
     var val = Math.floor(valueForXY(xy, someData) * 255); 
     buffer[offset] = val; 
     buffer[offset + 1] = val; 
     buffer[offset + 2] = val; 
     buffer[offset + 3] = 255; 
    } 
    ctx.putImageData(tempImg, 0, 0); 
} 


// return normalized (0->1) value for x,y and 
//  provided data. 
// xy is a 2 elements array 
function someValueForXY(xy, someData) { 
    var res = 0; 
    for (var i = 0; i < someData.length; i++) { 
     var thisData = someData[i]; 
     var dist = Math.pow(sq(thisData[0] - xy[0]) + sq(thisData[1] - xy[1]), -0.55); 
     localRes = 0.04 * dist; 
     res += localRes; 
    } 
    if (res > 1) res = 1; 
    return res; 
} 

var someData = [ 
    [0.6, 0.2], 
    [0.35, 0.8], 
    [0.2, 0.5], 
    [0.6, 0.75] 
]; 

buildImage(canvas, someValueForXY, someData); 

// ------------------------ 
function sq(x) { 
    return x * x 
} 
+0

非常聰明,謝謝。我將把它用於現有的畫布(此外,它允許使用任何公式來淡出光線,我的線性淡出不太現實......),並且我將添加一個帶有WebGL 3D模擬的第三個畫布。另一個小問題是:多種顏色的真實混合只是每個組件的所有來源的值的增加?還是更復雜? (二次方也許?) – Biduleohm

+0

不客氣。對於你的webgl想法,如果你願意,可以去找它,但如果imageData解決方案不夠好(對於30 fps而不是全屏畫布,我確定它是),那麼一定要先進行基準測試。特別是因爲webgl不擅長迭代對象,所以你的像素着色器會很大而且很慢(但速度肯定會更快)。 對於混合,最容易的是分離r,g,b以清楚地看到事物。所以是的基本方法是一個補充,但你可能更喜歡一些其他的混合...太多在這裏說! Bonne的機會! – GameAlchemist

+0

實際上,畫布僅在輸入中的更改事件以及延遲2秒後刷新(如果用戶不斷更改參數,則不需要刷新......),因此無需以許多fps運行,因此解決方案可能足夠好,我會在確定之前在許多光源上進行測試。我在WebGL方面遇到了一些問題,但我會爲此提出另一個主題,因爲它與此問題無關。 Encore merci :) – Biduleohm

0

實際上GameAlchemist的解決方案是不是快或我做了一件非常錯誤的事情我只爲頂視圖實現了這個算法,因爲前視圖要複雜得多。

對於120個燈,頂視圖需要100-105毫秒的舊代碼,並且此代碼需要1650-1700毫秒(並且此代碼在新代碼中仍然缺少一些東西,例如顏色):

drawTopLightForColor_ : function(canvasW, canvasD, rampX, rampY, rampZ, ledsArrays, color) { 
    function sq(x) { 
     return x * x; 
    } 
    var tmpImg = Distri.topCanvasCtx.createImageData(canvasW, canvasD); 
    var rawData = tmpImg.data; 
    var ledsArray = ledsArrays[color]; 
    var len = ledsArray.length; 
    var i = 0; 
    for (var y = 0; y < canvasD; y++) { 
     for (var x = 0; x < canvasW; x++, i += 4) { 
      var intensity = 0; 
      for (var j = 0; j < len; j++) { 
       intensity += 2 * Math.pow(
        sq((rampX + ledsArray[j].x) - x) + 
        sq((rampZ + ledsArray[j].y) - y), 
        -0.5 
       ); 
      } 
      if (intensity > 1) { 
       intensity = 1; 
      } 
      intensity = Math.round(intensity * 255); 
      rawData[i] = intensity; 
      rawData[i + 1] = intensity; 
      rawData[i + 2] = intensity; 
      rawData[i + 3] = 255; 
     } 
    } 
    Distri.topCanvasCtx.putImageData(tmpImg, 0, 0); 
}, 

我做錯了什麼?