Canvas动画实战从入门到精通前言各位前端小伙伴不知道你们有没有想过在浏览器中实现复杂的动画效果Canvas可以让你实现各种炫酷的动画我曾经开发过一个在线绘图应用使用Canvas实现了流畅的画笔效果和动画回放功能用户体验非常棒Canvas动画概述Canvas是HTML5提供的一个2D绘图API可以用于创建动态图形、游戏、数据可视化等。Canvas动画的核心是使用requestAnimationFrame进行帧同步。Canvas基础创建Canvas元素canvas idmyCanvas width800 height600/canvas获取上下文const canvas document.getElementById(myCanvas) const ctx canvas.getContext(2d)基本绘图操作// 绘制矩形 ctx.fillStyle red ctx.fillRect(10, 10, 100, 100) // 绘制圆形 ctx.beginPath() ctx.arc(200, 200, 50, 0, Math.PI * 2) ctx.fillStyle blue ctx.fill() // 绘制路径 ctx.beginPath() ctx.moveTo(300, 100) ctx.lineTo(400, 200) ctx.lineTo(300, 300) ctx.closePath() ctx.strokeStyle green ctx.lineWidth 3 ctx.stroke()Canvas动画基础使用requestAnimationFramefunction animate() { ctx.clearRect(0, 0, canvas.width, canvas.height) update() render() requestAnimationFrame(animate) } animate()带时间戳的动画let lastTime 0 function animate(timestamp) { const deltaTime timestamp - lastTime lastTime timestamp ctx.clearRect(0, 0, canvas.width, canvas.height) update(deltaTime) render() requestAnimationFrame(animate) } requestAnimationFrame(animate)Canvas动画实战粒子系统class Particle { constructor(x, y) { this.x x this.y y this.vx (Math.random() - 0.5) * 4 this.vy (Math.random() - 0.5) * 4 this.radius Math.random() * 5 2 this.color hsl(${Math.random() * 360}, 70%, 50%) } update() { this.x this.vx this.y this.vy if (this.x 0 || this.x canvas.width) { this.vx * -1 } if (this.y 0 || this.y canvas.height) { this.vy * -1 } } render() { ctx.beginPath() ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2) ctx.fillStyle this.color ctx.fill() } } const particles [] for (let i 0; i 100; i) { particles.push(new Particle( Math.random() * canvas.width, Math.random() * canvas.height )) } function animate() { ctx.clearRect(0, 0, canvas.width, canvas.height) particles.forEach(particle { particle.update() particle.render() }) requestAnimationFrame(animate) } animate()物理模拟class Ball { constructor(x, y, radius) { this.x x this.y y this.radius radius this.vx 0 this.vy 0 this.gravity 0.5 this.bounce 0.8 } update() { this.vy this.gravity this.x this.vx this.y this.vy if (this.y this.radius canvas.height) { this.y canvas.height - this.radius this.vy * -this.bounce } if (this.x - this.radius 0) { this.x this.radius this.vx * -1 } if (this.x this.radius canvas.width) { this.x canvas.width - this.radius this.vx * -1 } } render() { ctx.beginPath() ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2) ctx.fillStyle orange ctx.fill() ctx.strokeStyle black ctx.lineWidth 2 ctx.stroke() } } const ball new Ball(400, 100, 30) ball.vx 5 function animate() { ctx.clearRect(0, 0, canvas.width, canvas.height) ball.update() ball.render() requestAnimationFrame(animate) } animate()Canvas高级技术使用ImageDatafunction createImageData() { const imageData ctx.createImageData(canvas.width, canvas.height) const data imageData.data for (let i 0; i data.length; i 4) { data[i] Math.random() * 255 // R data[i 1] Math.random() * 255 // G data[i 2] Math.random() * 255 // B data[i 3] 255 // A } ctx.putImageData(imageData, 0, 0) }使用离屏Canvasconst offscreenCanvas document.createElement(canvas) offscreenCanvas.width canvas.width offscreenCanvas.height canvas.height const offscreenCtx offscreenCanvas.getContext(2d) function renderToOffscreen() { offscreenCtx.clearRect(0, 0, offscreenCanvas.width, offscreenCanvas.height) offscreenCtx.fillStyle red offscreenCtx.fillRect(10, 10, 100, 100) ctx.drawImage(offscreenCanvas, 0, 0) }使用Canvas合成模式ctx.globalCompositeOperation source-over // 默认 ctx.globalCompositeOperation multiply // 正片叠底 ctx.globalCompositeOperation screen // 滤色 ctx.globalCompositeOperation overlay // 叠加 ctx.globalCompositeOperation lighter // 变亮 ctx.globalCompositeOperation darken // 变暗 ctx.globalCompositeOperation xor // 异或Canvas性能优化批量绘制function renderObjects(objects) { ctx.save() objects.forEach(obj { ctx.translate(obj.x, obj.y) ctx.rotate(obj.rotation) ctx.fillStyle obj.color ctx.fillRect(-obj.width/2, -obj.height/2, obj.width, obj.height) ctx.setTransform(1, 0, 0, 1, 0, 0) }) ctx.restore() }使用缓存const cacheCanvas document.createElement(canvas) cacheCanvas.width 100 cacheCanvas.height 100 const cacheCtx cacheCanvas.getContext(2d) function drawCachedObject() { cacheCtx.fillStyle blue cacheCtx.fillRect(0, 0, 100, 100) ctx.drawImage(cacheCanvas, 0, 0) }使用requestAnimationFrame节流const TARGET_FPS 30 const FRAME_INTERVAL 1000 / TARGET_FPS let lastFrameTime 0 function animate(timestamp) { if (timestamp - lastFrameTime FRAME_INTERVAL) { requestAnimationFrame(animate) return } lastFrameTime timestamp ctx.clearRect(0, 0, canvas.width, canvas.height) update() render() requestAnimationFrame(animate) } requestAnimationFrame(animate)Canvas动画库使用Konva.jsimport Konva from konva const stage new Konva.Stage({ container: container, width: 800, height: 600 }) const layer new Konva.Layer() stage.add(layer) const circle new Konva.Circle({ x: 400, y: 300, radius: 50, fill: red, draggable: true }) layer.add(circle) layer.draw() circle.to({ x: 600, y: 400, scaleX: 2, scaleY: 2, duration: 1, easing: Konva.Easings.EaseInOut })使用Fabric.jsimport { fabric } from fabric const canvas new fabric.Canvas(canvas) const rect new fabric.Rect({ left: 100, top: 100, width: 100, height: 100, fill: blue, angle: 45 }) canvas.add(rect) rect.animate(left, 200, { duration: 1000, onChange: canvas.renderAll.bind(canvas), easing: fabric.util.ease.easeOutBounce })常见问题问题1动画卡顿解决方案使用requestAnimationFrame减少绘制调用次数使用离屏Canvas缓存问题2内存泄漏解决方案及时清理画布避免创建大量临时对象使用对象池问题3模糊问题解决方案设置canvas.width/height属性使用devicePixelRatioconst dpr window.devicePixelRatio || 1 canvas.width canvas.offsetWidth * dpr canvas.height canvas.offsetHeight * dpr ctx.scale(dpr, dpr)总结Canvas是前端动画开发的核心技术。通过学习Canvas我们可以实现复杂动画粒子系统、物理模拟创建交互式应用绘图工具、游戏数据可视化图表、图形展示性能优化批量绘制、缓存策略现在开始学习Canvas动画吧你的用户会感谢你的最后一句忠告记得使用requestAnimationFrame进行帧同步
Canvas动画实战:从入门到精通
发布时间:2026/5/16 16:56:35
Canvas动画实战从入门到精通前言各位前端小伙伴不知道你们有没有想过在浏览器中实现复杂的动画效果Canvas可以让你实现各种炫酷的动画我曾经开发过一个在线绘图应用使用Canvas实现了流畅的画笔效果和动画回放功能用户体验非常棒Canvas动画概述Canvas是HTML5提供的一个2D绘图API可以用于创建动态图形、游戏、数据可视化等。Canvas动画的核心是使用requestAnimationFrame进行帧同步。Canvas基础创建Canvas元素canvas idmyCanvas width800 height600/canvas获取上下文const canvas document.getElementById(myCanvas) const ctx canvas.getContext(2d)基本绘图操作// 绘制矩形 ctx.fillStyle red ctx.fillRect(10, 10, 100, 100) // 绘制圆形 ctx.beginPath() ctx.arc(200, 200, 50, 0, Math.PI * 2) ctx.fillStyle blue ctx.fill() // 绘制路径 ctx.beginPath() ctx.moveTo(300, 100) ctx.lineTo(400, 200) ctx.lineTo(300, 300) ctx.closePath() ctx.strokeStyle green ctx.lineWidth 3 ctx.stroke()Canvas动画基础使用requestAnimationFramefunction animate() { ctx.clearRect(0, 0, canvas.width, canvas.height) update() render() requestAnimationFrame(animate) } animate()带时间戳的动画let lastTime 0 function animate(timestamp) { const deltaTime timestamp - lastTime lastTime timestamp ctx.clearRect(0, 0, canvas.width, canvas.height) update(deltaTime) render() requestAnimationFrame(animate) } requestAnimationFrame(animate)Canvas动画实战粒子系统class Particle { constructor(x, y) { this.x x this.y y this.vx (Math.random() - 0.5) * 4 this.vy (Math.random() - 0.5) * 4 this.radius Math.random() * 5 2 this.color hsl(${Math.random() * 360}, 70%, 50%) } update() { this.x this.vx this.y this.vy if (this.x 0 || this.x canvas.width) { this.vx * -1 } if (this.y 0 || this.y canvas.height) { this.vy * -1 } } render() { ctx.beginPath() ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2) ctx.fillStyle this.color ctx.fill() } } const particles [] for (let i 0; i 100; i) { particles.push(new Particle( Math.random() * canvas.width, Math.random() * canvas.height )) } function animate() { ctx.clearRect(0, 0, canvas.width, canvas.height) particles.forEach(particle { particle.update() particle.render() }) requestAnimationFrame(animate) } animate()物理模拟class Ball { constructor(x, y, radius) { this.x x this.y y this.radius radius this.vx 0 this.vy 0 this.gravity 0.5 this.bounce 0.8 } update() { this.vy this.gravity this.x this.vx this.y this.vy if (this.y this.radius canvas.height) { this.y canvas.height - this.radius this.vy * -this.bounce } if (this.x - this.radius 0) { this.x this.radius this.vx * -1 } if (this.x this.radius canvas.width) { this.x canvas.width - this.radius this.vx * -1 } } render() { ctx.beginPath() ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2) ctx.fillStyle orange ctx.fill() ctx.strokeStyle black ctx.lineWidth 2 ctx.stroke() } } const ball new Ball(400, 100, 30) ball.vx 5 function animate() { ctx.clearRect(0, 0, canvas.width, canvas.height) ball.update() ball.render() requestAnimationFrame(animate) } animate()Canvas高级技术使用ImageDatafunction createImageData() { const imageData ctx.createImageData(canvas.width, canvas.height) const data imageData.data for (let i 0; i data.length; i 4) { data[i] Math.random() * 255 // R data[i 1] Math.random() * 255 // G data[i 2] Math.random() * 255 // B data[i 3] 255 // A } ctx.putImageData(imageData, 0, 0) }使用离屏Canvasconst offscreenCanvas document.createElement(canvas) offscreenCanvas.width canvas.width offscreenCanvas.height canvas.height const offscreenCtx offscreenCanvas.getContext(2d) function renderToOffscreen() { offscreenCtx.clearRect(0, 0, offscreenCanvas.width, offscreenCanvas.height) offscreenCtx.fillStyle red offscreenCtx.fillRect(10, 10, 100, 100) ctx.drawImage(offscreenCanvas, 0, 0) }使用Canvas合成模式ctx.globalCompositeOperation source-over // 默认 ctx.globalCompositeOperation multiply // 正片叠底 ctx.globalCompositeOperation screen // 滤色 ctx.globalCompositeOperation overlay // 叠加 ctx.globalCompositeOperation lighter // 变亮 ctx.globalCompositeOperation darken // 变暗 ctx.globalCompositeOperation xor // 异或Canvas性能优化批量绘制function renderObjects(objects) { ctx.save() objects.forEach(obj { ctx.translate(obj.x, obj.y) ctx.rotate(obj.rotation) ctx.fillStyle obj.color ctx.fillRect(-obj.width/2, -obj.height/2, obj.width, obj.height) ctx.setTransform(1, 0, 0, 1, 0, 0) }) ctx.restore() }使用缓存const cacheCanvas document.createElement(canvas) cacheCanvas.width 100 cacheCanvas.height 100 const cacheCtx cacheCanvas.getContext(2d) function drawCachedObject() { cacheCtx.fillStyle blue cacheCtx.fillRect(0, 0, 100, 100) ctx.drawImage(cacheCanvas, 0, 0) }使用requestAnimationFrame节流const TARGET_FPS 30 const FRAME_INTERVAL 1000 / TARGET_FPS let lastFrameTime 0 function animate(timestamp) { if (timestamp - lastFrameTime FRAME_INTERVAL) { requestAnimationFrame(animate) return } lastFrameTime timestamp ctx.clearRect(0, 0, canvas.width, canvas.height) update() render() requestAnimationFrame(animate) } requestAnimationFrame(animate)Canvas动画库使用Konva.jsimport Konva from konva const stage new Konva.Stage({ container: container, width: 800, height: 600 }) const layer new Konva.Layer() stage.add(layer) const circle new Konva.Circle({ x: 400, y: 300, radius: 50, fill: red, draggable: true }) layer.add(circle) layer.draw() circle.to({ x: 600, y: 400, scaleX: 2, scaleY: 2, duration: 1, easing: Konva.Easings.EaseInOut })使用Fabric.jsimport { fabric } from fabric const canvas new fabric.Canvas(canvas) const rect new fabric.Rect({ left: 100, top: 100, width: 100, height: 100, fill: blue, angle: 45 }) canvas.add(rect) rect.animate(left, 200, { duration: 1000, onChange: canvas.renderAll.bind(canvas), easing: fabric.util.ease.easeOutBounce })常见问题问题1动画卡顿解决方案使用requestAnimationFrame减少绘制调用次数使用离屏Canvas缓存问题2内存泄漏解决方案及时清理画布避免创建大量临时对象使用对象池问题3模糊问题解决方案设置canvas.width/height属性使用devicePixelRatioconst dpr window.devicePixelRatio || 1 canvas.width canvas.offsetWidth * dpr canvas.height canvas.offsetHeight * dpr ctx.scale(dpr, dpr)总结Canvas是前端动画开发的核心技术。通过学习Canvas我们可以实现复杂动画粒子系统、物理模拟创建交互式应用绘图工具、游戏数据可视化图表、图形展示性能优化批量绘制、缓存策略现在开始学习Canvas动画吧你的用户会感谢你的最后一句忠告记得使用requestAnimationFrame进行帧同步