【html系列】如何使用 HTML 和 Canvas 绘制李萨如图并实现 3D 旋转效果

引言

李萨如图是一种由两个互相垂直的正弦波合成形成的图形。通过调整这两个正弦波的频率,可以得到各种复杂的图案。本文将介绍如何使用 HTML 和 Canvas 来绘制李萨如图,并添加滑块来控制两个方向上的频率变化,同时让图形产生 3D 旋转的效果。

步骤一:创建基本的 HTML 结构

首先,我们需要创建一个基本的 HTML 文件结构,并引入必要的 CSS 样式和 JavaScript 脚本。

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Lissajous Figure</title>
<style>
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #f0f0f0;
}
canvas {
border: 1px solid black;
}
.controls {
position: absolute;
top: 10px;
left: 10px;
}
input[type="range"] {
width: 200px;
margin-bottom: 10px;
}
</style>
</head>
<body>
<div class="controls">
<label for="frequencyX">Frequency X:</label>
<input type="range" id="frequencyX" min="1" max="20" value="5">
<span id="valueX">5</span><br>

<label for="frequencyY">Frequency Y:</label>
<input type="range" id="frequencyY" min="1" max="20" value="4">
<span id="valueY">4</span>
</div>
<canvas id="lissajousCanvas" width="500" height="500"></canvas>

<script>
// JavaScript code will go here
</script>
</body>
</html>

步骤二:编写 JavaScript 代码

接下来,我们将在 <script> 标签内编写 JavaScript 代码来绘制李萨如图,并实现动态效果。

初始化变量

首先,获取 Canvas 元素及其上下文,以及滑块元素和显示频率值的 span 元素。

1
2
3
4
5
6
7
8
9
10
const canvas = document.getElementById('lissajousCanvas');
const ctx = canvas.getContext('2d');
const frequencyXInput = document.getElementById('frequencyX');
const frequencyYInput = document.getElementById('frequencyY');
const valueXSpan = document.getElementById('valueX');
const valueYSpan = document.getElementById('valueY');

let frequencyX = parseInt(frequencyXInput.value);
let frequencyY = parseInt(frequencyYInput.value);
let delta = 0;

绘制李萨如图

定义 drawLissajous 函数来绘制李萨如图。我们将使用 requestAnimationFrame 来不断更新画面,从而实现动画效果。

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
function drawLissajous() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.strokeStyle = 'blue';
ctx.lineWidth = 2;

const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
const radius = 200;

for (let t = 0; t <= Math.PI * 4; t += 0.01) {
const x = centerX + radius * Math.sin(frequencyX * t + delta);
const y = centerY + radius * Math.sin(frequencyY * t);

if (t === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
}

ctx.stroke();

delta += 0.01;
requestAnimationFrame(drawLissajous);
}

添加事件监听器

为滑块添加事件监听器,以便在用户调整滑块时更新频率值。

1
2
3
4
5
6
7
8
9
frequencyXInput.addEventListener('input', () => {
frequencyX = parseInt(frequencyXInput.value);
valueXSpan.textContent = frequencyX;
});

frequencyYInput.addEventListener('input', () => {
frequencyY = parseInt(frequencyYInput.value);
valueYSpan.textContent = frequencyY;
});

启动绘图函数

最后,调用 drawLissajous 函数来启动整个绘制过程。

1
drawLissajous();

完整代码

以下是完整的 HTML 文件代码:

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Lissajous Figure</title>
<style>
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #f0f0f0;
}
canvas {
border: 1px solid black;
}
.controls {
position: absolute;
top: 10px;
left: 10px;
}
input[type="range"] {
width: 200px;
margin-bottom: 10px;
}
</style>
</head>
<body>
<div class="controls">
<label for="frequencyX">Frequency X:</label>
<input type="range" id="frequencyX" min="1" max="20" value="5">
<span id="valueX">5</span><br>

<label for="frequencyY">Frequency Y:</label>
<input type="range" id="frequencyY" min="1" max="20" value="4">
<span id="valueY">4</span>
</div>
<canvas id="lissajousCanvas" width="500" height="500"></canvas>

<script>
const canvas = document.getElementById('lissajousCanvas');
const ctx = canvas.getContext('2d');
const frequencyXInput = document.getElementById('frequencyX');
const frequencyYInput = document.getElementById('frequencyY');
const valueXSpan = document.getElementById('valueX');
const valueYSpan = document.getElementById('valueY');

let frequencyX = parseInt(frequencyXInput.value);
let frequencyY = parseInt(frequencyYInput.value);
let delta = 0;

function drawLissajous() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.strokeStyle = 'blue';
ctx.lineWidth = 2;

const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
const radius = 200;

for (let t = 0; t <= Math.PI * 4; t += 0.01) {
const x = centerX + radius * Math.sin(frequencyX * t + delta);
const y = centerY + radius * Math.sin(frequencyY * t);

if (t === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
}

ctx.stroke();

delta += 0.01;
requestAnimationFrame(drawLissajous);
}

frequencyXInput.addEventListener('input', () => {
frequencyX = parseInt(frequencyXInput.value);
valueXSpan.textContent = frequencyX;
});

frequencyYInput.addEventListener('input', () => {
frequencyY = parseInt(frequencyYInput.value);
valueYSpan.textContent = frequencyY;
});

drawLissajous();
</script>
</body>
</html>

总结

通过以上步骤,我们成功地使用 HTML 和 Canvas 实现了一个带有滑块控制的李萨如图,并实现了 3D 旋转效果。你可以根据自己的需求进一步调整样式和功能。希望这篇教程对你有所帮助!


这样就完成了一篇详细的博客文章教程,帮助读者了解如何使用 HTML 和 Canvas 绘制李萨如图并实现动态效果。