学习Canvas中获取鼠标坐标的方法,掌握坐标转换和变换后的坐标计算。正确获取鼠标在Canvas中的坐标是实现交互的基础,需要考虑元素位置、滚动、缩放等因素。
canvas.addEventListener('click', (e) => {
console.log('鼠标位置:', e.offsetX, e.offsetY)
})
canvas.addEventListener('click', (e) => {
const rect = canvas.getBoundingClientRect()
const x = e.clientX - rect.left
const y = e.clientY - rect.top
console.log('鼠标位置:', x, y)
})
canvas.addEventListener('click', (e) => {
const rect = canvas.getBoundingClientRect()
const x = e.pageX - rect.left - window.scrollX
const y = e.pageY - rect.top - window.scrollY
console.log('鼠标位置:', x, y)
})
| 属性 | 说明 | 参考点 |
|---|---|---|
| offsetX/offsetY | 相对于目标元素 | 目标元素左上角 |
| clientX/clientY | 相对于视口 | 浏览器视口左上角 |
| pageX/pageY | 相对于文档 | 文档左上角 |
| screenX/screenY | 相对于屏幕 | 屏幕左上角 |
function getMousePos(canvas, e) {
const rect = canvas.getBoundingClientRect()
return {
x: e.clientX - rect.left,
y: e.clientY - rect.top
}
}
canvas.addEventListener('click', (e) => {
const pos = getMousePos(canvas, e)
console.log('点击位置:', pos.x, pos.y)
})
function getMousePosWithBorder(canvas, e) {
const rect = canvas.getBoundingClientRect()
const style = getComputedStyle(canvas)
const borderLeft = parseInt(style.borderLeftWidth) || 0
const borderTop = parseInt(style.borderTopWidth) || 0
return {
x: e.clientX - rect.left - borderLeft,
y: e.clientY - rect.top - borderTop
}
}
function getMousePosWithPadding(canvas, e) {
const rect = canvas.getBoundingClientRect()
const style = getComputedStyle(canvas)
const paddingLeft = parseInt(style.paddingLeft) || 0
const paddingTop = parseInt(style.paddingTop) || 0
return {
x: e.clientX - rect.left - paddingLeft,
y: e.clientY - rect.top - paddingTop
}
}
function getMousePosWithScale(canvas, e) {
const rect = canvas.getBoundingClientRect()
const scaleX = canvas.width / rect.width
const scaleY = canvas.height / rect.height
return {
x: (e.clientX - rect.left) * scaleX,
y: (e.clientY - rect.top) * scaleY
}
}
function getMousePosWithTransform(canvas, e) {
const rect = canvas.getBoundingClientRect()
const style = getComputedStyle(canvas)
const transform = style.transform
let scaleX = 1
let scaleY = 1
let offsetX = 0
let offsetY = 0
if (transform && transform !== 'none') {
const matrix = transform.match(/matrix\(([^)]+)\)/)
if (matrix) {
const values = matrix[1].split(',').map(v => parseFloat(v.trim()))
scaleX = values[0]
scaleY = values[3]
offsetX = values[4]
offsetY = values[5]
}
}
return {
x: (e.clientX - rect.left - offsetX) / scaleX,
y: (e.clientY - rect.top - offsetY) / scaleY
}
}
let translateX = 100
let translateY = 50
ctx.translate(translateX, translateY)
canvas.addEventListener('click', (e) => {
const rect = canvas.getBoundingClientRect()
const canvasX = e.clientX - rect.left
const canvasY = e.clientY - rect.top
const worldX = canvasX - translateX
const worldY = canvasY - translateY
console.log('世界坐标:', worldX, worldY)
})
let scale = 2
ctx.scale(scale, scale)
canvas.addEventListener('click', (e) => {
const rect = canvas.getBoundingClientRect()
const canvasX = e.clientX - rect.left
const canvasY = e.clientY - rect.top
const worldX = canvasX / scale
const worldY = canvasY / scale
console.log('世界坐标:', worldX, worldY)
})
let rotation = Math.PI / 4
let centerX = 200
let centerY = 150
ctx.translate(centerX, centerY)
ctx.rotate(rotation)
ctx.translate(-centerX, -centerY)
canvas.addEventListener('click', (e) => {
const rect = canvas.getBoundingClientRect()
const canvasX = e.clientX - rect.left
const canvasY = e.clientY - rect.top
const dx = canvasX - centerX
const dy = canvasY - centerY
const worldX = centerX + dx * Math.cos(-rotation) - dy * Math.sin(-rotation)
const worldY = centerY + dx * Math.sin(-rotation) + dy * Math.cos(-rotation)
console.log('世界坐标:', worldX, worldY)
})
function getTransformedPoint(canvas, e) {
const rect = canvas.getBoundingClientRect()
const canvasX = e.clientX - rect.left
const canvasY = e.clientY - rect.top
const matrix = ctx.getTransform()
const inverse = matrix.inverse()
if (inverse) {
const point = new DOMPoint(canvasX, canvasY)
const transformed = point.matrixTransform(inverse)
return { x: transformed.x, y: transformed.y }
}
return { x: canvasX, y: canvasY }
}
function invertMatrix(a, b, c, d, e, f) {
const det = a * d - b * c
if (det === 0) return null
return {
a: d / det,
b: -b / det,
c: -c / det,
d: a / det,
e: (b * f - d * e) / det,
f: (c * e - a * f) / det
}
}
function applyInverseTransform(x, y, matrix) {
if (!matrix) return { x, y }
return {
x: matrix.a * x + matrix.c * y + matrix.e,
y: matrix.b * x + matrix.d * y + matrix.f
}
}
class Camera {
constructor() {
this.x = 0
this.y = 0
this.zoom = 1
}
screenToWorld(screenX, screenY) {
return {
x: (screenX - this.x) / this.zoom,
y: (screenY - this.y) / this.zoom
}
}
worldToScreen(worldX, worldY) {
return {
x: worldX * this.zoom + this.x,
y: worldY * this.zoom + this.y
}
}
apply(ctx) {
ctx.setTransform(this.zoom, 0, 0, this.zoom, this.x, this.y)
}
}
const camera = new Camera()
canvas.addEventListener('click', (e) => {
const rect = canvas.getBoundingClientRect()
const screenX = e.clientX - rect.left
const screenY = e.clientY - rect.top
const worldPos = camera.screenToWorld(screenX, screenY)
console.log('世界坐标:', worldPos)
})
class CoordinateSystem {
constructor(canvas) {
this.canvas = canvas
this.transforms = []
}
pushTransform(transform) {
this.transforms.push(transform)
}
popTransform() {
this.transforms.pop()
}
screenToCanvas(e) {
const rect = this.canvas.getBoundingClientRect()
return {
x: e.clientX - rect.left,
y: e.clientY - rect.top
}
}
canvasToWorld(canvasX, canvasY) {
let x = canvasX
let y = canvasY
for (let i = this.transforms.length - 1; i >= 0; i--) {
const t = this.transforms[i]
if (t.type === 'translate') {
x -= t.x
y -= t.y
} else if (t.type === 'scale') {
x /= t.x
y /= t.y
} else if (t.type === 'rotate') {
const cos = Math.cos(-t.angle)
const sin = Math.sin(-t.angle)
const nx = x * cos - y * sin
const ny = x * sin + y * cos
x = nx
y = ny
}
}
return { x, y }
}
screenToWorld(e) {
const canvas = this.screenToCanvas(e)
return this.canvasToWorld(canvas.x, canvas.y)
}
}