HTML5 Canvas围绕中心旋转渐变

时间:2017-08-10 22:42:32

标签: javascript html5 canvas

我正在尝试围绕画布的中心点旋转渐变,但我没有找到为实现此目的而需要进行的正确计算。

我想要做的一个例子是:http://victorblog.com/html5-canvas-gradient-creator/。旋转滑块正是我想要做的,但我已经查看了源代码,逻辑似乎可以使用cos / sin进行简化(虽然我不是这方面的专家,因此我问这个问题)。

我发现这个SO线程(Calculate rotation of canvas gradient)有点帮助,但这围绕中心点而不是像第一个例子中的旋转滑块那样坚持它。

非常感谢任何帮助者。

由于

1 个答案:

答案 0 :(得分:1)

旋转渐变。

简单旋转。

如果您不担心渐变拟合画布,那么简单的旋转就可以解决问题。

首先,你必须计算出渐变的最大长度,这样当它是对角线时它仍然适合画布。

const maxLength = Math.sqrt(canvas.width * canvas.width + canvas.height * canvas.height);

然后您可以按如下方式创建渐变

var angle = ?; // The angle in radians
               // A value of 0 if a gradient from right to left
const gradient = ctx.createLinearGradient(
     // the start of the gradient added to the center
     canvas.width / 2 + Math.cos(angle) * maxLength * 0.5,
     canvas.height / 2 + Math.sin(angle) * maxLength * 0.5,
     // the end of the gradient subtracted from the center
     canvas.width / 2 - Math.cos(angle) * maxLength * 0.5,
     canvas.height / 2 - Math.sin(angle) * maxLength * 0.5
)

这样可以工作但是当它不在对角线上时会剪切渐变。

适合对角线示例



const eachOf=(a,cb)=>{var i=0;const len=a.length;while(i<len)cb(a[i], i++);};
const ctx = canvas.getContext("2d");
const w = canvas.width;
const h = canvas.height;
const maxWidth = Math.sqrt(w * w + h * h) / 2;
// empty colour items are skipped when creating the gradient
const gradientColours = ["white","blue",,,,"green","yellow","green",,,,"cyan","black"];
function createRotatedGradient(angle, colors){
    const g = ctx.createLinearGradient(
        w / 2 + Math.cos(angle) * maxWidth,  // start pos
        h / 2 + Math.sin(angle) * maxWidth,      
        w / 2 - Math.cos(angle) * maxWidth,  // end pos    
        h / 2 - Math.sin(angle) * maxWidth      
    );
    // add colours
    eachOf(colors,(col,i)=> col && g.addColorStop(i / (colors.length - 1), col) );
    return g;
}

function update(timer){
    ctx.fillStyle = createRotatedGradient(timer / 1000,gradientColours);
    ctx.fillRect(0,0,w,h);
    requestAnimationFrame(update);
}
requestAnimationFrame(update);
&#13;
canvas { border : 2px solid black; }
&#13;
<canvas id="canvas"></canvas><
&#13;
&#13;
&#13;

可以通过将渐变的maxWidth设置为canvas.heightcanvas.width

来修改此宽度或高度

适合高度示例

&#13;
&#13;
const eachOf=(a,cb)=>{var i=0;const len=a.length;while(i<len)cb(a[i], i++);};
const ctx = canvas.getContext("2d");
const w = canvas.width;
const h = canvas.height;
const maxWidth = h / 2;
// empty colour items are skipped when creating the gradient
const gradientColours = ["white","green",,,,"blue","cyan","blue",,,,"yellow","black"];
function createRotatedGradient(angle, colors){
    const g = ctx.createLinearGradient(
        w / 2 + Math.cos(angle) * maxWidth,  // start pos
        h / 2 + Math.sin(angle) * maxWidth,      
        w / 2 - Math.cos(angle) * maxWidth,  // end pos    
        h / 2 - Math.sin(angle) * maxWidth      
    );
    // add colours
    eachOf(colors,(col,i)=> col && g.addColorStop(i / (colors.length - 1), col) );
    return g;
}

function update(timer){
    ctx.fillStyle = createRotatedGradient(timer / 1000,gradientColours);
    ctx.fillRect(0,0,w,h);
    requestAnimationFrame(update);
}
requestAnimationFrame(update);
&#13;
canvas { border : 2px solid black; }
&#13;
<canvas id="canvas"></canvas><
&#13;
&#13;
&#13;

适合宽度示例

&#13;
&#13;
const eachOf=(a,cb)=>{var i=0;const len=a.length;while(i<len)cb(a[i], i++);};
const ctx = canvas.getContext("2d");
const w = canvas.width;
const h = canvas.height;
const maxWidth = w / 2;
// empty colour items are skipped when creating the gradient
const gradientColours = ["white","blue",,,,"yellow","green","yellow",,,,"cyan","black"];
function createRotatedGradient(angle, colors){
    const g = ctx.createLinearGradient(
        w / 2 + Math.cos(angle) * maxWidth,  // start pos
        h / 2 + Math.sin(angle) * maxWidth,      
        w / 2 - Math.cos(angle) * maxWidth,  // end pos    
        h / 2 - Math.sin(angle) * maxWidth      
    );
    // add colours
    eachOf(colors,(col,i)=> col && g.addColorStop(i / (colors.length - 1), col) );
    return g;
}

