这个项目http://biduleohm.free.fr/ledohm/(对不起,用户界面是法语但代码是英文)我需要一个角度渐变,但它不存在于原生中,所以我用线性渐变实现了它在一条线上,我画线越来越长,形成一个三角形。结果图形正常,但速度不是很好(125个三角形为1850毫秒)。它位于[Répartition]选项卡中,如果其中一个输入上有一个keyup事件,它会重绘,不要害怕明显的慢,我限制为每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,stroke,closePath放在循环之外,因为每次迭代都会改变渐变(我已尝试但是它使用了每一行的最后一个渐变(具有讽刺意味的是,它与第一个功能...)这是可以理解的,但不是我想要的。)
我接受任何建议(包括重做整个功能并修改他的调用者以外包一些代码)来提高速度,比如5倍(理想情况下更多)。
答案 0 :(得分:0)
我认为你从一开始就采取了错误的方式:当做了如此多的颜色变化时,你最好在像素级别进行操作。
所以是的,可以使用webgl像素着色器,但是你必须努力让所有平台上的样板运行正常(或者让lib为你做这个)。
无论如何,这里有一个完美的解决方案,并且足够快(几毫秒):使用原始像素数据,使用相关功能逐个更新它们,然后绘制结果。
这样做的步骤是:
- 创建一个与画布大小相同的缓冲区
- 迭代它的像素,跟踪点的x,y
- 规范化坐标,使其符合您的空间&#39;
- 计算所有数据中标准化(x,y)的值
- 从该值中写出一种颜色(在我的例子中,我选择灰度)
- 将整个缓冲区绘制到画布上。
我做了一个jsfiddle,这里有4个数据点的结果:
小提琴在这里: 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
}
答案 1 :(得分: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);
},
我做错了吗?