南郊居士 发表于 2020-2-17 22:38

【HTML+js+纯前端】三次方贝塞尔曲线手工拟合小工具

本帖最后由 南郊居士 于 2020-2-18 23:38 编辑

2020.2.18 重要修正:

1. 修正了当图样长宽不同时不能正确显示各控制点的问题。
(一整天了都没人吐槽这个问题,看来这个工具还真是花狸狐哨的成分多些{:1_926:})
2. 新增了多条路径的生成功能。
3. 修正其他小问题,修改了界面。

文章最后有彩蛋。


--------------------------------- 正文分割线 ----------------------------------
之前发布过一个帖子,一个微信微博九宫格图拆分器:https://www.52pojie.cn/thread-1107687-1-1.html

我国著名政治家马邦德曾经说过:“饭要一口口吃,路要一步步走。”在更新的过程中,我突发奇想要添加一个心形裁剪功能,以满足某些人的少女心。于是上网搜索心形线的画法,结果发现凡是用一个方程画出来的,不管是极坐标还是直角坐标,成品都不怎么好看:


于是退而求其次,用贝塞尔曲线分段绘制,可是问题又来了,作为一名数学小白,我不会算曲线方程啊。。。
于是这个工具就诞生了。

------------------------------- 正式内容分割线 ---------------------------------------

工具名称:曲折
功能:可手工拟合任意首尾相连的三次方贝塞尔曲线并生成坐标。
语言:HTML5+Javascript纯前端
操作说明:
可在编辑范围内任意拖动图样,选中下方按钮(变色)后点击编辑区域即可设定各点位置。图样仅仅为绘制曲线的参考,不会参与最后的生成。
指定原点:你的绘制原点,最终生成的全部坐标均以此原点计算偏移,默认为图样正中央。
封闭路径(图上没有截到):使所有曲线构成一个闭环,封闭后无法再添加新线,若仍需添加则选“回到上一段”即可。
随机已绘/在绘颜色:改变一下图中已经画好的曲线/正在画的曲线的显示颜色,避免曲线和图样颜色相近看不清,颜色为随机生成的24位真彩色,如果还是看不清就再变一次。
初始视口:把图片位置恢复至刚打开的状态。
生成点序列:做好了就能生成各贝塞尔曲线控制点的坐标了。
半径:半径的定义是图样的长、宽较大值的一半,你可以任意修改这个半径值,它将乘到最后生成的坐标里面,默认为1。
效果:
按照上图的心型手工拟合的贝塞尔曲线坐标如下:


找了个在线测试器绘制测试:

一颗红心献给你,很漂亮吧。

你也可以自己测试这段代码:
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.save();
ctx.translate(150, 75);
ctx.scale(50, 50);
ctx.fillStyle = "red";
ctx.beginPath();
ctx.moveTo(-0.004082, -0.683418);
ctx.bezierCurveTo(-0.208163, -1.067092, -0.620408, -1.099745, -0.869388, -0.789541);
ctx.bezierCurveTo(-1.146939, -0.365051, -0.906122, 0.075765, -0.730612, 0.259439);
ctx.bezierCurveTo(-0.526531, 0.508418, -0.208163, 0.724745, 0, 1.002296);
ctx.bezierCurveTo(0.204082,0.73699, 0.461224, 0.553316, 0.661224, 0.332908);
ctx.bezierCurveTo(0.946939, 0.06352, 1.106122, -0.401786, 0.906122, -0.728316);
ctx.bezierCurveTo(0.677551, -1.144643, 0.146939, -1.03852, -0.004082, -0.683418);
ctx.fill();
ctx.restore();

工具链接:http://gxsoftware.gitee.io/bezier/
只有一个html文件,源码请自行另存为。


--------------------------------------- 彩蛋分割线 ---------------------------------------

@akronblim 提到:
“任意点数的贝塞尔曲线,不过随着点数变多,计算量变得过大,拖动变得很缓慢”


于是我花了20分钟肝(误)了一张图。


我在网上搜了一张我觉得自己能接受的(搜索“复杂图片”出来的结果光看着就头晕),边缘比较复杂,基本不可能靠人工计算来画出的一张图:



一张很吉祥的图对吧?
正片开始:

这是我手工取点完成以后的样子,用时约15分钟。


最终统计,一共四条路径,共32条曲线:


编写绘制脚本,用时约5分钟:
<!DOCTYPE html>
<html>
<body>

<canvas id="myCanvas" width="300" height="150" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.</canvas>

<script>
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.translate(150, 75);

ctx.fillStyle = "red";
ctx.beginPath();
ctx.moveTo(-27.193803, -21.550932);
ctx.bezierCurveTo(-35.127598, -32.739616, -60.149564, -36.197937, -56.894675, -17.075458);
ctx.bezierCurveTo(-70.117665, -17.68575, -72.151971, -5.886774, -67.269636, 0.623006);
ctx.bezierCurveTo(-64.014746, 6.319064, -56.894675, 8.760231, -53.232923, 4.284758);
ctx.bezierCurveTo(-48.350588, 18.321471, -37.161904, 17.711179, -30.855555, 7.946509);
ctx.bezierCurveTo(-26.17665, -6.903927, -41.230517, -14.43086, -46.926574, -3.649037);
ctx.bezierCurveTo(-47.536866, -16.465166, -31.669277, -8.327941, -27.600665, -5.073051);
ctx.bezierCurveTo(-21.904607, -1.818161, -17.022272, 4.691619, -12.546799, 8.35337);
ctx.bezierCurveTo(-1.561545, 16.490595, 13.288891, 21.779791, 31.394216, 20.355777);
ctx.bezierCurveTo(12.678599, 19.338624, -1.561545, 9.980815, -8.681617, 0.826437);
ctx.bezierCurveTo(-14.377674, -5.073051, -21.090885, -15.854874, -27.193803, -21.550932);
ctx.fill();

