【问题标题】:Bezier path function by multiple points with native javascript使用本机 javascript 多点的 Bezier 路径函数
【发布时间】:2019-10-02 18:48:19
【问题描述】:

我需要几个点的贝塞尔函数并将其分开一些步骤。想象一下带有虚线的海盗地图 :) 我有一个破折号(一些带有 x/y 位置和旋转的显示对象)用于构建此地图

我找到了画布方法.quadraticCurveTo() 等,但我需要特定的函数来将曲线与步骤分开

我使用较早的原生函数作为基本贝塞尔曲线

function bezier(t, p0, p1, p2, p3){
  var cX = 3 * (p1.x - p0.x),
      bX = 3 * (p2.x - p1.x) - cX,
      aX = p3.x - p0.x - cX - bX;

  var cY = 3 * (p1.y - p0.y),
      bY = 3 * (p2.y - p1.y) - cY,
      aY = p3.y - p0.y - cY - bY;

  var x = (aX * Math.pow(t, 3)) + (bX * Math.pow(t, 2)) + (cX * t) + p0.x;
  var y = (aY * Math.pow(t, 3)) + (bY * Math.pow(t, 2)) + (cY * t) + p0.y;

  return {x: x, y: y};
}

但我不知道我可以用它来解决我的问题,因为我只有带有简单点 [{x:0, y:0}, {x:-30, y:-50}, {x:-10, y:-100}, {x:-30, y:-150}] 的初始数组(没有贝塞尔控制点)

请帮我计算软路径的控制点

【问题讨论】:

  • 这看起来不像是贝塞尔函数?对于三次曲线(因为您显示四个点),它是 a(1-t)³ + 3bt(1-t)² + 3ct²(1-t) + dt³ 其中 a、b、c 和 d 是您的坐标值。此外,Canvas2D API 支持使用 bezierCurveTo() 的三次贝塞尔曲线,因此甚至没有任何理由手动计算任何曲线坐标。所以说了这么多,给pomax.github.io/bezierinfo看看吧。

标签: javascript bezier


【解决方案1】:

您可以根据之前和之后的点为点创建控制点:

function cp(a, b, c) {
  if (!a || !c) return b;
  return {
    x: b.x + (c.x - a.x) * .25,
    y: b.y + (c.y - a.y) * .25
  };
}

以及相应的贝塞尔函数

function bezier4(t,a,b,c,d) {
  var u = 1-t, fa = u*u*u, fb = 3*u*u*t, fc = 3*u*t*t, fd = t*t*t;
  return {
    x: a.x*fa + b.x*fb + c.x*fc + d.x*fd
    y: a.y*fa + b.y*fb + c.y*fc + d.y*fd
  };
}

function bezier(t, ...points) {
  var last = points.length-1;
  t *= last;

  if(t <= 0) return points[0];
  if(t >= last) return points[last];
  var i = Math.floor(t);
  if(t === i) return points[i];

  return bezier4(
    t-i,
    points[i],
    cp(points[i-1], points[i], points[i+1]),
    cp(points[i+2], points[i+1], points[i]),
    points[i+1]
  );
}

还有一点 sn-p 取点并创建一个 svg。

var points = [{x:0, y:0}, {x:-30, y:-50}, {x:-10, y:-100}, {x:-30, y:-150}];

function cp(a, b, c) {
  if (!a || !c) return b;
  return {
    x: b.x + (c.x - a.x) * .25,
    y: b.y + (c.y - a.y) * .25
  };
}

var pointsWithControlPoints = points.flatMap((pt, i) => [
    cp(points[i + 1], pt, points[i - 1]),
    pt,
    cp(points[i - 1], pt, points[i + 1]),
  ])
  .slice(1, -1) // remove the control points before the first point and after the last one
  .map(pt => [pt.x, pt.y]);

var bounds = points.reduce((bounds, pt) => {
  bounds.top = Math.min(bounds.top, pt.y);
  bounds.left = Math.min(bounds.left, pt.x);
  bounds.bottom = Math.max(bounds.bottom, pt.y);
  bounds.right = Math.max(bounds.right, pt.x);
  return bounds;
}, {
  top: points[0].x,
  left: points[0].y,
  bottom: points[0].x,
  right: points[0].y
});
bounds.width = bounds.right - bounds.left;
bounds.height = bounds.bottom - bounds.top;

document.body.innerHTML = `<svg 
  viewBox="${bounds.left-10} ${bounds.top-10} ${bounds.width+20} ${bounds.height+20}"
  width="${bounds.width+20}"
  height="${bounds.height+20}"
>
  <path fill="none" 
    stroke="black" 
    stroke-dasharray="4" 
    d="M${pointsWithControlPoints[0]}C${pointsWithControlPoints.slice(1)}" 
    />
  ${points.map(pt => `<rect x="${pt.x - 4}" y="${pt.y - 4}" width="8" height="8" />`).join("\n")}
</svg>`;

【讨论】:

  • 谢谢!我编写了类似的代码。但我还有一个问题:破折号prnt.sc/pdwro4 之间的空间不同(在我的情况下,4 条曲线的长度不同)我想像一条一样连接所有曲线并在它们上面放置 25 个破折号
  • 我认为可以通过贝塞尔函数的一些迭代来计算曲线长度
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-23
  • 1970-01-01
  • 2014-01-22
  • 1970-01-01
相关资源
最近更新 更多