PixiJS高性能渲染实战:小程序游戏开发三步走与性能优化指南
1. 项目概述最近几年小程序游戏的热度一直居高不下从营销活动到轻量级休闲游戏它几乎成了触达用户最直接的渠道之一。但很多开发者尤其是从Web前端转过来的朋友一上手就发现不对劲在小程序里做游戏怎么感觉处处受限性能也总差那么一口气Canvas 2D API用起来束手束脚稍微复杂点的动画就卡顿想上WebGL又发现小程序环境对DOM和BOM的支持几乎为零很多成熟的Web游戏引擎根本跑不起来。这正是“PixiJS高性能渲染实战指南”要解决的核心问题。PixiJS本身是一个久经考验的2D WebGL渲染引擎在H5游戏和互动应用领域口碑极佳。但它的运行严重依赖浏览器环境。而“pixi-miniprogram”这个适配方案通过一系列巧妙的“外科手术”让PixiJS的核心渲染能力成功移植到了小程序平台。这不仅仅是简单的API封装而是对渲染管线、资源加载、事件系统的一次深度重构。通过它你可以用熟悉的PixiJS语法在小程序里创建数百甚至上千个精灵Sprite实现流畅的粒子系统、骨骼动画和物理效果性能相比原生Canvas 2D有数倍的提升。这篇文章我将以一个实际参与过的小程序游戏项目经验为基础为你拆解如何用三步走通这条高性能开发路径。无论你是想为品牌活动打造一个炫酷的互动H5还是开发一款轻量级的休闲小游戏这套方案都能让你避开早期探索的坑直接站在一个更高的起点上。我们会从环境搭建、核心场景构建一直讲到性能调优和真机调试所有步骤都配有可运行的代码示例和踩坑实录。2. 核心思路与方案选型为什么是PixiJS 小程序适配在决定技术栈时我们面临几个选择继续用小程序原生的Canvas 2D、寻找其他专为小程序设计的轻量引擎或者走这条“移植成熟引擎”的路。最终选择PixiJS适配方案是基于以下几个核心考量。2.1 性能需求的根本矛盾小程序游戏尤其是带有丰富视觉效果的互动应用对渲染性能的要求非常高。原生Canvas 2D API进行大量绘制调用时尤其是在低端安卓机上帧率很难稳定在60fps。其绘制模式是“立即执行”每帧都要重新绘制所有元素CPU负担重。而WebGL或OpenGL ES的优势在于它是基于状态机和批处理的可以将大量相似的绘制操作合并提交给GPU执行效率有质的飞跃。PixiJS的核心价值就在于它高效地封装了WebGL的复杂性提供了精灵、纹理、容器等高级抽象让开发者无需直接操作晦涩的GL命令就能享受到GPU加速的红利。pixi-miniprogram项目所做的就是架起一座桥让PixiJS这套优秀的渲染逻辑能在小程序的沙箱环境里跑起来。2.2 开发效率与生态优势从头打造一个高性能渲染引擎的代价是巨大的。PixiJS拥有超过7年的迭代历史其稳定性、功能丰富度和社区生态是小团队或新引擎无法比拟的。它支持纹理图集、精灵表动画、滤镜、混合模式、遮罩等高级特性。这意味着我们不需要重复造轮子可以直接利用这些现成的、经过优化的功能模块快速实现产品需求。选择适配方案相当于我们站在了巨人的肩膀上。团队的学习成本也大大降低熟悉PixiJS或Web前端图形开发的工程师可以快速上手将更多精力投入到游戏玩法逻辑和美术资源制作上而不是纠结于底层的渲染细节。2.3 “适配层”而非“重写”的智慧pixi-miniprogram的方案非常聪明它不是用JavaScript重写了一个PixiJS而是修改了原版PixiJS的源码。具体来说它主要做了两件事环境模拟PixiJS内部会用到一些浏览器特有的对象如window、document、navigator等。适配层创建了这些对象的模拟版本提供一个PixiJS期望的运行环境。渲染器替换将PixiJS中直接调用WebGL API的部分替换为调用小程序提供的canvas组件的对应方法。小程序的Canvas组件本身也提供了WebGL上下文通过wx.createCanvasContext或SelectorQuery获取适配层的工作就是做好这个“翻译”。这种做法的好处是我们最大程度地保留了PixiJS的所有功能和性能特性只是改变了它的“运行平台”。项目的example/目录提供了完整的示例展示了从资源加载到交互的完整流程极具参考价值。注意由于修改了源码这个适配方案通常会锁定某个PixiJS版本例如v5.x。在引入项目时需要确认该适配版本与你希望使用的PixiJS特性是否兼容。不过对于大多数2D游戏需求v5版本的功能已经绰绰有余。3. 三步实战从零构建你的第一个PixiJS小程序游戏理论讲完我们进入最关键的实战环节。我将用一个简单的“点击消灭星星”游戏为例带你走通完整的开发流程。这个例子包含了精灵创建、动画、交互和资源管理这些核心要素。3.1 第一步工程化环境搭建与配置小程序开发有自己的工程结构我们不能像在浏览器里一样直接引入一个script标签。第一步就是创建一个干净的小程序项目并正确集成pixi-miniprogram。3.1.1 创建小程序项目与引入适配库首先在微信开发者工具中创建一个新的小程序项目。然后我们需要获取适配库。通常有两种方式直接复制示例项目从pixi-miniprogram的GitCode仓库克隆整个项目将其中的example目录作为一个完整的小程序项目打开。这是最快的方式里面配置和示例代码都是现成的。手动集成推荐用于正式项目更清晰的做法是只将必要的文件拷贝到我们自己的项目中。在项目根目录下新建一个libs文件夹。从pixi-miniprogram仓库中将src/目录下的适配源码通常是pixi.js、pixi.min.js及其相关模块以及示例中提供的pixi-miniprogram组件目录一并拷贝到libs下。我推荐第二种因为它保持了项目结构的清晰。你的libs目录结构可能看起来像这样libs/ ├── pixi.miniprogram/ # 小程序自定义组件 │ ├── index.js │ ├── index.json │ ├── index.wxml │ └── index.wxss └── pixi.js # 适配后的PixiJS核心库3.1.2 关键配置详解接下来是几个容易出错的配置点app.json中启用WebGL必须在app.json的window配置项或具体页面的json文件中声明使用WebGL的Canvas。{ usingComponents: { pixi-canvas: /libs/pixi-miniprogram/index } }同时确保项目设置中“本地设置”选项卡下的“调试基础库”版本足够新以支持完整的WebGL能力。页面WXML模板在游戏页面的WXML中使用我们注册的自定义组件。这里有一个至关重要的细节必须给pixi-canvas组件设置一个固定的、非零的宽高或者在init回调中动态设置。WebGL上下文依赖于一个有效尺寸的Canvas。!-- game.wxml -- view classgame-container pixi-canvas idgameCanvas width750 height1200 bind:initonCanvasInit /pixi-canvas /viewwidth和height我使用了rpx单位对应的逻辑像素值假设设计稿是750rpx宽这能更好地适配不同屏幕。bind:init是组件初始化完成后的回调事件我们将在其中初始化PixiJS应用。game.wxss样式确保Canvas容器样式正确防止滚动或溢出。.game-container { width: 100vw; height: 100vh; display: flex; justify-content: center; align-items: center; background-color: #000; }3.2 第二步PixiJS应用初始化与基础场景构建当Canvas准备就绪后我们就进入了熟悉的PixiJS世界。3.2.1 初始化Pixi应用实例在页面的JS文件game.js中我们首先在onLoad或onReady生命周期中等待Canvas初始化。// game.js Page({ data: {}, onReady() { // 组件已挂载但Canvas可能还未就绪 }, // 这是pixi-canvas组件初始化完成后的回调 onCanvasInit(event) { const canvas event.detail.canvas; // 获取小程序Canvas对象 this.initPixiApp(canvas); }, initPixiApp(canvas) { // 关键步骤使用适配层提供的createPIXI方法传入小程序Canvas const PIXI createPIXI(canvas); // 创建PixiJS应用实例关联到这个Canvas this.app new PIXI.Application({ view: canvas, // 指定渲染视图 width: 750, // 与WXML中宽高一致 height: 1200, backgroundColor: 0x1099bb, // 经典的PixiJS蓝色背景 resolution: window.devicePixelRatio || 1, // 适配高清屏 autoStart: true, // 自动开始渲染循环 }); // 将PixiJS的舞台(stage)保存到页面实例方便后续操作 this.stage this.app.stage; this._setupGame(); // 开始构建游戏场景 }, })这里的createPIXI函数是适配层的入口它处理了环境模拟和渲染器绑定。this.app对象就是整个PixiJS世界的控制器管理着渲染循环、舞台和插件。3.2.2 创建游戏背景与静态元素让我们先搭建一个简单的星空背景。_setupGame() { // 1. 创建背景这里用纯色实际项目中会加载图片纹理 const bg new PIXI.Graphics(); bg.beginFill(0x0a0a2a); // 深蓝色 bg.drawRect(0, 0, this.app.screen.width, this.app.screen.height); bg.endFill(); this.stage.addChild(bg); // 2. 创建标题文本 const titleStyle new PIXI.TextStyle({ fontFamily: Arial, fontSize: 48, fill: #ffffff, stroke: #4a1850, strokeThickness: 5, }); this.titleText new PIXI.Text(点击消灭星星, titleStyle); this.titleText.anchor.set(0.5); // 设置锚点为中点 this.titleText.x this.app.screen.width / 2; this.titleText.y 100; this.stage.addChild(this.titleText); // 3. 创建分数显示 this.score 0; const scoreStyle new PIXI.TextStyle({ fill: 0xffff00, fontSize: 36 }); this.scoreText new PIXI.Text(得分: ${this.score}, scoreStyle); this.scoreText.x 30; this.scoreText.y 30; this.stage.addChild(this.scoreText); // 接下来准备生成可交互的星星 this._createStars(); }至此一个静态的游戏界面就完成了。但我们的星星还没出现。3.3 第三步实现核心交互与动画游戏的灵魂在于交互和动效。我们来创建会动的星星并响应点击事件。3.3.1 动态生成与动画精灵首先我们需要星星的纹理。在小程序里加载网络图片资源需要注意域名配置。为了简化我们可以使用PixiJS内置的图形Graphics来绘制或者使用Base64格式的小图。这里我们用图形绘制。_createStars() { this.stars []; // 用于存储所有星星精灵的数组 const starCount 20; // 星星数量 for (let i 0; i starCount; i) { // 创建一个图形作为星星 const star new PIXI.Graphics(); // 绘制一个五角星 star.beginFill(this._getRandomColor()); star.drawStar(0, 0, 5, 25, 10); // drawStar是PixiJS Graphics的扩展方法适配层需支持 star.endFill(); // 设置随机位置 star.x Math.random() * (this.app.screen.width - 100) 50; star.y Math.random() * (this.app.screen.height - 300) 150; // 设置交互性 star.interactive true; // 启用交互 star.buttonMode true; // 鼠标或触摸悬停时显示手型光标 star.cursor pointer; // 添加点击事件监听器 star.on(pointerdown, this._onStarClicked.bind(this, star)); // 为星星添加一个简单的上下浮动动画 this._addFloatAnimation(star); // 添加到舞台和数组 this.stage.addChild(star); this.stars.push(star); } } // 生成随机颜色 _getRandomColor() { const colors [0xFFD700, 0xFF6B6B, 0x4ECDC4, 0x95E1D3, 0xFF9A76]; return colors[Math.floor(Math.random() * colors.length)]; } // 添加浮动动画 _addFloatAnimation(sprite) { const startY sprite.y; const amplitude 20; // 浮动幅度 const speed 0.05 Math.random() * 0.03; // 随机速度 // 在PixiJS的渲染循环ticker中更新位置 this.app.ticker.add((delta) { // delta是时间因子使动画与帧率无关 sprite.y startY Math.sin(sprite.floatTime || 0) * amplitude; sprite.floatTime (sprite.floatTime || 0) speed * delta; }); }这里有几个关键点interactive和buttonMode必须设置为true精灵才能响应触摸事件。app.ticker这是PixiJS的全局心跳计时器每帧执行。将动画逻辑放在这里可以保证动画流畅。使用delta参数能使动画速度在不同帧率下保持一致。性能注意在ticker回调中避免创建新对象或进行复杂计算。我们这里只是修改已有的属性效率很高。3.3.2 处理用户交互当星星被点击时我们需要让它有一个“被消灭”的效果然后更新分数。_onStarClicked(star) { // 1. 播放一个缩小消失的动画 const shrinkTween new PIXI.Tween.Tween(star.scale) .to({ x: 0, y: 0 }, 300) // 300毫秒内缩放到0 .easing(PIXI.Tween.Easing.Back.In) // 使用回弹缓动函数效果更生动 .start(); // 2. 动画结束后从舞台和数组中移除星星 shrinkTween.onComplete(() { this.stage.removeChild(star); const index this.stars.indexOf(star); if (index -1) { this.stars.splice(index, 1); } // 3. 更新分数 this.score 100; this.scoreText.text 得分: ${this.score}; // 4. 如果星星被消灭完游戏结束 if (this.stars.length 0) { this._showGameOver(); } }); } _showGameOver() { const style new PIXI.TextStyle({ fill: 0x00ff00, fontSize: 64, fontWeight: bold }); const gameOverText new PIXI.Text(恭喜通关, style); gameOverText.anchor.set(0.5); gameOverText.x this.app.screen.width / 2; gameOverText.y this.app.screen.height / 2; this.stage.addChild(gameOverText); }这里使用了PIXI.Tween需要单独引入tween.js库或使用其他动画库来实现补间动画让消失效果更平滑。你也可以用app.ticker手动实现动画但使用动画库代码更简洁。3.3.3 资源加载的注意事项在实际项目中你肯定会使用图片纹理。PixiJS的PIXI.Loader在小程序里同样可以使用但加载路径需要注意。// 假设图片放在项目根目录的images文件夹下 PIXI.Loader.shared .add(star, /images/star.png) // 路径相对于小程序根目录 .add(bg, /images/background.jpg) .load((loader, resources) { // 资源加载完成 const starTexture resources.star.texture; const starSprite new PIXI.Sprite(starTexture); // ... 使用sprite });重要提示小程序对于网络图片有域名白名单限制。如果图片资源放在你自己的服务器上务必在小程序管理后台的“开发设置”-“服务器域名”中配置downloadFile合法域名。对于大量图片强烈建议使用纹理图集Texture Atlas它能将多张图片合并成一张大图减少网络请求和GPU纹理切换显著提升性能。可以使用TexturePacker等工具生成图集和对应的JSON数据文件然后用PIXI.Loader加载。4. 性能优化与深度调优实战一个能跑起来的Demo和一款流畅的游戏之间隔着性能优化这道鸿沟。在小程序环境下内存和GPU资源比浏览器更加紧张优化尤为重要。4.1 渲染性能监控与分析微信开发者工具是性能分析的第一站。打开“调试器”面板的“Performance”或“性能”标签页不同版本名称可能不同录制一段游戏操作。关注FPS图表理想情况是稳定的60fps或屏幕刷新率。如果出现频繁的掉帧FPS骤降说明存在性能瓶颈。观察CPU和内存占用长时间游戏后内存是否持续增长可能存在内存泄漏如未正确移除事件监听器、纹理未销毁。分析调用树Call Tree找到耗时最长的JavaScript函数它们就是优化的重点。在代码中我们也可以埋点监控let frameCount 0; let lastTime Date.now(); this.app.ticker.add(() { frameCount; const now Date.now(); if (now - lastTime 1000) { const fps Math.round((frameCount * 1000) / (now - lastTime)); console.log(当前FPS: ${fps}); frameCount 0; lastTime now; } });4.2 核心优化策略4.2.1 对象池Object Pooling在“消灭星星”游戏中星星不断被销毁和创建虽然我们例子中是游戏结束才创建。频繁创建和销毁PIXI对象Graphics,Sprite会触发垃圾回收GC导致卡顿。对象池是解决这个问题的经典模式。class StarPool { constructor(createFunc, initialSize 20) { this.pool []; this.createFunc createFunc; for (let i 0; i initialSize; i) { this.pool.push(this.createFunc()); } } acquire() { if (this.pool.length 0) { return this.pool.pop(); } // 池子空了新建一个 return this.createFunc(); } release(obj) { // 重置对象状态例如位置、透明度、动画等 obj.alpha 1; obj.scale.set(1); obj.x 0; obj.y 0; if (obj.children) { // 如果有子元素也需要重置 obj.children.forEach(child this.release(child)); } this.pool.push(obj); } } // 使用对象池 this.starPool new StarPool(() { const star new PIXI.Graphics(); star.beginFill(0xFFD700); star.drawStar(0, 0, 5, 25, 10); star.endFill(); star.visible false; // 创建时先隐藏 this.stage.addChild(star); // 创建时就加入舞台只是隐藏 return star; }); // 需要星星时 const star this.starPool.acquire(); star.visible true; star.x 100; star.y 100; // ... 设置其他属性 // 星星被消灭时 star.visible false; this.starPool.release(star); // 回收到池子而不是销毁通过对象池我们复用了图形对象极大地减少了GC压力。4.2.2 纹理与渲染批处理Batch RenderingPixiJS内部会自动对使用相同纹理的精灵进行批处理以减少WebGL绘制调用draw calls。为了最大化这个优势使用纹理图集这是最重要的优化手段。将游戏中的所有小图合并到一张大图上这样无论精灵如何变化GPU都只需要绑定一次纹理渲染效率极高。避免频繁切换纹理在代码层面尽量将使用相同纹理的精灵连续地添加到舞台上或者放在同一个容器PIXI.Container里。PixiJS的渲染顺序会影响批处理。慎用滤镜和混合模式滤镜Filter和特殊的混合模式通常会打断批处理因为它们改变了渲染状态。如果可能将需要滤镜的效果预渲染成纹理。4.2.3 离屏渲染Render Texture对于复杂的、静态的或变化不频繁的背景元素可以考虑使用离屏渲染。将其渲染到一个PIXI.RenderTexture上然后把这个纹理作为一个PIXI.Sprite显示在主舞台上。这样无论背景多复杂每帧都只需要绘制一次这个静态的精灵。// 创建离屏渲染容器 const offScreenContainer new PIXI.Container(); // ... 向其中添加复杂的背景元素 // 创建渲染纹理 const renderTexture PIXI.RenderTexture.create({ width: 750, height: 1200 }); // 将容器渲染到纹理上 this.app.renderer.render(offScreenContainer, { renderTexture }); // 创建一个使用该纹理的精灵并添加到主舞台 const backgroundSprite new PIXI.Sprite(renderTexture); this.stage.addChildAt(backgroundSprite, 0); // 放在最底层注意离屏纹理会占用额外的GPU内存且如果内容需要更新需要重新渲染。4.3 内存管理与泄漏防范小程序有严格的内存限制内存泄漏会导致页面卡顿甚至闪退。销毁纹理当确定不再使用某个纹理时如切换场景调用texture.destroy(true)来销毁它并释放GPU内存。PIXI.utils.destroyTextureCache()可以清理整个纹理缓存但要小心使用。移除事件监听器在精灵被销毁或回收到对象池之前务必移除其上的所有事件监听器off或removeAllListeners否则这些监听器会阻止对象被垃圾回收。清理Ticker回调添加到app.ticker的动画函数如果其引用了已被销毁的对象也会造成泄漏。确保在对象销毁时也从ticker中移除对应的回调。const tickerCallback (delta) { /* 引用了一些对象 */ }; this.app.ticker.add(tickerCallback); // 在需要清理的时候 this.app.ticker.remove(tickerCallback);使用开发者工具的内存快照Memory Snapshot定期拍摄内存快照对比不同时间点的对象实例数量查找持续增长且不应增长的对象类型如PIXI.Sprite,PIXI.Graphics这是定位内存泄漏最有效的方法。5. 真机调试、发布与进阶技巧5.1 真机调试必查清单在开发者工具里跑得流畅不代表在真机上没问题。真机调试是上线前的必经之路。Canvas层级问题小程序的canvas组件是原生组件层级最高会覆盖在普通的WebView组件之上。这意味着你不能用普通的view来覆盖Canvas实现UI如弹出框。解决方案是所有游戏内UI分数、按钮都用PixiJS的PIXI.Text和PIXI.Graphics绘制在Canvas内部。对于不得不使用小程序原生组件的场景如激励视频广告需要动态隐藏Canvas。触摸事件穿透确保Canvas区域能正确接收触摸事件。检查WXML中Canvas组件是否被其他view覆盖或者CSS样式导致其可点区域变小。iOS与安卓差异性能通常iOS的WebGL性能更稳定。低端安卓机是性能瓶颈的主要发生地务必在这些设备上进行测试。内存iOS对内存管理更严格超出限制会直接闪退。安卓则可能表现为越来越卡。渲染精度某些GPU驱动差异可能导致WebGL渲染细微差别特别是透明度和颜色混合方面。需要在多款机型上测试视觉效果。网络资源加载真机环境下图片等资源的加载速度可能较慢甚至失败。要做好加载失败的处理显示占位图和加载中的等待动画Loading界面。5.2 分包与体积优化随着游戏资源图片、音频、字体增多小程序的包体积很容易超过2MB的主包限制。必须使用小程序的分包加载功能。规划分包结构将PixiJS适配库、游戏核心逻辑放在主包。将不同的游戏场景、关卡资源、大量的纹理图集放到独立的分包中。动态加载分包在进入某个游戏场景前使用wx.loadSubpackage预下载对应的分包。wx.loadSubpackage({ name: game-level1, // 分包名 success: (res) { console.log(分包加载成功); this.startLevel1(); }, fail: (err) { console.error(分包加载失败, err); // 处理失败情况如提示用户重试 } });纹理图集压缩使用工具对纹理图集的PNG图片进行压缩如TinyPNG在不明显损失画质的前提下减小文件体积。音频文件同样需要压缩。5.3 进阶技巧状态管理与粒子系统对于更复杂的游戏良好的代码结构至关重要。状态管理即使是小游戏也建议引入一个简单的状态管理机制。例如使用一个全局的gameState对象来管理分数、玩家生命值、游戏阶段菜单、游戏中、暂停、结束等。这比把状态散落在各个页面和组件里要清晰得多。粒子系统Particle SystemPixiJS有强大的粒子系统库pixi-particles。用它来实现爆炸、火焰、魔法等特效非常简单且性能优异。需要注意的是需要找到适用于小程序适配版本的pixi-particles或者根据适配原理自己进行移植。粒子数量是性能杀手务必在低端机上测试并设置合理的粒子数量上限。// 伪代码展示概念 const emitter new PIXI.particles.Emitter( this.stage, // 容器 [texture], // 粒子纹理 emitterConfig // 粒子配置速度、大小、生命周期等 ); emitter.emit true; // 开始发射 this.app.ticker.add(() { emitter.update(this.app.ticker.elapsedMS * 0.001); // 更新粒子 });5.4 常见问题排查速查表问题现象可能原因排查步骤与解决方案白屏Canvas无内容1. Canvas宽高为02. PixiJS初始化失败3. WebGL上下文创建失败1. 检查WXML中Canvas的width和height属性是否设置有效值。2. 在onCanvasInit回调中打印canvas对象确认是否获取到。3. 检查开发者工具Console是否有WebGL相关报错。尝试在真机上运行有些模拟器WebGL支持不全。点击/触摸无反应1. 精灵interactive未设置2. 事件被其他元素遮挡3. 精灵超出Canvas边界1. 确认精灵的interactive和buttonMode属性为true。2. 检查Canvas的WXML和CSS确保其层级最高且无覆盖。3. 检查精灵的hitArea点击区域是否设置正确或使用默认的图形边界。动画卡顿FPS低1. 每帧操作太多对象2. 存在内存泄漏GC频繁3. 复杂滤镜或混合模式过多1. 使用开发者工具Performance面板分析找到耗时函数。引入对象池。2. 使用Memory面板拍摄快照检查对象数量是否异常增长。3. 减少或移除滤镜使用纹理图集优化绘制调用。图片加载失败1. 路径错误2. 服务器域名未配置3. 图片格式或尺寸问题1. 检查图片路径小程序中根目录是/。2. 登录小程序后台在“开发管理”-“开发设置”-“服务器域名”中配置downloadFile合法域名。3. 尝试更换图片格式为PNG或JPG检查图片尺寸是否为2的幂次方非必须但某些GPU优化需要。真机与模拟器表现不一致1. 性能差异2. API支持度差异3. 网络环境差异1. 以真机表现为准在低端安卓机上进行充分测试和性能优化。2. 某些WebGL扩展在真机上可能不可用代码中做好兼容性判断if (renderer.context.getExtension(...))。3. 真机网络不稳定资源加载要有超时和重试机制。走到这里你已经掌握了使用PixiJS在小程序中进行高性能游戏开发的核心流程、关键技术和避坑指南。从环境搭建到性能优化每一步都结合了理论分析和实战代码。这套方案的优势在于它让你能用一套成熟的、高性能的图形API去挑战小程序这个充满潜力的平台。剩下的就是发挥你的创意去构建那些吸引用户的精彩互动体验了。记住性能优化是一个持续的过程在开发的每个阶段都要心中有“帧”眼里有“内存”。