深入理解Canvas二维坐标系统,掌握坐标计算方法,包含多个交互式案例
Canvas使用二维坐标系统来定位画布上的每一个点。掌握坐标系统是绑制任何图形的基础。
什么是二维坐标系统
二维坐标系统用两个数值(x, y)来确定平面上的位置:
Canvas坐标的特点
与数学坐标的区别
| 特性 | 数学坐标 | Canvas坐标 |
|---|
| 原点位置 | 左下角 | 左上角 |
| Y轴方向 | 向上递增 | 向下递增 |
| 角度方向 | 逆时针 | 顺时针 |
案例一:坐标距离计算
两点距离计算(点击两点)
代码实现
<canvas id="myCanvas" width="400" height="250"></canvas>
<script>
const canvas = document.getElementById('myCanvas')
const ctx = canvas.getContext('2d')
const points = []
function distance(x1, y1, x2, y2) {
const dx = x2 - x1
const dy = y2 - y1
return Math.sqrt(dx * dx + dy * dy)
}
canvas.addEventListener('click', function(e) {
const rect = canvas.getBoundingClientRect()
const x = Math.round(e.clientX - rect.left)
const y = Math.round(e.clientY - rect.top)
if (points.length >= 2) {
points.length = 0
}
points.push({ x, y })
ctx.clearRect(0, 0, canvas.width, canvas.height)
points.forEach((point, index) => {
ctx.fillStyle = '#3498db'
ctx.beginPath()
ctx.arc(point.x, point.y, 8, 0, Math.PI * 2)
ctx.fill()
})
if (points.length === 2) {
ctx.strokeStyle = '#e74c3c'
ctx.lineWidth = 2
ctx.beginPath()
ctx.moveTo(points[0].x, points[0].y)
ctx.lineTo(points[1].x, points[1].y)
ctx.stroke()
const dist = distance(points[0].x, points[0].y, points[1].x, points[1].y)
console.log(`距离: ${dist.toFixed(1)}px`)
}
})
</script>
代码解析
| 方法 | 说明 |
|---|
Math.sqrt() | 计算平方根 |
Math.pow(x, 2) 或 x * x | 计算平方 |
| 勾股定理 | 距离 = √(dx² + dy²) |
案例二:坐标旋转
点绕原点旋转
代码实现
<canvas id="myCanvas" width="400" height="250"></canvas>
<script>
const canvas = document.getElementById('myCanvas')
const ctx = canvas.getContext('2d')
const centerX = canvas.width / 2
const centerY = canvas.height / 2
const radius = 80
let angle = 0
function rotatePoint(x, y, angle) {
const cos = Math.cos(angle)
const sin = Math.sin(angle)
return {
x: x * cos - y * sin,
y: x * sin + y * cos
}
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height)
ctx.strokeStyle = '#ecf0f1'
ctx.lineWidth = 1
ctx.beginPath()
ctx.moveTo(0, centerY)
ctx.lineTo(canvas.width, centerY)
ctx.moveTo(centerX, 0)
ctx.lineTo(centerX, canvas.height)
ctx.stroke()
ctx.strokeStyle = '#ddd'
ctx.beginPath()
ctx.arc(centerX, centerY, radius, 0, Math.PI * 2)
ctx.stroke()
const x = centerX + radius * Math.cos(angle)
const y = centerY + radius * Math.sin(angle)
ctx.strokeStyle = '#3498db'
ctx.lineWidth = 2
ctx.beginPath()
ctx.moveTo(centerX, centerY)
ctx.lineTo(x, y)
ctx.stroke()
ctx.fillStyle = '#3498db'
ctx.beginPath()
ctx.arc(x, y, 10, 0, Math.PI * 2)
ctx.fill()
angle += 0.02
requestAnimationFrame(draw)
}
draw()
</script>
代码解析
| 方法 | 说明 |
|---|
Math.cos(angle) | 余弦函数,计算x分量 |
Math.sin(angle) | 正弦函数,计算y分量 |
角度 × π / 180 | 角度转弧度 |
弧度 × 180 / π | 弧度转角度 |
案例三:坐标边界检测
点是否在区域内(移动鼠标测试)
代码实现
<canvas id="myCanvas" width="400" height="250"></canvas>
<script>
const canvas = document.getElementById('myCanvas')
const ctx = canvas.getContext('2d')
const rect = { x: 50, y: 50, w: 120, h: 80 }
const circle = { x: 280, y: 90, r: 50 }
function isPointInRect(px, py, rx, ry, rw, rh) {
return px >= rx && px <= rx + rw && py >= ry && py <= ry + rh
}
function isPointInCircle(px, py, cx, cy, r) {
const dx = px - cx
const dy = py - cy
return dx * dx + dy * dy <= r * r
}
canvas.addEventListener('mousemove', function(e) {
const rectBound = canvas.getBoundingClientRect()
const x = e.clientX - rectBound.left
const y = e.clientY - rectBound.top
ctx.clearRect(0, 0, canvas.width, canvas.height)
const inRect = isPointInRect(x, y, rect.x, rect.y, rect.w, rect.h)
ctx.fillStyle = inRect ? 'rgba(52, 152, 219, 0.5)' : 'rgba(52, 152, 219, 0.2)'
ctx.fillRect(rect.x, rect.y, rect.w, rect.h)
const inCircle = isPointInCircle(x, y, circle.x, circle.y, circle.r)
ctx.fillStyle = inCircle ? 'rgba(231, 76, 60, 0.5)' : 'rgba(231, 76, 60, 0.2)'
ctx.beginPath()
ctx.arc(circle.x, circle.y, circle.r, 0, Math.PI * 2)
ctx.fill()
ctx.fillStyle = '#2ecc71'
ctx.beginPath()
ctx.arc(x, y, 6, 0, Math.PI * 2)
ctx.fill()
})
</script>
代码解析
| 检测方法 | 公式 |
|---|
| 矩形检测 | x >= rx && x <= rx+w && y >= ry && y <= ry+h |
| 圆形检测 | (x-cx)² + (y-cy)² <= r² |
坐标计算工具函数
const CoordinateUtils = {
distance(x1, y1, x2, y2) {
const dx = x2 - x1
const dy = y2 - y1
return Math.sqrt(dx * dx + dy * dy)
},
midpoint(x1, y1, x2, y2) {
return {
x: (x1 + x2) / 2,
y: (y1 + y2) / 2
}
},
angle(x, y) {
return Math.atan2(y, x)
},
toRadians(degrees) {
return degrees * Math.PI / 180
},
toDegrees(radians) {
return radians * 180 / Math.PI
},
rotatePoint(x, y, angle) {
const cos = Math.cos(angle)
const sin = Math.sin(angle)
return {
x: x * cos - y * sin,
y: x * sin + y * cos
}
},
isPointInRect(px, py, rx, ry, rw, rh) {
return px >= rx && px <= rx + rw && py >= ry && py <= ry + rh
},
isPointInCircle(px, py, cx, cy, r) {
const dx = px - cx
const dy = py - cy
return dx * dx + dy * dy <= r * r
}
}
小结
二维坐标系统是Canvas绑制的基础:
- 理解(x, y)的含义
- 掌握坐标计算方法
- 学会坐标转换和旋转
- 能够进行边界检测