二维坐标系统

深入理解Canvas二维坐标系统,掌握坐标计算方法,包含多个交互式案例 Canvas使用二维坐标系统来定位画布上的每一个点。掌握坐标系统是绑制任何图形的基础。

什么是二维坐标系统

二维坐标系统用两个数值(x, y)来确定平面上的位置:

  • 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)的含义
  • 掌握坐标计算方法
  • 学会坐标转换和旋转
  • 能够进行边界检测