在 canvas 中多次旋转单行

rotating a single line multiple times in canvas

下面的代码几乎没问题,但我想通过使用单个 makeline() 函数使其简短,以避免重复代码行。 我希望加载图像保持不变,只是我希望代码简短。通过使用上述功能。正如您在下面看到的那样,代码太长了,当它是 运行 时,这些行没有正确调整。 我希望形状保持不变,线条的位置应该相同,只需要一点点调整

<!DOCTYPE html>
<html>

<head>

    <style>
        myCanvas {
            border: 1px;
            background: rgba( 240, 238, 238, 0.898);
        }
    </style>

</head>

<body>

    <canvas id="canvas" width="1000" height="1000" style="border:  ">

</canvas>

    <script>
        var c = document.getElementById("canvas");
        var ctx = c.getContext("2d");
        ctx.lineWidth = 5;

        ctx.beginPath();
        ctx.translate(470, 470)
        ctx.rotate(15 * Math.PI / 180);
        ctx.moveTo(25, 45);
        ctx.lineTo(100, 150);
        ctx.stroke();

        ctx.beginPath();
        ctx.rotate(55 * Math.PI / 180);
        ctx.moveTo(35, 35);
        ctx.lineTo(100, 150);
        ctx.stroke();

        ctx.beginPath();
        ctx.rotate(45 * Math.PI / 180);
        ctx.moveTo(35, 35);
        ctx.lineTo(100, 150);
        ctx.stroke();
        ctx.beginPath();
        ctx.rotate(45 * Math.PI / 180);
        ctx.moveTo(35, 35);
        ctx.lineTo(100, 150);

        ctx.stroke();
        ctx.beginPath();
        ctx.rotate(50 * Math.PI / 180);
        ctx.moveTo(25, 35);
        ctx.lineTo(100, 150);

        ctx.stroke();
        ctx.beginPath();
        ctx.rotate(50 * Math.PI / 180);
        ctx.moveTo(35, 35);
        ctx.lineTo(100, 150);

        ctx.stroke();

        ctx.beginPath();
        ctx.rotate(40 * Math.PI / 180);
        ctx.moveTo(35, 50);
        ctx.lineTo(100, 150);

        ctx.stroke();
        ctx.beginPath();
        ctx.rotate(5 * Math.PI / 180);
        ctx.moveTo(35, 50);
        ctx.lineTo(100, 150);

        ctx.stroke();
        ctx.beginPath();
        ctx.rotate(15 * Math.PI / 180);
        ctx.moveTo(35, 45);
        ctx.lineTo(100, 150);

        ctx.stroke();
        ctx.beginPath();
        ctx.rotate(15 * Math.PI / 180);
        ctx.moveTo(35, 45);
        ctx.lineTo(100, 150);

        ctx.stroke();
        ctx.beginPath();
        ctx.rotate(25 * Math.PI / 180);
        ctx.moveTo(35, 35);
        ctx.lineTo(100, 150);

        ctx.stroke();
        ctx.beginPath();
        ctx.rotate(45 * Math.PI / 180);
        ctx.moveTo(35, 35);
        ctx.lineTo(100, 150);

        ctx.stroke();

    </script>
</body>

</html>

如果你对结果满意,你可以这样写一个函数:

function drawLine(t,r, p1,p2){
//t: translation object 
//r: rotation object
//p1: point  object for the moveTo() method
//p2: point  object for the lineTo() method
    //ctx.save();
    ctx.beginPath();
    ctx.translate(t.x, t.y)
    ctx.rotate(r * Math.PI / 180);
    ctx.moveTo(p1.x, p1.y);
    ctx.lineTo(p2.x, p2.y);
    ctx.stroke();
    //ctx.restore();
}

我已经注释掉了保存和恢复方法,因为您不使用它们,但是这些方法非常有用,可以帮助您节省大量计算。

看来你想画出某种散发着光芒的太阳。

这里有一个选项,代码比你的少很多,关键是用一个函数来画图

var c = document.getElementById("canvas");
var ctx = c.getContext("2d");
var hw = c.width/2; 
var hh = c.height/2
ctx.translate(hw, hh)
ctx.lineWidth = 5;

function drawLine(x, y) {
  ctx.beginPath();
  ctx.moveTo(x * hw/3, y * hh/3);
  ctx.lineTo(x * hw, y * hh);
  ctx.stroke();
}

var p200 = Math.PI * 200
for (i = 0; i < p200; i += p200 / 12)
  drawLine(Math.sin(i/100),  Math.cos(i/100));
<canvas id="canvas" width="170" height="170">



并且使用相同的函数和一些数学技巧,您可以绘制一些更复杂的形状,例如大海 shell 看起来像这样的形状:

var c = document.getElementById("canvas");
var ctx = c.getContext("2d");
const p200 = Math.PI * 200

