Math 画布和单位/法线渐变

Math 画布和单位/法线渐变,math,html5-canvas,gradient,linear-gradients,Math,Html5 Canvas,Gradient,Linear Gradients,我试图在html5画布元素中使用基于单位/法向量的渐变,然后对它们进行变换以获得所需的结果。然而,我似乎认为问题可能是因为我缺乏数学知识。我试图创建一个从0,0到1,0的简单线性梯度,即从左到右的简单单位梯度。之后,我变换画布以缩放、旋转和移动渐变。然而,例如,当给出45度的旋转值时,实际渐变绘制错误。右下角有很多黑色,也就是说,梯度似乎不够大。这是我的密码: var rect = {x: 0, y: 0, w: 500, h: 500}; var rotation = 45 * Math.PI

我试图在html5画布元素中使用基于单位/法向量的渐变,然后对它们进行变换以获得所需的结果。然而,我似乎认为问题可能是因为我缺乏数学知识。我试图创建一个从0,0到1,0的简单线性梯度,即从左到右的简单单位梯度。之后,我变换画布以缩放、旋转和移动渐变。然而,例如,当给出45度的旋转值时,实际渐变绘制错误。右下角有很多黑色,也就是说,梯度似乎不够大。这是我的密码:

var rect = {x: 0, y: 0, w: 500, h: 500};
var rotation = 45 * Math.PI/180;
var sx = 1;
var sy = 1;
var tx = 0;
var ty = 0;
var radial = false;

// Create unit vector 0,0 1,1
var grd = radial ? ctx.createRadialGradient(0, 0, 0, 0, 0, 0.5) : ctx.createLinearGradient(0, 0, 1, 0);
grd.addColorStop(0, 'black');
grd.addColorStop(0.1, 'lime');
grd.addColorStop(0.9, 'yellow');
grd.addColorStop(1, 'black');

// Add our rectangle path before transforming
ctx.beginPath();
ctx.moveTo(rect.x, rect.y);
ctx.lineTo(rect.x + rect.w, rect.y);
ctx.lineTo(rect.x + rect.w, rect.y + rect.h);
ctx.lineTo(rect.x, rect.y + rect.h);
ctx.closePath();

// Rotate and scale unit gradient
ctx.rotate(rotation);
ctx.scale(sx * rect.w, sy * rect.h);
ctx.fillStyle = grd;

// Fill gradient
ctx.fill();
这里有一把小提琴来试一试:

奇怪的是,将单位线性渐变向量更改为大约1.41的因子可以使渐变看起来正确:

ctx.createLinearGradient(0, 0, 1.41, 0)
从这把小提琴中可以看出:


但是我不知道如何计算这个因子?

因为你想使用标准化的渐变,你必须决定如何标准化。在这里,您可以选择使渐变居中,并使其x,y在[-0.5,0.5]范围内。 第一个问题是线性渐变不是居中的,它在[0,1.0]范围内。 以相同的方式对其进行规范化:

var linGrd = ctx.createLinearGradient(-0.5, 0, 0.5, 0);
第二个问题是,您必须转换到图形的中心,然后缩放,然后以规范化的方式绘制。 这意味着您必须使用与渐变相同的坐标系。 因为你们都在画一个以w,h为大小的形状,并使用w,h的比例,所以你们在画一个ww,hh大小的矩形。 正确的绘图代码如下:

function drawRect(rect, fill) {
    ctx.save();
    // translate to the center of the rect (the new (0,0) )
    ctx.translate(rect.x + rect.w / 2, rect.y + rect.h / 2);
    // Rotate 
    ctx.rotate(rotation);
    // scale to the size of the rect
    ctx.scale(rect.w, rect.h);
    // ...
    ctx.fillStyle = fill;
    // draw 'normalized' rect
    ctx.fillRect(-0.5, -0.5, 1, 1);
    ctx.restore();
}
请注意,默认情况下,radialGradient将以0.5的距离结束,这意味着,如果要填充矩形,它将使用渐变的最后一种颜色填充角。也许你希望角点结束渐变。 在这种情况下,您希望使渐变在以下距离处达到其值:

sqrt ( 0.5*0.5 + 0.5*0.5 ) = 0.7  ( pythagore in the normalized circle)
因此,您将定义规格化渐变,如下所示:

var fullRadGrd = ctx.createRadialGradient(0, 0, 0, 0, 0, 0.7) ;

哇,回答得很好。事实上,就在几分钟前,我自己已经解决了线性梯度的中心问题: