【问题标题】:how to draw a curve that passes three points in canvas?如何在画布中绘制通过三个点的曲线?
【发布时间】:2020-06-24 08:06:44
【问题描述】:

我要做的是改变圆的曲线。 如果我单击圆圈中的一个点并将其拖动到另一点,则该圆弧应该相应地扩展或收缩。 我打算使用 beizer 曲线,但不能保证新的 beizer 曲线会通过拖动点。 附件是鼠标拖动时显示新曲线的图像,我无法解决。 任何人都可以在这件事上帮助我吗? 期待您的回复

【问题讨论】:

  • 为什么没有保证?你的意思是当它们对齐的时候?
  • 我研究了这个材料:'javascript.info/bezier-curve'。当我输入三个点时,新曲线不会通过中间点。它只是放置在三个点的三角形内。你知道如何让它通过中间点吗?我所知道的只是三点坐标。
  • 哦!你的意思是控制点!但是在二维空间上给出三个点一条曲线适合它们。那么这里有一些可以帮助你的东西:stackoverflow.com/questions/6711707/…
  • 感谢您的回答。我看了你给我的材料,假设 t=0.5 它给了我新的控制点( 800, 100 ),但我仍然得到错误的结果。请看一下'ibb.co/K0zqLPr'并告诉我我错在哪里。提前致谢
  • 我找到了一个完整的示例,可以满足您的需求:jsbin.com/ApitIxo/2 请考虑在此处奖励原始提交者:stackoverflow.com/a/20309900/632445

标签: javascript canvas


【解决方案1】:

将圆拟合到点

也许这会有所帮助。

示例顶部的函数fitCircleToPoints(x1, y1, x2, y2, x3, y3) 会将一个圆拟合为 3 个点。

它返回一个对象

{
   x, y,   // center of circle
   radius, // radius of circle
   CCW,    // true if circle segment is counter clockwise
}

如果 3 个点都在同一条线上,则没有可以拟合的圆(半径无穷大无效)因此函数返回 undefined。

function fitCircleToPoints(x1, y1, x2, y2, x3, y3) {
    var x, y, u;
    const slopeA = (x2 - x1) / (y1 - y2); // slope of vector from point 1 to 2
    const slopeB = (x3 - x2) / (y2 - y3); // slope of vector from point 2 to 3
    if (slopeA === slopeB)  { return } // Slopes are same thus 3 points form striaght line. No circle can fit.
    if(y1 === y2){   // special case with points 1 and 2 have same y 
        x = ((x1 + x2) / 2);
        y = slopeB * x + (((y2 + y3) / 2) - slopeB * ((x2 + x3) / 2));  
    }else
    if(y2 === y3){ // special case with points 2 and 3 have same y 
        x = ((x2 + x3) / 2);
        y = slopeA * x + (((y1 + y2) / 2) - slopeA * ((x1 + x2) / 2));  
    } else{
        x = ((((y2 + y3) / 2) - slopeB * ((x2 + x3) / 2)) - (u = ((y1 + y2) / 2) - slopeA * ((x1 + x2) / 2))) / (slopeA - slopeB);
        y = slopeA * x + u;
    }
    
    return {
        x, y, 
        radius: ((x1 - x) ** 2 + (y1 - y) ** 2) ** 0.5,
        CCW: ((x3 - x1) * (y2 - y1) - (y3 - y1) * (x2 - x1)) >= 0,
    };
}





requestAnimationFrame(update);

Math.TAU = Math.PI * 2;
const ctx = canvas.getContext("2d");
const mouse  = {x : 0, y : 0, button : false}   
function mouseEvents(e){
    const bounds = canvas.getBoundingClientRect();
    mouse.x = e.pageX - bounds.left - scrollX;
    mouse.y = e.pageY - bounds.top - scrollY;
    mouse.button = e.type === "mousedown" ? true : e.type === "mouseup" ? false : mouse.button;
}
["down","up","move"].forEach(name => document.addEventListener("mouse" + name, mouseEvents));
var w = canvas.width, h = canvas.height, cw = w / 2, ch = h / 2;
var nearest, ox, oy, dragging, dragIdx;
const points = [10,110,200,100,400,110];
function drawPoint(x, y, rad, col = "black") {
     ctx.strokeStyle = col;
     ctx.beginPath();
     ctx.arc(x, y, rad, 0, Math.TAU);
     ctx.stroke();
 }
function drawLines(idx, col = "black") {
     ctx.strokeStyle = col;
     ctx.beginPath();
     ctx.lineTo(points[idx++], points[idx++]);
     ctx.lineTo(points[idx++], points[idx++]);
     ctx.lineTo(points[idx++], points[idx++]);
     ctx.stroke();
} 
function drawPoints() {
  var i = 0, x, y;
  nearest = - 1;
  var minDist = 20;
  while (i < points.length) {
     drawPoint(x = points[i++], y = points[i++], 4);
     const dist = (x - mouse.x) ** 2 + (y - mouse.y) ** 2;
     if (dist < minDist) {
        minDist = dist;
        nearest = i - 2;
     }
  }
}

function update(){
    ctx.setTransform(1,0,0,1,0,0); // reset transform
    if(w !== innerWidth || h !== innerHeight){
        cw = (w = canvas.width = innerWidth) / 2;
        ch = (h = canvas.height = innerHeight) / 2;
    }else{
        ctx.clearRect(0,0,w,h);
    }
    canvas.style.cursor = "default";
    drawPoints();
    if (nearest > -1) {
      if (mouse.button) {
        if (!dragging) {
            dragging = true;
            ox = points[nearest] - mouse.x;
            oy = points[nearest+1] - mouse.y;
            dragIdx = nearest;
        }
      } else {
          canvas.style.cursor = "move";
      }
      
      
      drawPoint(points[nearest], points[nearest + 1], 6, "red")
    }
    if (dragging) {
        if (!mouse.button) {
            dragging = false;
        } else {
            points[dragIdx] = mouse.x + ox;
            points[dragIdx + 1] = mouse.y + oy
            canvas.style.cursor = "none";
        }
    }
    
    drawLines(0, "#0002");
  
      const circle = fitCircleToPoints(points[0], points[1], points[2], points[3], points[4], points[5]);

    if (circle) {
        ctx.strokeStyle = "#000";
        const ang1 = Math.atan2(points[1] - circle.y, points[0]- circle.x);
        const ang2 = Math.atan2(points[5] - circle.y, points[4]- circle.x);
        ctx.beginPath();
        ctx.arc(circle.x, circle.y, circle.radius, ang1, ang2, circle.CCW);
        ctx.stroke();
    }
    
    requestAnimationFrame(update);
}
canvas { position : absolute; top : 0px; left : 0px; }
<canvas id="canvas"></canvas>
Use mouse to move points.

【讨论】:

    【解决方案2】:

    您可以绘制两条曲线,但要确保控制点在一条直线上,以便获得平滑过渡。使用this tool 我做了一个例子。然而,他的不是一个圆,也不是一个椭圆。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-08-06
      • 2011-10-26
      • 1970-01-01
      • 2023-04-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多