function drawLine(x, y, m) {
  ctx.beginPath();
  ctx.moveTo(x * m/3, y * m/3);
  ctx.lineTo(x * m, y * m);
  ctx.stroke();
}    

function shell(ini, end, mid) {
  ctx.lineWidth = 5;
  ctx.strokeStyle="black";
  for (i = ini; i < end; i += p200 / 124)
    drawLine(Math.cos(i/100),  Math.sin(i/100), mid - i/10);

  ctx.translate(-3, -3) 
  ctx.strokeStyle="cyan";
  for (i = ini; i < end; i += p200 / 96)
    drawLine(Math.cos(i/100),  Math.sin(i/100), mid - i/10);

  ctx.strokeStyle="blue";
  for (i = ini; i < end; i += p200 / 48)
    drawLine(Math.cos(i/100),  Math.sin(i/100), mid - i/10);     

  ctx.lineWidth = 0.5;
  ctx.strokeStyle="green";
  for (i = ini; i < end; i += p200 / 48)
    drawLine(Math.cos(i/100),  Math.sin(i/100), mid - i/10);
}


ctx.translate(70, 60) 
shell(0, p200, 95)

ctx.translate(200, 40) 
shell(p200/1.8, p200+p200/1.8, 135)
<canvas id="canvas" width="340" height="170">

扫A线

如果您想沿其法线移动直线(偏离其方向 90 度)并在这样做时旋转它,下面将解释如何使用绝对定位(无 ctx.rotate 调用)

开始使用一些实用函数。

// A Point also defines a vector.
const Point = (x,y) => ({x,y});
const Line = (p1, p2) => ({p1, p2});
const lengthOfVec = vec => (vec.x ** 2 + vec.y ** 2) ** 0.5;
const normalVec = line => { // normalize the line and return a vector
     const vec = Point(line.p2.x - line.p1.x, line.p2.y - line.p1.y);
     const length =lengthOfVec(vec);
     vec.x /= length;
     vec.y /= length;
     return vec;
}
const rotateVec90 = vec => ([vec.y, vec.x] = [vec.x, - vec.y], vec);

我们可以从一条由端点 p1p2 定义的线开始,线的法线是线左侧 90 度的向量。

我们将创建一个沿着这条法线移动直线的函数,该函数也会旋转直线,从你的例子来看,直线也需要改变长度,所以我们可以添加一个比例也有争论。

步骤

函数将是 sweepLine(line, distance, rotate, scale),其中 rotate 以弧度为单位(我不会使用度数),distance 以像素为单位,scale > 1 将增长线scale < 1 将缩小线。

function sweepLine(line, dist, rot, scale){

我们需要线的中心和线的归一化和法线作为向量

    const center = Point((line.p1.x + line.p2.x) / 2, (line.p1.y + line.p2.y) / 2);
    const lineNorm = normalVec(line);
    const norm = rotateVec90(Point(lineNorm.x, lineNorm.y));

不旋转

我们需要中心的新位置,如果我们正在旋转,新的中心将在弧的末端,如果不旋转,新的位置将在直线的末端。沿着法线移动中心

    if(rot !== 0){
         // need the dist of point from center 
         const ax = line.p2.x - center.x;
         const ay = line.p2.y - center.y;

         // move the point
         center.x += norm.x * dist;
         center.y += norm.y * dist;

现在我们可以缩放线了

         line.p1.x = center.x - ax * scale 
         line.p1.y = center.y - ay * scale;
         line.p2.x = center.x + ax * scale;
         line.p2.y = center.y + ay * scale;
    }

圆弧上的旋转移动

对于旋转线,我们需要找到圆弧上的点,要定义圆弧,我们需要圆弧的中心。圆弧的长度是角度的变化乘以半径,我们没有半径

     else {
         const arcRadius = dist / rot;

圆弧中心距圆心 arcRadius 距离(注意 rot 可以是负数,这会将圆心移动到正确的位置)

         const arcCenter = Point(
             center.x + lineNorm.x * arcRadius,
             center.y + lineNorm.y * arcRadius
         );

现在我们有了圆心,我们需要圆弧的起始角,也就是直线的方向。

         const startAngle = Math.atan2(lineNorm.y, lineNorm.x);
         const endAngle = startAngle + rot;

我们将旋转添加到 startAngle,然后从 arcCenter 沿新角度向新中心移动 arcRadius 距离。

         center.x = arcCenter.x + Math.cos(endAngle) * arcRadius;             
         center.y = arcCenter.y + Math.sin(endAngle) * arcRadius;

有了中心的新位置,我们可以改变线条的大小,如果我们得到线条的长度,同时旋转它。

         const len = lengthOfVec(Point(line.p1.x - line.p2.x, line.p1.y - line.p2.y));

         line.p1.x = center.x - Math.cos(endAngle) * len * scale * 0.5;
         line.p1.y = center.y - Math.sin(endAngle) * len * scale * 0.5;
         line.p2.x = center.x + Math.cos(endAngle) * len * scale * 0.5;
         line.p2.y = center.y + Math.sin(endAngle) * len * scale * 0.5;

就是这样。函数可以return.

    }
}

