惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

J
Java Code Geeks
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
V
V2EX
小众软件
小众软件
WordPress大学
WordPress大学
Apple Machine Learning Research
Apple Machine Learning Research
Recent Announcements
Recent Announcements
有赞技术团队
有赞技术团队
MongoDB | Blog
MongoDB | Blog
C
Check Point Blog
S
Schneier on Security
C
Cybersecurity and Infrastructure Security Agency CISA
The Cloudflare Blog
V
Vulnerabilities – Threatpost
The Hacker News
The Hacker News
T
Threatpost
T
Tenable Blog
aimingoo的专栏
aimingoo的专栏
IT之家
IT之家
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
C
CERT Recently Published Vulnerability Notes
U
Unit 42
Spread Privacy
Spread Privacy
博客园 - 司徒正美
Hacker News: Ask HN
Hacker News: Ask HN
C
CXSECURITY Database RSS Feed - CXSecurity.com
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
阮一峰的网络日志
阮一峰的网络日志
SecWiki News
SecWiki News
云风的 BLOG
云风的 BLOG
The Register - Security
The Register - Security
AWS News Blog
AWS News Blog
月光博客
月光博客
Security Latest
Security Latest
H
Heimdal Security Blog
S
Secure Thoughts
博客园 - 聂微东
PCI Perspectives
PCI Perspectives
博客园 - 叶小钗
Scott Helme
Scott Helme
O
OpenAI News
Google DeepMind News
Google DeepMind News
Google DeepMind News
Google DeepMind News
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
S
Security @ Cisco Blogs
NISL@THU
NISL@THU
S
Securelist
Latest news
Latest news
P
Proofpoint News Feed
博客园 - 【当耐特】

轻笑Chuckle

NestJS实践杂记 React-记-3 React-记-2 React-记-1 闭包与内存泄漏 Vue3.5 更新了啥 Git lint 相关 集成 lint 代码规范工具 Monorepo 单仓库多应用 Vue+TS 实现虚拟列表 开发了一个 Canvas 2D 渲染引擎 Canvas 2D 事件 Canvas 2D 进阶 Canvas 2D 基础 Git 规范与实践 Git 熟知熟用 解决System占CPU过高 浏览器HTTP缓存 浏览器渲染流程
Canvas 2D 贝塞尔曲线
『轻笑Chuckle』 · 2024-05-22 · via 轻笑Chuckle

每个运动点在各自线段上都可视为一阶贝塞尔曲线:
Pa = (1 - t)P0 + tP1
Pb = (1 - t)P1 + tP2
Pt = (1 - t)Pa + tPb

这里递归将 n 阶贝塞尔曲线降为 1 阶,每次递归都调用 calcMotionPoint() 计算当前运动点(下一阶的控制点)位置,最后 1 阶的运动点就是曲线上的点,将其加入曲线点集合数组,并调用 drawCurve() 增量绘制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
class Bezier {
constructor(ctx) {
this.ctx = ctx;
}


draw(controlPoints, t = 1) {
if (!controlPoints.length) return;
const curvePoints = [];
let i = 0;
while (i <= t) {
this.drawLines(controlPoints, i, curvePoints, false, null, "blue");
i += 0.01;
}

this.drawLines(controlPoints, t, curvePoints, false, null, "blue");
return curvePoints;
}


drawAnimation(controlPoints, t = 1) {
if (!controlPoints.length) return Promise.resolve([]);
return new Promise((resolve) => {
const curvePoints = [];
let i = 0;
const p2d = new Path2D();
const draw = () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
if (i > t) {

this.drawLines(controlPoints, t, curvePoints, true, p2d, "blue");
resolve(curvePoints);
return;
}
ctx.fillText(`t = (${i.toFixed(2)})`, 10, 20);
this.drawLines(controlPoints, i, curvePoints, true, p2d, "blue");
i += 0.01;
requestAnimationFrame(draw);
};
draw();
});
}


drawLines(points, t, curvePoints, isAnimation, p2d, color) {

if (points.length < 2) {
curvePoints.push({ ...points[0] });
this.drawCurve(curvePoints, isAnimation, p2d);
return;
}
if (isAnimation) {

this.ctx.save();
for (let i = 0; i < points.length; i++) {

this.ctx.beginPath();
this.ctx.strokeStyle = color ?? "red";
this.ctx.lineWidth = 2;
i > 0 && this.ctx.moveTo(points[i - 1].x, points[i - 1].y);
this.ctx.lineTo(points[i].x, points[i].y);
this.ctx.stroke();

this.ctx.beginPath();
this.ctx.strokeStyle = "black";
this.ctx.lineWidth = 1;
this.ctx.arc(points[i].x, points[i].y, 5, 0, 2 * Math.PI);
this.ctx.stroke();
}
this.ctx.restore();
}

const newPoints = [];

for (let i = 0; i < points.length - 1; i++) {
newPoints.push(this.calcMotionPoint(points[i], points[i + 1], t));
}

this.drawLines(newPoints, t, curvePoints, isAnimation, p2d, null);
}


drawCurve(curvePoints, isAnimation, p2d) {

const len = curvePoints.length;
if (isAnimation) {
const x = curvePoints[len - 1].x;
const y = curvePoints[len - 1].y;

p2d.lineTo(x, y);
this.ctx.stroke(p2d);

this.ctx.save();
this.ctx.beginPath();
this.ctx.strokeStyle = "blue";
this.ctx.lineWidth = 1;
this.ctx.arc(x, y, 5, 0, 2 * Math.PI);
this.ctx.stroke();
this.ctx.restore();
} else {

if (len < 3) return;
this.ctx.beginPath();
this.ctx.moveTo(curvePoints[len - 3].x, curvePoints[len - 3].y);
this.ctx.lineTo(curvePoints[len - 2].x, curvePoints[len - 2].y);
this.ctx.lineTo(curvePoints[len - 1].x, curvePoints[len - 1].y);
this.ctx.stroke();
}
}


calcMotionPoint(p1, p2, t) {

return {
x: (1 - t) * p1.x + t * p2.x,
y: (1 - t) * p1.y + t * p2.y,
};
}
}