渲染与图形
requestAnimationFrame 是什么,有什么作用?
答案
方法 | 主要用途 | 执行时机 | 优势 | 典型场景 |
---|---|---|---|---|
requestAnimationFrame | 浏览器动画帧调度 | 重绘前 | 与刷新率同步,节能流畅 | JS动画、进度条等 |
补充说明
- requestAnimationFrame 会在浏览器每次重绘前自动调用回调,通常约每秒60次,能保证动画与屏幕刷新同步,减少卡顿和掉帧。
- 返回的ID可用于 cancelAnimationFrame 取消动画,适合实现高性能动画循环。
- 与 setTimeout/setInterval 相比,requestAnimationFrame 更节能且动画更平滑,页面切换到后台时会自动暂停,节省资源。
let id
function loop () {
// 动画逻辑
id = requestAnimationFrame(loop)
}
loop()
// 取消动画
cancelAnimationFrame(id)
提示
推荐所有涉及 DOM 动画的场景优先使用 requestAnimationFrame,提升性能和流畅度。
延伸阅读
canvas 是如何处理复杂事件交互的?
答案
核心概念
Canvas 本身不具备 DOM 事件绑定能力,复杂事件交互需通过监听容器事件并结合图形坐标判定实现。常见做法是监听鼠标或触摸事件,计算事件点在 canvas 内的坐标,再判断是否命中特定图形,实现点击、悬停、拖拽等交互。
详细实现步骤
- 监听事件:在 canvas 或其父容器上监听
mousemove
、mousedown
、mouseup
、touchstart
等事件。 - 坐标转换:通过
getBoundingClientRect
获取 canvas 的位置,将事件的clientX/Y
转换为 canvas 内部坐标。 - 命中检测:遍历所有图形对象,判断事件点是否落在某个图形区域(如圆形、矩形等)。
- 状态管理:可维护图形对象数组,记录每个图形的状态(如选中、悬停),实现动态交互效果。
代码示例
const canvas = document.getElementById('myCanvas')
const ctx = canvas.getContext('2d')
const shapes = [
{ type: 'circle', x: 100, y: 100, radius: 50 },
{ type: 'rectangle', x: 200, y: 200, width: 100, height: 50 }
]
canvas.addEventListener('mousemove', function (event) {
const rect = canvas.getBoundingClientRect()
const mouseX = event.clientX - rect.left
const mouseY = event.clientY - rect.top
shapes.forEach(shape => {
if (shape.type === 'circle' && isPointInCircle(mouseX, mouseY, shape)) {
ctx.fillStyle = 'red'
} else {
ctx.fillStyle = 'blue'
}
drawShape(shape)
})
})
function isPointInCircle (x, y, { x: cx, y: cy, radius }) {
const dx = x - cx
const dy = y - cy
return dx * dx + dy * dy <= radius * radius
}
function drawShape (shape) {
if (shape.type === 'circle') {
ctx.beginPath()
ctx.arc(shape.x, shape.y, shape.radius, 0, Math.PI * 2)
ctx.fill()
} else if (shape.type === 'rectangle') {
ctx.fillRect(shape.x, shape.y, shape.width, shape.height)
}
}
开发建议
- 复杂交互建议封装图形对象和事件处理逻辑,或使用如 Fabric.js、Konva.js 等第三方库简化开发。
- 需注意每次交互后需清空并重绘 canvas,避免残影。
延伸阅读