Canvas绑制三角形详解,包括等边三角形、等腰三角形、直角三角形的绑制方法,包含多个交互式案例。
三角形是最简单的多边形,通过moveTo和lineTo组合可以绑制各种三角形。
三角形类型
| 类型 | 特点 | 绘制方式 |
|---|
| 等边三角形 | 三边相等 | 计算三个顶点 |
| 等腰三角形 | 两边相等 | 简化计算 |
| 直角三角形 | 一个直角 | 固定角度 |
| 任意三角形 | 无特殊要求 | 手动指定顶点 |
案例一:基本三角形
不同类型的三角形
代码实现
<canvas id="myCanvas" width="400" height="200"></canvas>
<script>
const canvas = document.getElementById('myCanvas')
const ctx = canvas.getContext('2d')
ctx.beginPath()
ctx.moveTo(60, 30)
ctx.lineTo(30, 90)
ctx.lineTo(90, 90)
ctx.closePath()
ctx.fillStyle = '#e74c3c'
ctx.fill()
ctx.beginPath()
ctx.moveTo(150, 30)
ctx.lineTo(120, 90)
ctx.lineTo(180, 90)
ctx.closePath()
ctx.strokeStyle = '#3498db'
ctx.lineWidth = 3
ctx.stroke()
ctx.beginPath()
ctx.moveTo(220, 30)
ctx.lineTo(220, 90)
ctx.lineTo(280, 90)
ctx.closePath()
ctx.fillStyle = '#2ecc71'
ctx.fill()
</script>
代码解析
| 步骤 | 说明 |
|---|
| moveTo | 设置起点 |
| lineTo | 画线到其他顶点 |
| closePath | 闭合路径 |
| fill/stroke | 填充或描边 |
案例二:等边三角形函数
使用函数绑制等边三角形
代码实现
<canvas id="myCanvas" width="400" height="200"></canvas>
<script>
const canvas = document.getElementById('myCanvas')
const ctx = canvas.getContext('2d')
function drawEquilateralTriangle(cx, cy, size, rotation, color, fill) {
ctx.beginPath()
for (let i = 0; i < 3; i++) {
const angle = rotation + i * 2 * Math.PI / 3 - Math.PI / 2
const x = cx + size * Math.cos(angle)
const y = cy + size * Math.sin(angle)
if (i === 0) ctx.moveTo(x, y)
else ctx.lineTo(x, y)
}
ctx.closePath()
if (fill) {
ctx.fillStyle = color
ctx.fill()
} else {
ctx.strokeStyle = color
ctx.lineWidth = 2
ctx.stroke()
}
}
drawEquilateralTriangle(100, 100, 40, 0, '#e74c3c', true)
drawEquilateralTriangle(200, 100, 40, Math.PI / 6, '#3498db', true)
drawEquilateralTriangle(300, 100, 40, Math.PI / 3, '#2ecc71', true)
</script>
代码解析
| 技术 | 说明 |
|---|
| 中心点 | 三角形中心坐标 |
| 角度计算 | i * 2π / 3 均分三个顶点 |
| 旋转 | 添加rotation参数 |
| -π/2 | 从顶部开始绘制 |
案例三:三角形图案
三角形组成的图案
代码实现
<canvas id="myCanvas" width="400" height="200"></canvas>
<script>
const canvas = document.getElementById('myCanvas')
const ctx = canvas.getContext('2d')
function drawTriangle(x1, y1, x2, y2, x3, y3, color) {
ctx.beginPath()
ctx.moveTo(x1, y1)
ctx.lineTo(x2, y2)
ctx.lineTo(x3, y3)
ctx.closePath()
ctx.fillStyle = color
ctx.fill()
}
const colors = ['#e74c3c', '#3498db', '#2ecc71', '#f39c12', '#9b59b6']
const size = 35
const h = size * Math.sqrt(3) / 2
for (let row = 0; row < 4; row++) {
for (let col = 0; col < 6; col++) {
const x = 50 + col * size
const y = 30 + row * h
const colorIndex = (row + col) % colors.length
if ((row + col) % 2 === 0) {
drawTriangle(x, y, x + size, y, x + size / 2, y + h, colors[colorIndex])
} else {
drawTriangle(x + size / 2, y, x + size, y + h, x, y + h, colors[colorIndex])
}
}
}
</script>
代码解析
| 技术 | 说明 |
|---|
| 高度计算 | h = size * √3 / 2 |
| 交替方向 | 奇偶判断决定方向 |
| 颜色循环 | (row + col) % colors.length |
案例四:旋转三角形动画
旋转三角形动画
代码实现
<canvas id="myCanvas" width="400" height="200"></canvas>
<script>
const canvas = document.getElementById('myCanvas')
const ctx = canvas.getContext('2d')
let rotation = 0
const triangles = [
{ x: 80, y: 100, size: 40, color: '#e74c3c', speed: 1 },
{ x: 160, y: 100, size: 35, color: '#3498db', speed: -1.5 },
{ x: 240, y: 100, size: 45, color: '#2ecc71', speed: 2 },
{ x: 320, y: 100, size: 30, color: '#f39c12', speed: -1 }
]
function drawTriangle(t, rot) {
ctx.beginPath()
for (let i = 0; i < 3; i++) {
const angle = rot + i * 2 * Math.PI / 3 - Math.PI / 2
const x = t.x + t.size * Math.cos(angle)
const y = t.y + t.size * Math.sin(angle)
if (i === 0) ctx.moveTo(x, y)
else ctx.lineTo(x, y)
}
ctx.closePath()
ctx.fillStyle = t.color
ctx.fill()
}
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height)
triangles.forEach(t => {
drawTriangle(t, rotation * t.speed)
})
rotation += 0.02
requestAnimationFrame(animate)
}
animate()
</script>
代码解析
| 技术 | 说明 |
|---|
| 不同速度 | speed属性控制旋转速度 |
| 正负速度 | 正数顺时针,负数逆时针 |
| 动画循环 | requestAnimationFrame |
案例五:谢尔宾斯基三角形
谢尔宾斯基三角形(分形)
代码实现
<canvas id="myCanvas" width="400" height="200"></canvas>
<script>
const canvas = document.getElementById('myCanvas')
const ctx = canvas.getContext('2d')
function sierpinski(x1, y1, x2, y2, x3, y3, depth) {
if (depth === 0) {
ctx.beginPath()
ctx.moveTo(x1, y1)
ctx.lineTo(x2, y2)
ctx.lineTo(x3, y3)
ctx.closePath()
ctx.fillStyle = '#3498db'
ctx.fill()
return
}
const mx1 = (x1 + x2) / 2
const my1 = (y1 + y2) / 2
const mx2 = (x2 + x3) / 2
const my2 = (y2 + y3) / 2
const mx3 = (x1 + x3) / 2
const my3 = (y1 + y3) / 2
sierpinski(x1, y1, mx1, my1, mx3, my3, depth - 1)
sierpinski(mx1, my1, x2, y2, mx2, my2, depth - 1)
sierpinski(mx3, my3, mx2, my2, x3, y3, depth - 1)
}
const size = 180
const h = size * Math.sqrt(3) / 2
const cx = 200
const cy = 150
sierpinski(cx, cy - h * 2/3, cx - size/2, cy + h/3, cx + size/2, cy + h/3, 4)
</script>
代码解析
| 技术 | 说明 |
|---|
| 递归 | 分形的核心思想 |
| 中点计算 | 三边中点形成新三角形 |
| 深度控制 | depth决定递归层数 |