function update(timer){
    ctx.fillStyle = createRotatedGradient(timer / 1000,gradientColours);
    ctx.fillRect(0,0,w,h);
    requestAnimationFrame(update);
}
requestAnimationFrame(update);
&#13;
canvas { border : 2px solid black; }
&#13;
<canvas id="canvas"></canvas><
&#13;
&#13;
&#13;

简单动态契合

要适合宽度和高度,您可以使用非常简单的y轴缩放。该比例是宽度和高度的比率。

const maxLen = canvas.width;
const aspect = canvas.height / canvas.width;
const angle = ?
const gradient = ctx.createLinearGradient(
     // the start of the gradient added to the center
     canvas.width / 2 + Math.cos(angle) * maxLen * 0.5,
     canvas.height / 2 + Math.sin(angle) * maxLen * 0.5 * aspect,
     // the end of the gradient subtracted from the center
     canvas.width / 2 - Math.cos(angle) * maxLen * 0.5,
     canvas.height / 2 - Math.sin(angle) * maxLen * 0.5 * aspect
)

方面缩放的示例

&#13;
&#13;
const eachOf=(a,cb)=>{var i=0;const len=a.length;while(i<len)cb(a[i], i++);};
const ctx = canvas.getContext("2d");
const w = canvas.width;
const h = canvas.height;
const maxWidth = w / 2;
const aspect = h / w;
// empty colour items are skipped when creating the gradient
const gradientColours = ["white","red",,,,"yellow","green","yellow",,,,"red","black"];
function createRotatedGradient(angle, colors){
    const g = ctx.createLinearGradient(
        w / 2 + Math.cos(angle) * maxWidth,  // start pos
        h / 2 + Math.sin(angle) * maxWidth * aspect,      
        w / 2 - Math.cos(angle) * maxWidth,  // end pos    
        h / 2 - Math.sin(angle) * maxWidth * aspect      
    );
    // add colours
    eachOf(colors,(col,i)=> col && g.addColorStop(i / (colors.length - 1), col) );
    return g;
}

function update(timer){
    ctx.fillStyle = createRotatedGradient(timer / 1000,gradientColours);
    ctx.fillRect(0,0,w,h);
    requestAnimationFrame(update);
}
requestAnimationFrame(update);
&#13;
canvas { border : 2px solid black; }
&#13;
<canvas id="canvas"></canvas><
&#13;
&#13;
&#13;

最合适。

你提供的example site拟合渐变,使其来自最近的边缘,这是一个更好的拟合,但仍然不是完美的拟合,因为渐变有时太短。由于你有该网站的方法,我不会在这里包含它。

最佳拟合稍微复杂一点,但总是适合画布,以便画布没有过度或不足的流动(不会在渐变之外设置像素,并且所有渐变都可以在任何角度看到)

有关信息,请参阅示例。数学是根据适合旋转图像的this answer进行调整的。

最适合的例子。

&#13;
&#13;
const eachOf=(a,cb)=>{var i=0;const len=a.length;while(i<len)cb(a[i], i++);};
const ctx = canvas.getContext("2d");
const w = canvas.width;
const h = canvas.height;
const maxWidth = w / 2;
const aspect = h / w;
// empty colour items are skipped when creating the gradient
const gradientColours = ["black","white",,,,"white","red",,,,,,,,,"yellow","green","yellow",,,,,,,,,"red","black",,,,"black","white"];

function bestFitGradient(angle, colors){
    
    var dist = Math.sqrt(w * w + h * h) / 2; // get the diagonal length    
    var diagAngle = Math.asin((h / 2) / dist); // get the diagonal angle

    // Do the symmetry on the angle (move to first quad
    var a1 = ((angle % (Math.PI *2))+ Math.PI*4) % (Math.PI * 2);
    if(a1 > Math.PI){ a1 -= Math.PI }
    if(a1 > Math.PI / 2 && a1 <= Math.PI){ a1 = (Math.PI / 2) - (a1 - (Math.PI / 2)) }
    // get angles from center to edges for along and right of gradient
    var ang1 = Math.PI/2 - diagAngle - Math.abs(a1);
    var ang2 = Math.abs(diagAngle - Math.abs(a1));
    
    // get distance from center to horizontal and vertical edges
    var dist1 = Math.cos(ang1) * h;
    var dist2 = Math.cos(ang2) * w;
    
    // get the max distance
    var scale = Math.max(dist2, dist1) / 2;
    
    // get the vector to the start and end of gradient
    var dx = Math.cos(angle) * scale;
    var dy = Math.sin(angle) * scale;
    
    // create the gradient
    const g = ctx.createLinearGradient(
        w / 2 + dx,  // start pos
        h / 2 + dy,      
        w / 2 - dx,  // end pos    
        h / 2 - dy      
    );
    // add colours
    eachOf(colors,(col,i)=> col && g.addColorStop(i / (colors.length - 1), col) );
    return g;
}



function update(timer){
    ctx.fillStyle = bestFitGradient(timer / 1000,gradientColours);
    ctx.fillRect(0,0,w,h);
    requestAnimationFrame(update);
}
requestAnimationFrame(update);
&#13;
canvas { border : 2px solid black; }
&#13;
<canvas id="canvas"></canvas><
&#13;
&#13;
&#13;