ctx.beginPath();
ctx.moveTo(-8.478186, -16.668597);
ctx.bezierCurveTo(-15.801689, -11.582831, -10.105631, -4.66619, -5.630157, 0.216145);
ctx.bezierCurveTo(1.083053, 9.777384, 12.882029, 15.676872, 24.070714, 17.304317);
ctx.bezierCurveTo(40.141733, 20.762638, 57.636767, 14.659719, 69.842604, 4.284758);
ctx.bezierCurveTo(49.499542, 12.421982, 32.6148, 12.828844, 19.391809, 6.725925);
ctx.bezierCurveTo(3.32079, 2.047021, 3.117359, -19.109764, 13.695752, -13.413707);
ctx.bezierCurveTo(6.982541, -5.479912, 15.730058, 2.453882, 22.646699, -0.597577);
ctx.bezierCurveTo(33.225092, -4.259329, 32.207939, -19.516625, 21.426116, -25.009252);
ctx.bezierCurveTo(17.560934, -27.043559, 13.492321, -25.009252, 10.644292, -23.788669);
ctx.bezierCurveTo(10.440862, -32.739616, 2.303637, -35.587645, -5.019866, -31.722463);
ctx.bezierCurveTo(-10.919354, -30.095018, -12.750229, -21.14407, -8.478186, -16.668597);
ctx.fill();

ctx.beginPath();
ctx.moveTo(-26.380081, 11.811691);
ctx.bezierCurveTo(-35.331028, 14.252858, -33.907014, 21.169499, -32.483, 24.424389);
ctx.bezierCurveTo(-31.465847, 27.272418, -28.617818, 29.917016, -26.380081, 28.289571);
ctx.bezierCurveTo(-23.938914, 37.037088, -16.20855, 38.054241, -10.309062, 29.510155);
ctx.bezierCurveTo(-6.850741, 22.593514, -13.15709, 16.897456, -19.056579, 18.728332);
ctx.bezierCurveTo(-24.142344, 22.186652, -19.056579, 27.475849, -16.818842, 23.407236);
ctx.bezierCurveTo(-16.005119, 29.713585, -24.345775, 27.679279, -24.345775, 21.779791);
ctx.bezierCurveTo(-23.328622, 15.676872, -17.632564, 16.083734, -11.733076, 18.524901);
ctx.bezierCurveTo(-3.799282, 24.017528, 8.406556, 26.662126, 21.629546, 22.390083);
ctx.bezierCurveTo(-0.340961, 23.407236, -6.647311, 14.456289, -14.377674, 8.556801);
ctx.bezierCurveTo(-21.090885, 5.505341, -27.397234, 6.319064, -26.380081, 11.811691);
ctx.fill();

ctx.fillStyle = "#ffffff";
ctx.beginPath();
ctx.moveTo(2.710498, -12.599984);
ctx.bezierCurveTo(-5.630157, -6.700496, 0.472761, 4.488188, 7.796264, 7.336217);
ctx.bezierCurveTo(1.489914, 3.267604, -1.358114, -4.259329, 2.710498, -12.599984);
ctx.fill();

</script>

</body>
</html>


于是成品就是这样的:


还不错吧,挺像的。


不否认的是,如果做专业的曲线拟合(我不专业),可能有成千上万条曲线,大概会卡。
但是如果仅仅是手工取点,我相信这点规模的绘制应该完全不会卡。


测试环境:
研华工控机+奔腾G850+4G内存+Windows7专业版+360极速浏览器


以上来自值班中的百无聊赖。


工具链接:http://gxsoftware.gitee.io/bezier/

huanwuying 发表于 2020-2-18 11:34

嗯,很好,就是看得有点晕,复制下来慢慢研究下(借用)

南郊居士 发表于 2020-2-17 23:19

a361690548 发表于 2020-2-17 23:13
刚学完js的小小小白白白 完全看不懂但还是得说楼主牛逼

我还差得远,工控出身,写的都是C味儿的js,想进步不要学我啊。

Lin5520 发表于 2020-2-17 23:05

图形学,还好没挂科,吐了,

a361690548 发表于 2020-2-17 23:13

刚学完js的小小小白白白 完全看不懂但还是得说楼主牛逼

南郊居士 发表于 2020-2-17 23:15

Lin5520 发表于 2020-2-17 23:05
图形学,还好没挂科,吐了,

还好我不是计算机专业的:Dweeqw

akronblim 发表于 2020-2-17 23:56

之前学数据结构的时候用cpp+SDL做了一了类似的东西,任意点数的贝塞尔曲线,不过随着点数变多,计算量变得过大,拖动变得很缓慢

igor 发表于 2020-2-18 00:08

强行看懂{:1_885:}

VanYun 发表于 2020-2-18 00:34

数学不及格

包装物 发表于 2020-2-18 09:04

可以学着去哄女孩子啦,感谢{:1_893:}
页: [1] 2
查看完整版本: 【HTML+js+纯前端】三次方贝塞尔曲线手工拟合小工具