例子

为了显示用法示例,下面的代码片段执行相同的操作,但在此过程中进行了一些优化。

该示例创建一条随机线,然后使用 sweepLine 函数移动它。它是连续画线的动画。

requestAnimationFrame(update);

const ctx = canvas.getContext("2d");

var w = canvas.width;
var h = canvas.height;
function update(timer){
    if(w !== innerWidth || h !== innerHeight){
        w = canvas.width = innerWidth;
        h = canvas.height = innerHeight;
    }
    jiggle(); 
    sweepLine(line, moveBy,rotateBy, scaleBy);
    drawLine(line);
    requestAnimationFrame(update);
}


// A Point also defines a vector.
const Point = (x,y) => ({x,y});
const Line = (p1, p2) => ({p1, p2});
const lengthOfVec = vec => (vec.x ** 2 + vec.y ** 2) ** 0.5;
const normalVec = line => { // normalize the line and return a vector
     const vec = Point(line.p2.x - line.p1.x, line.p2.y - line.p1.y);
     const length = lengthOfVec(vec);
     vec.x /= length;
     vec.y /= length;
     return vec;
}
const rotateVec90 = vec => {
    const t = vec.x;
    vec.x = - vec.y; 
    vec.y = t;
    return vec;
}




function sweepLine(line, dist, rot, scale){
    const center = Point((line.p1.x + line.p2.x) / 2, (line.p1.y + line.p2.y) / 2);
    const len = lengthOfVec(Point(line.p1.x - line.p2.x, line.p1.y - line.p2.y));
    const lineNorm = normalVec(line);
    const norm = rotateVec90(Point(lineNorm.x, lineNorm.y));
    if(rot === 0){
         const ax = (line.p2.x - center.x) * scale;
         const ay = (line.p2.y - center.y) * scale;
         center.x += norm.x * dist;
         center.y += norm.y * dist;
         line.p1.x = center.x - ax 
         line.p1.y = center.y - ay;
         line.p2.x = center.x + ax;
         line.p2.y = center.y + ay;
    } else {
         const arcRadius = dist / rot;
         const arcCenter = Point(
             center.x - lineNorm.x * arcRadius, center.y - lineNorm.y * arcRadius
         );
         const endAngle = Math.atan2(lineNorm.y, lineNorm.x) + rot;
         var ax = Math.cos(endAngle);
         var ay = Math.sin(endAngle);
         center.x = arcCenter.x + ax * arcRadius;             
         center.y = arcCenter.y + ay * arcRadius;
         const len = lengthOfVec(Point(line.p1.x - line.p2.x, line.p1.y - line.p2.y));
         ax *= len * scale * 0.5;
         ay *= len * scale * 0.5;
         line.p1.x = center.x - ax;
         line.p1.y = center.y - ay;
         line.p2.x = center.x + ax;
         line.p2.y = center.y + ay;
    }
}



function drawLine(line){
    ctx.lineWidth = 8;
    ctx.lineCap = "round";  
    ctx.strokeStyle = col;
    ctx.beginPath();
    ctx.lineTo(line.p1.x, line.p1.y);
    ctx.lineTo(line.p2.x, line.p2.y);
    ctx.stroke();
}


function createRandomLine(){
    const x = Math.random() * w * 0.3 + w * 0.35;
    const y = Math.random() * h * 0.3 + h * 0.35;
    const len = Math.random() * 40 + 10;
    const dir = Math.random() * Math.PI * 2; 
    return Line(
        Point(x - Math.cos(dir) * len * 0.5, y - Math.sin(dir) * len * 0.5),
        Point(x + Math.cos(dir) * len * 0.5, y + Math.sin(dir) * len * 0.5)
    );
}


// sweep the line randomly needs some settings
var line, rotateBy, moveBy, scaleBy, col, l = 50,s = 70,hue = 0,moveFor = 0; //
function randomize(){
  rotateBy = Math.random() * 0.5 - 0.25;
  moveBy = Math.random() * 5 + 5;
  scaleBy = 1;
  moveFor = 200; 
  line = createRandomLine();

}
function jiggle(){
   if(moveFor === 0 ){ randomize() }
   rotateBy += (Math.random() - 0.5) * 0.2;

   scaleBy = Math.random() < 0.2 ? 1/1.1 : Math.random() < 0.2 ?  1.1 : 1;
   moveBy +=  (Math.random() - 0.5) * 4;
   moveFor --;
   hue = (hue + 1) % 360;
   s = (s + 100  + Math.random() - 0.5) % 100;
   l = (l + 100  + Math.random() - 0.5) % 100;
   col = "hsl("+hue+","+s+"%,"+l+"%)";
}
    
canvas { position : absolute; top : 0px; left : 0px; }
<canvas id="canvas"></canvas>