HTML5 Canvas以最佳适配方式围绕中心旋转渐变

huangapple go评论86阅读模式
英文:

HTML5 Canvas rotate gradient around centre with best fit

问题

我想要创建一个渐变,无论角度如何,都覆盖整个画布。

所以我使用了在Stack Overflow帖子中找到的一种方法,但实际上是不正确的。解决方案几乎正确,但事实上,画布并没有完全被渐变覆盖。

这就是答案:https://stackoverflow.com/a/45628098/5594331
(你必须查看最后一个名为"最佳适配示例"的部分。)

在下面的我的代码示例中,黄色部分不应可见,因为它应该被黑白渐变覆盖。这基本上是Blindman67答案中的代码,稍作调整以突出显示问题。

我用绿色绘制了渐变的控制点。通过正确的计算,这些点应该在任何角度都延伸到画布的边缘。

HTML5 Canvas以最佳适配方式围绕中心旋转渐变

<canvas id="canvas" width="300" height="200"></canvas>
canvas {
  border: 2px solid red;
}
var ctx = canvas.getContext("2d");
var w = canvas.width;
var h = canvas.height;

function bestFitGradient(angle) {
    var dist = Math.sqrt(w * w + h * h) / 2; // 获取对角线长度
    var diagAngle = Math.asin((h / 2) / dist); // 获取对角线角度

    // 对角线角度的对称性(移到第一象限)
    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)) }
    // 获取梯度左侧和右侧从中心到边缘的角度
    var ang1 = Math.PI / 2 - diagAngle - Math.abs(a1);
    var ang2 = Math.abs(diagAngle - Math.abs(a1));

    // 获取从中心到水平和垂直边缘的距离
    var dist1 = Math.cos(ang1) * h;
    var dist2 = Math.cos(ang2) * w;

    // 获取最大距离
    var scale = Math.max(dist2, dist1) / 2;

    // 获取梯度起始和结束的向量
    var dx = Math.cos(angle) * scale;
    var dy = Math.sin(angle) * scale;

    var x0 = w / 2 + dx;
    var y0 = h / 2 + dy;
    var x1 = w / 2 - dx;
    var y1 = h / 2 - dy;

    // 创建渐变
    const g = ctx.createLinearGradient(x0, y0, x1, y1);

    // 添加颜色
    g.addColorStop(0, "yellow");
    g.addColorStop(0, "white");
    g.addColorStop(0.5, "black");
    g.addColorStop(1, "white");
    g.addColorStop(1, "yellow");

    return {
        g: g,
        x0: x0,
        y0: y0,
        x1: x1,
        y1: y1
    };
}

function update(timer) {
    var r = bestFitGradient(timer / 1000);

    // 绘制渐变
    ctx.fillStyle = r.g;
    ctx.fillRect(0, 0, w, h);

    // 绘制点
    ctx.lineWidth = 3;
    ctx.fillStyle = '#00FF00';
    ctx.strokeStyle = '#FF0000';
    ctx.beginPath();
    ctx.arc(r.x0, r.y0, 5, 0, 2 * Math.PI, false);
    ctx.stroke();
    ctx.fill();
    ctx.beginPath();
    ctx.arc(r.x1, r.y1, 5, 0, 2 * Math.PI, false);
    ctx.stroke();
    ctx.fill();

    requestAnimationFrame(update);
}
requestAnimationFrame(update);

