当前位置: 首页 > news >正文

Canvas动画实战:从入门到精通

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进行帧同步
http://www.rkmt.cn/news/1294487.html

相关文章:

  • TSN网络仿真入门:除了OMNeT++,这几个开源框架(NeSTiNg/CoRE4INET)到底该怎么选?
  • SmartNIC与DPU技术解析:计算卸载与性能优化实践
  • GPT-Image2:高保真视频生成新突破
  • ElevenLabs老年语音克隆成功率骤降41%?独家逆向解析v2.8.3模型更新日志中的年龄泛化层删减细节
  • DSP串口通信实战:从寄存器配置到printf重定向
  • 永磁同步电机矢量控制(二)——从坐标系变换到 i_d=0 控制的实现路径
  • 【鸿蒙 HarmonyOS】从零到一:Node.js 环境配置与 DevEco Studio 无缝对接指南
  • FanControl深度技术解析:构建精准智能的风扇控制体系
  • 本地大模型一站式图形化工具Hermes-Studio部署与调优指南
  • 从零到一:在CentOS上部署Chrome与Chromedriver的实战指南
  • Controller层@Transactional注解实战:从“能用”到“用好”的边界探索
  • 用Python玩转城市路网:OSMnx一键下载北京/上海街道数据并可视化分析(附完整代码)
  • [轻量级语义分割] [PaddlePaddle] PP-LiteSeg:从STDCNet到FLD,剖析实时分割的轻量化设计哲学
  • 瑞为技术获IPO备案:年营收4.4亿 亏损6815万
  • 3步完成Android Studio中文界面配置:告别英文困扰,提升开发效率
  • 智芯MCU开发环境实战:从零搭建Keil与JLink生态
  • PX4飞控L1制导律:从航点追踪到航向保持的实战解析
  • 初次在Taotoken平台购买与使用API Key的完整流程
  • MySQL 核心进阶:事务、隔离级别与视图实战
  • 别再问哪个NAS系统好用了!手把手教你用闲置旧电脑安装OpenMediaVault(保姆级教程)
  • Jetson Orin Nano 性能调优实战:为YoloV5推理释放更多显存与算力
  • 如何在本地电脑上实现专业级音频AI处理:OpenVINO AI插件的完整指南
  • 3步轻松掌握视觉Transformer实战:从零开始训练CIFAR-10分类模型
  • 3分钟掌握QuickRecorder:macOS最强开源录屏工具终极指南
  • ZYNQ Ultrascale+ MPSoC平台DDR4配置实战:从数据手册到Vivado参数详解
  • 从1080P到8K视频:拆解FPGA的BANK设计如何扛住高速LVDS信号的压力(以Xilinx 7系列为例)
  • 对比直接使用官方API体验Taotoken在模型切换上的便捷性
  • 暗黑破坏神3终极辅助工具:D3KeyHelper如何彻底解放你的双手?
  • 物理学家们证明,弦理论是从关于宇宙的基本假设中独特推导出来的。
  • 从仿真到硅片:如何用PTPX功耗分析结果指导你的低功耗设计决策?