Canvas HTML - lineTo 和 Bezier 曲线之间的平滑度

Posted

技术标签:

【中文标题】Canvas HTML - lineTo 和 Bezier 曲线之间的平滑度【英文标题】:Canvas HTML - Smoothness between lineTo and Bezier curve 【发布时间】:2019-10-11 11:24:52 【问题描述】:

也许这更像是一个数学问题,但让我们看看:

我正在使用 html5 Canvas 绘制折线图。 该图表基本上是位置 X 时间。 每条线表示在给定时间 (X) 内处于位置 (Y) 的车辆。 我只有关于车辆何时通过道路上确定点的信息。因此,如果车辆停在两点之间,我没有它实际停止的信息,但是当它通过下一个点时,我将能够绘制一条几乎水平的线,因为平均速度,即线坡度,会非常小。

在这些场景中,我们定义了如果车辆以低于 10Km/h 的速度行驶,我应该认为它已经停止并应该画一条水平平滑线。

基本上我必须改变这个:

var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");

ctx.lineWidth = 5;
ctx.lineJoin = 'round';
ctx.lineCap = 'round';

ctx.beginPath();
ctx.moveTo(0, 30);
ctx.lineTo(20, 50);
ctx.lineTo(220, 70);
ctx.lineTo(240, 110);
ctx.stroke();
<canvas id="myCanvas"   style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.</canvas>

进入这个:

var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");

ctx.lineWidth = 5;
ctx.lineJoin = 'round';
ctx.lineCap = 'round';

ctx.beginPath();
ctx.moveTo(0, 30);
ctx.lineTo(20, 50);

ctx.bezierCurveTo(
  50, 70,
  210, 50,
  220, 70
)

ctx.lineTo(240, 110);
ctx.stroke();
<canvas id="myCanvas"   style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.</canvas>

问题是:如何为贝塞尔点选择好的值? 在上面的示例中,我已经通过实验完成了它。我找不到以编程方式选择好的点值的方法,所以我的线条看起来还不错:

var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");

ctx.lineWidth = 5;
ctx.lineJoin = 'round';
ctx.lineCap = 'round';

ctx.beginPath();
ctx.moveTo(0, 30);
ctx.lineTo(20, 50);

ctx.bezierCurveTo(
  20, 70,
  180, 50,
  220, 70
)

ctx.lineTo(240, 110);
ctx.stroke();


ctx.beginPath();
ctx.moveTo(0, 80);
ctx.lineTo(20, 100);

ctx.bezierCurveTo(
  20, 120,
  220, 100,
  220, 120
)

ctx.lineTo(280, 150);
ctx.stroke();
<canvas id="myCanvas"   style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.</canvas>

我正在寻找一种计算简单的解决方案,因为它每次都会用很多线条重绘,所以我不希望这会影响我的表现。

感谢任何提示!

【问题讨论】:

我不知道它是否是曲线拟合中计算最简单的解决方案,但我会使用图形 gems 中的 fitCurve 代码,这里有一个 js 实现:github.com/soswow/fit-curve/blob/master/src/fit-curve.js 真正的问题:当您真的不知道数据应该有多平滑时,为什么要对数据撒谎并让它看起来很平滑?如果您只有快照数据,那么只需显示快照数据。不要让它看起来你知道的比你实际知道的多,只是按原样显示数据?使用连接线已经是有问题的了,这应该是折线图,因为这就是你真正知道的一切。为什么还要坚持用假线连接价值观?无论如何,这不是您的数据可以让您抽象/假设的内容。 @Mike'Pomax'Kamermans 好问题。简单的答案是:客户请求。另外,我不明白您所说的“使用连接线是有问题的,这应该是折线图”是什么意思。会有什么区别? 我只有您在帖子中发布的内容,如果您使用 SO,您可以在这里获得 所有 答案,包括可能对您个人不利的答案,但请记住:你不是在问你,你是在问你,以及将来会发现这个问题的其他所有人,他们也需要首先考虑为什么他们这样做,以及这是否有意义。如果您有客户希望您这样做,那么您应该将其放在您的帖子中,因为它解释了您为什么需要这样做,因此完全删除了“您不应该这样做”的论点。 那么,话虽如此:您能否扩展您的代码,使其成为minimal reproducible example?真正缺少的只是一些数据点的硬编码列表,以及用于将这些数据点转换为图表的函数。现在,您正在显示“裸”画布调用,没有循环任何类型的数据,因此很难就如何使点序列看起来不错给出一个好的答案。 【参考方案1】:

我为我的问题找到了一个很好的解决方案:使用线插值。

拥有以下片段并想象我想在BC 之间进行平滑处理,我​​该如何选择一些能够保证平滑曲线而不是断线的贝塞尔点?

首先,我插入线段AB 并找到点B',这是线AB 将触及C 的Y 坐标的点。然后我用同样的过程找到C'

B'C' 点为贝塞尔曲线提供了很好的点,以便将事物平滑到一条水平线:

这在计算上也足够简单,因为找到直线方程相当简单。

【讨论】:

以上是关于Canvas HTML - lineTo 和 Bezier 曲线之间的平滑度的主要内容,如果未能解决你的问题,请参考以下文章

html 5 canvas LineTo() 线条颜色问题

设置 Canvas.LineTo 的线端样式

如何在 Canvas html 标记中绘制垂直线

delphi Canvas画线问题

canvas路径剪切和判断是否在路径内

canvas绘制3D金字塔