一、canvas
HTML5 新增 <canvas> 元素用于图形的绘制。
绘制一条直线:
<!DOCTYPE html>
<html lang="en">
<head> </head>
<body>
<canvas width="200" height="200" style="border: 1px solid #000"></canvas>
<script>
var ctx = document.querySelector("canvas").getContext("2d")
ctx.beginPath()
ctx.moveTo(0, 0)
ctx.lineTo(100, 100)
ctx.stroke()
</script>
</body>
</html>
二、L-system
L-system 是生物学领域有关生长发展中的细胞交互作用的数学模型,尤其被广泛应用于植物生长过程的研究,也是一种形态发生(morphogenesis)算法。
下面从 Hilbert 曲线图形来讲解:
Hilbert 曲线图形规则
const Hilbert = {
// 基态 --> 最初的形态
start: "A",
// 裂变规则
rules: { A: "-BF+AFA+FB-", B: "+AF-BFB-FA+" },
}
- 第 0 次裂变 "A"
- 第 1 次裂变 "A" 按照裂变规则为 "-BF+AFA+FB-"
- 第 2 次裂变 "-BF+AFA+FB-" --> "-+AF-BFB-FA+F+-BF+AFA+FB-F-BF+AFA+FB-+F+AF-BFB-FA+-"
Hilbert 图形绘制规则
const drawRules = {
angle: 0, // 旋转角度记录,起始角度
len: 20, // 一格距离
x: 100, // 起点 X 位置
y: 100, // 起点 Y 位置
rotate: Math.PI / 2, // 旋转叠加角度
actions: { "-": "left", "+": "right", F: "forward" }, // x行动规则
}
| 符号 | 意义 |
|---|---|
| - | 左转 |
| + | 右转 |
| F | 前进一格 |
| A | 禁止 |
| B | 禁止 |
裂变获得对应的字符串,根据绘制规则 —— 设定起点(x,y)和方向(angle),从左到右扫描字符串,遇到 F 就前进一格,遇到 - 向左旋转 90°,遇到 + 向右旋转 90°,这样就得到一个 Hilbert 曲线。
裂变代码实现
/**
* 裂变
* @param graph 图形
* @param times 裂变次数
* @return
*/
function replacement(graph, times) {
let { start, rules } = graph
for (let i = 0; i < times; i++) {
let temp = ""
for (let j = 0; j < start.length; j++) {
const s = start[j]
if (Object.keys(rules).includes(s)) {
temp += rules[s]
} else {
temp += s
}
}
start = temp
}
return start
}
图形绘制代码实现
/**
* 图形绘制规则
* @param str 裂变后字符串
* @return 图形点二维数组
*/
function interpretation(str, drawRules) {
let { actions, rotate, angle, len, x, y } = drawRules,
points = [[x, y]]
for (let i = 0; i < str.length; i++) {
const s = str[i]
if (!Object.keys(actions).includes(s)) {
continue
} else if (actions[s] === "left") {
angle -= rotate
} else if (actions[s] === "right") {
angle += rotate
} else if (actions[s] === "forward") {
const last = points.at(-1),
next_x = last[0] + len * Math.cos(angle),
next_y = last[1] + len * Math.sin(angle)
points.push([next_x, next_y])
}
}
return points
}
绘制代码实现
/**
* 绘制
* @param points 图形点二维数组
* @param canvas = "canvas" 元素选择器
*/
function draw(points, canvas = "canvas") {
const ctx = document.querySelector(canvas).getContext("2d")
ctx.strokeStyle = "#2678b2"
ctx.beginPath()
for (let i = 0; i < points.length; i++) {
ctx.lineTo(points[i][0], points[i][1])
}
ctx.stroke()
}