<details>
<summary>英文:</summary>
I want to make a gradient that covers the whole canvas whatever the angle of it.
So I used a method found on a Stack Overflow post which is finally incorrect. The solution is almost right but, in fact, the canvas is not totally covered by the gradient.
It is this answer: https://stackoverflow.com/a/45628098/5594331
(You have to look at the last point named &quot;Example of best fit.&quot;)
In my code example below, the yellow part should not be visible because it should be covered by the black and white gradient. This is mostly the code written in Blindman67&#39;s answer with some adjustments to highlight the problem.
I have drawn in green the control points of the gradient. With the right calculations, these should be stretched to the edges of the canvas at any angle.
[![enter image description here][1]][1]
&lt;!-- begin snippet: js hide: false console: true babel: false --&gt;
&lt;!-- language: lang-js --&gt;
var ctx = canvas.getContext(&quot;2d&quot;);
var w = canvas.width;
var h = canvas.height;
function bestFitGradient(angle){
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 &gt; Math.PI){ a1 -= Math.PI }
if(a1 &gt; Math.PI / 2 &amp;&amp; a1 &lt;= 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;
var x0 =  w / 2 + dx;
var y0 =  h / 2 + dy;
var x1 =  w / 2 - dx;
var y1 =  h / 2 - dy;
// create the gradient
const g = ctx.createLinearGradient(x0, y0, x1, y1);
// add colours
g.addColorStop(0, &quot;yellow&quot;);
g.addColorStop(0, &quot;white&quot;);
g.addColorStop(.5, &quot;black&quot;);
g.addColorStop(1, &quot;white&quot;);
g.addColorStop(1, &quot;yellow&quot;);
return {
g: g,
x0: x0,
y0: y0,
x1: x1,
y1: y1
};
}	
function update(timer){
var r = bestFitGradient(timer / 1000);
// draw gradient
ctx.fillStyle = r.g;
ctx.fillRect(0,0,w,h);
// draw points
ctx.lineWidth = 3;
ctx.fillStyle = &#39;#00FF00&#39;;
ctx.strokeStyle = &#39;#FF0000&#39;;
ctx.beginPath();
ctx.arc(r.x0, r.y0, 5, 0, 2 * Math.PI, false);
ctx.stroke();
ctx.fill();
ctx.beginPath();
ctx.arc(r.x1, r.y1, 5, 0, 2 * Math.PI, false);
ctx.stroke();
ctx.fill();
requestAnimationFrame(update);
}
requestAnimationFrame(update);
&lt;!-- language: lang-css --&gt;
canvas {
border : 2px solid red;
}
&lt;!-- language: lang-html --&gt;
&lt;canvas id=&quot;canvas&quot; width=&quot;300&quot; height=&quot;200&quot;&gt;&lt;/canvas&gt;
&lt;!-- end snippet --&gt;
[1]: https://i.stack.imgur.com/k8Ibg.png
</details>
# 答案1
**得分**: 1
在[这个示例][1]中,有一个计算旋转线与点之间距离的函数:
```javascript
function distanceToPoint(px, py, angle) {
const cx = width / 2;
const cy = height / 2;
return Math.abs((Math.cos(angle) * (px - cx)) - (Math.sin(angle) * (py - cy)));
}

然后,该函数用于找到线与角点之间的最大距离(仅考虑两个点,因为与另外两个点的距离是镜像的):

const dist = Math.max(
  distanceToPoint(0, 0, angle),
  distanceToPoint(0, height, angle)
);

这可以用于计算梯度末端的偏移点:

const ox = Math.cos(angle) * dist;
const oy = Math.sin(angle) * dist;
const gradient = context.createLinearGradient(
  width / 2 + ox,
  height / 2 + oy,
  width / 2 - ox,
  height / 2 - oy
);
英文:

In this fiddle there is a function that calculates the distance between a rotated line and a point:

function distanceToPoint(px, py, angle) {
const cx = width / 2;
const cy = height / 2;
return Math.abs((Math.cos(angle) * (px - cx)) - (Math.sin(angle) * (py - cy)));
}

Which is then used to find the maximum distance between the line and the corner points (only two points are considered, because the distances to the other two points are mirrored):

const dist = Math.max(
distanceToPoint(0, 0, angle),
distanceToPoint(0, height, angle)
);

Which can be used to calculate offset points for the end of the gradient:

const ox = Math.cos(angle) * dist;
const oy = Math.sin(angle) * dist;
const gradient = context.createLinearGradient(
width / 2 + ox,
height / 2 + oy,
width / 2 - ox,
height / 2 - oy
)

huangapple
  • 本文由 发表于 2023年1月8日 23:23:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/75048967.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定