HarmonyOS图形处理:Canvas绘制与动画开发实战

本文将全面介绍HarmonyOS 5中Canvas组件的使用方法和动画开发技巧,通过详细的代码示例和最佳实践,帮助您掌握图形绘制和动态效果实现的核心技能。

1. Canvas组件基础与核心API

Canvas是HarmonyOS中用于2D图形绘制的重要组件,提供了丰富的绘图接口和灵活的动画支持。

1.1 Canvas基本用法

import { CanvasRenderingContext2D } from '@ohos.graphics.canvas';@Entry
@Component
struct BasicCanvasDemo {private settings: RenderingContextSettings = new RenderingContextSettings(true);private ctx: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);build() {Column() {// 创建Canvas组件Canvas(this.ctx).width('100%').height(300).backgroundColor('#f0f0f0').onReady(() => {this.drawBasicShapes();})}.padding(12)}// 绘制基本图形private drawBasicShapes() {// 绘制矩形this.ctx.fillStyle = '#3498db';this.ctx.fillRect(50, 50, 100, 80);// 绘制圆形this.ctx.beginPath();this.ctx.arc(250, 90, 40, 0, Math.PI * 2);this.ctx.fillStyle = '#e74c3c';this.ctx.fill();// 绘制文本this.ctx.font = '16px sans-serif';this.ctx.fillStyle = '#2c3e50';this.ctx.fillText('HarmonyOS Canvas', 120, 180);// 绘制线条this.ctx.beginPath();this.ctx.moveTo(50, 220);this.ctx.lineTo(300, 220);this.ctx.strokeStyle = '#27ae60';this.ctx.lineWidth = 3;this.ctx.stroke();}
}

1.2 核心绘图API详解

HarmonyOS Canvas提供了完整的2D绘图API,主要包含以下几类方法:

  • 路径绘制beginPath(), moveTo(), lineTo(), arc(), rect(), closePath()
  • 样式设置fillStyle, strokeStyle, lineWidth, lineCap, lineJoin
  • 填充与描边fill(), stroke(), fillRect(), strokeRect()
  • 文本绘制fillText(), strokeText(), font, textAlign
  • 变换操作translate(), rotate(), scale(), transform(), setTransform()
  • 图像绘制drawImage(), createImageData(), putImageData()

2. 高级绘图技巧

2.1 复杂路径与贝塞尔曲线

@Component
struct AdvancedPathDemo {private settings: RenderingContextSettings = new RenderingContextSettings(true);private ctx: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);build() {Canvas(this.ctx).width('100%').height(400).onReady(() => {this.drawComplexPaths();})}private drawComplexPaths() {// 绘制二次贝塞尔曲线this.ctx.beginPath();this.ctx.moveTo(50, 200);this.ctx.quadraticCurveTo(150, 50, 250, 200);this.ctx.strokeStyle = '#8e44ad';this.ctx.lineWidth = 4;this.ctx.stroke();// 绘制三次贝塞尔曲线this.ctx.beginPath();this.ctx.moveTo(50, 250);this.ctx.bezierCurveTo(100, 150, 200, 350, 250, 250);this.ctx.strokeStyle = '#f39c12';this.ctx.lineWidth = 4;this.ctx.stroke();// 绘制复杂形状this.ctx.beginPath();this.ctx.moveTo(300, 50);this.ctx.lineTo(350, 150);this.ctx.arcTo(400, 200, 350, 250, 50);this.ctx.lineTo(300, 200);this.ctx.closePath();this.ctx.fillStyle = 'rgba(52, 152, 219, 0.7)';this.ctx.fill();}
}

2.2 渐变与阴影效果

@Component
struct GradientShadowDemo {private settings: RenderingContextSettings = new RenderingContextSettings(true);private ctx: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);build() {Canvas(this.ctx).width('100%').height(300).onReady(() => {this.drawGradientEffects();})}private drawGradientEffects() {// 创建线性渐变const linearGradient = this.ctx.createLinearGradient(0, 0, 300, 0);linearGradient.addColorStop(0, '#ff9a9e');linearGradient.addColorStop(1, '#fad0c4');this.ctx.fillStyle = linearGradient;this.ctx.fillRect(50, 50, 100, 100);// 创建径向渐变const radialGradient = this.ctx.createRadialGradient(250, 100, 10, 250, 100, 60);radialGradient.addColorStop(0, '#a1c4fd');radialGradient.addColorStop(1, '#c2e9fb');this.ctx.fillStyle = radialGradient;this.ctx.beginPath();this.ctx.arc(250, 100, 60, 0, Math.PI * 2);this.ctx.fill();// 添加阴影效果this.ctx.shadowColor = 'rgba(0, 0, 0, 0.5)';this.ctx.shadowBlur = 15;this.ctx.shadowOffsetX = 10;this.ctx.shadowOffsetY = 10;this.ctx.fillStyle = '#27ae60';this.ctx.fillRect(150, 180, 100, 80);// 重置阴影this.ctx.shadowColor = 'transparent';}
}

3. 动画开发实战

3.1 基础动画实现

@Entry
@Component
struct BasicAnimationDemo {private settings: RenderingContextSettings = new RenderingContextSettings(true);private ctx: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);@State private angle: number = 0;@State private position: number = 50;private animationId: number = 0;build() {Column() {Canvas(this.ctx).width('100%').height(300).onReady(() => {this.startAnimation();}).onDisappear(() => {this.stopAnimation();})Button('重置动画').onClick(() => {this.resetAnimation();}).margin(10).width(200)}}private startAnimation() {const animate = () => {this.ctx.clearRect(0, 0, 400, 300);// 更新动画状态this.angle = (this.angle + 2) % 360;this.position = 50 + Math.sin(Date.now() / 500) * 100;// 绘制旋转矩形this.ctx.save();this.ctx.translate(150, 150);this.ctx.rotate(this.angle * Math.PI / 180);this.ctx.fillStyle = '#3498db';this.ctx.fillRect(-40, -40, 80, 80);this.ctx.restore();// 绘制弹跳球this.ctx.beginPath();this.ctx.arc(this.position, 250, 20, 0, Math.PI * 2);this.ctx.fillStyle = '#e74c3c';this.ctx.fill();this.animationId = requestAnimationFrame(animate);};animate();}private stopAnimation() {if (this.animationId) {cancelAnimationFrame(this.animationId);}}private resetAnimation() {this.stopAnimation();this.angle = 0;this.position = 50;this.startAnimation();}
}

3.2 高级动画:粒子系统

class Particle {x: number;y: number;vx: number;vy: number;radius: number;color: string;alpha: number;constructor(width: number, height: number) {this.x = Math.random() * width;this.y = Math.random() * height;this.vx = (Math.random() - 0.5) * 2;this.vy = (Math.random() - 0.5) * 2;this.radius = Math.random() * 5 + 1;this.color = `hsl(${Math.random() * 360}, 50%, 50%)`;this.alpha = Math.random() * 0.5 + 0.5;}update(width: number, height: number) {this.x += this.vx;this.y += this.vy;// 边界检测if (this.x < 0 || this.x > width) this.vx *= -1;if (this.y < 0 || this.y > height) this.vy *= -1;// 透明度衰减this.alpha -= 0.005;if (this.alpha <= 0) {this.alpha = 0;}}
}@Entry
@Component
struct ParticleSystemDemo {private settings: RenderingContextSettings = new RenderingContextSettings(true);private ctx: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);private particles: Particle[] = [];private animationId: number = 0;private width: number = 400;private height: number = 400;build() {Canvas(this.ctx).width('100%').height(this.height).onReady(() => {this.initializeParticles();this.startAnimation();}).onDisappear(() => {this.stopAnimation();})}private initializeParticles() {for (let i = 0; i < 100; i++) {this.particles.push(new Particle(this.width, this.height));}}private startAnimation() {const animate = () => {// 清空画布this.ctx.clearRect(0, 0, this.width, this.height);// 更新并绘制粒子this.particles.forEach((particle, index) => {particle.update(this.width, this.height);// 移除消失的粒子并添加新粒子if (particle.alpha <= 0) {this.particles.splice(index, 1);this.particles.push(new Particle(this.width, this.height));}// 绘制粒子this.ctx.beginPath();this.ctx.arc(particle.x, particle.y, particle.radius, 0, Math.PI * 2);this.ctx.fillStyle = particle.color;this.ctx.globalAlpha = particle.alpha;this.ctx.fill();});// 重置透明度this.ctx.globalAlpha = 1;this.animationId = requestAnimationFrame(animate);};animate();}private stopAnimation() {if (this.animationId) {cancelAnimationFrame(this.animationId);}}
}

4. 性能优化技巧

4.1 离屏Canvas与缓存

@Component
struct OffscreenCanvasDemo {private mainSettings: RenderingContextSettings = new RenderingContextSettings(true);private mainCtx: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.mainSettings);private offscreenSettings: RenderingContextSettings = new RenderingContextSettings(true);private offscreenCtx: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.offscreenSettings);private complexPattern: ImageBitmap | null = null;build() {Canvas(this.mainCtx).width('100%').height(300).onReady(async () => {await this.createOffscreenPattern();this.drawUsingCache();})}private async createOffscreenPattern() {// 在离屏Canvas上绘制复杂图案this.offscreenCtx.fillStyle = '#34495e';this.offscreenCtx.fillRect(0, 0, 100, 100);for (let i = 0; i < 20; i++) {this.offscreenCtx.beginPath();this.offscreenCtx.arc(Math.random() * 100,Math.random() * 100,Math.random() * 5 + 1,0,Math.PI * 2);this.offscreenCtx.fillStyle = `hsl(${Math.random() * 360}, 70%, 60%)`;this.offscreenCtx.fill();}// 转换为ImageBitmap用于高效重绘this.complexPattern = await createImageBitmap(this.offscreenCtx.canvas);}private drawUsingCache() {if (!this.complexPattern) return;// 使用缓存图案进行绘制(性能优化)for (let i = 0; i < 5; i++) {for (let j = 0; j < 3; j++) {this.mainCtx.drawImage(this.complexPattern,i * 110 + 20,j * 110 + 20,100,100);}}}
}

4.2 分层渲染与脏矩形优化

@Component
struct LayeredRenderingDemo {private backgroundSettings: RenderingContextSettings = new RenderingContextSettings(true);private backgroundCtx: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.backgroundSettings);private foregroundSettings: RenderingContextSettings = new RenderingContextSettings(true);private foregroundCtx: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.foregroundSettings);@State private mouseX: number = 0;@State private mouseY: number = 0;build() {Stack() {// 背景层(静态内容,只需绘制一次)Canvas(this.backgroundCtx).width('100%').height(400).onReady(() => {this.drawBackground();})// 前景层(动态内容,频繁更新)Canvas(this.foregroundCtx).width('100%').height(400).onReady(() => {this.startInteraction();}).onTouchMove((event) => {this.handleTouchMove(event);})}}private drawBackground() {// 绘制静态背景const gradient = this.backgroundCtx.createLinearGradient(0, 0, 400, 400);gradient.addColorStop(0, '#1a2980');gradient.addColorStop(1, '#26d0ce');this.backgroundCtx.fillStyle = gradient;this.backgroundCtx.fillRect(0, 0, 400, 400);// 绘制网格this.backgroundCtx.strokeStyle = 'rgba(255, 255, 255, 0.1)';this.backgroundCtx.lineWidth = 1;for (let i = 0; i < 400; i += 20) {this.backgroundCtx.beginPath();this.backgroundCtx.moveTo(i, 0);this.backgroundCtx.lineTo(i, 400);this.backgroundCtx.stroke();this.backgroundCtx.beginPath();this.backgroundCtx.moveTo(0, i);this.backgroundCtx.lineTo(400, i);this.backgroundCtx.stroke();}}private handleTouchMove(event: TouchEvent) {const touch = event.touches[0];if (touch) {this.mouseX = touch.x;this.mouseY = touch.y;this.updateForeground();}}private updateForeground() {// 只清除需要更新的区域(脏矩形优化)this.foregroundCtx.clearRect(0, 0, 400, 400);// 绘制交互效果this.foregroundCtx.beginPath();this.foregroundCtx.arc(this.mouseX, this.mouseY, 50, 0, Math.PI * 2);this.foregroundCtx.fillStyle = 'rgba(255, 255, 255, 0.2)';this.foregroundCtx.fill();this.foregroundCtx.beginPath();this.foregroundCtx.arc(this.mouseX, this.mouseY, 20, 0, Math.PI * 2);this.foregroundCtx.fillStyle = 'rgba(255, 255, 255, 0.5)';this.foregroundCtx.fill();}private startInteraction() {// 初始绘制this.updateForeground();}
}

5. 实战案例:数据可视化图表

interface ChartData {label: string;value: number;color: string;
}@Entry
@Component
struct DataChartDemo {private settings: RenderingContextSettings = new RenderingContextSettings(true);private ctx: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);@State private chartData: ChartData[] = [{ label: 'Q1', value: 120, color: '#3498db' },{ label: 'Q2', value: 180, color: '#2ecc71' },{ label: 'Q3', value: 90, color: '#e74c3c' },{ label: 'Q4', value: 210, color: '#f39c12' }];build() {Column() {Canvas(this.ctx).width('100%').height(400).onReady(() => {this.drawBarChart();})Button('更新数据').onClick(() => {this.updateData();}).margin(10).width(200)}}private drawBarChart() {const padding = 40;const chartWidth = 400 - padding * 2;const chartHeight = 300 - padding * 2;const barWidth = chartWidth / this.chartData.length * 0.6;const maxValue = Math.max(...this.chartData.map(item => item.value));// 清空画布this.ctx.clearRect(0, 0, 400, 400);// 绘制坐标轴this.ctx.strokeStyle = '#7f8c8d';this.ctx.lineWidth = 2;this.ctx.beginPath();this.ctx.moveTo(padding, padding);this.ctx.lineTo(padding, 400 - padding);this.ctx.lineTo(400 - padding, 400 - padding);this.ctx.stroke();// 绘制刻度this.ctx.textAlign = 'right';this.ctx.font = '12px sans-serif';this.ctx.fillStyle = '#7f8c8d';for (let i = 0; i <= 5; i++) {const value = (maxValue / 5) * i;const y = 400 - padding - (value / maxValue) * chartHeight;this.ctx.beginPath();this.ctx.moveTo(padding - 5, y);this.ctx.lineTo(padding, y);this.ctx.stroke();this.ctx.fillText(value.toString(), padding - 10, y + 4);}// 绘制柱状图this.chartData.forEach((item, index) => {const barHeight = (item.value / maxValue) * chartHeight;const x = padding + index * (chartWidth / this.chartData.length) + (chartWidth / this.chartData.length - barWidth) / 2;const y = 400 - padding - barHeight;// 绘制柱子this.ctx.fillStyle = item.color;this.ctx.fillRect(x, y, barWidth, barHeight);// 绘制数值this.ctx.textAlign = 'center';this.ctx.fillStyle = '#2c3e50';this.ctx.fillText(item.value.toString(), x + barWidth / 2, y - 5);// 绘制标签this.ctx.fillText(item.label, x + barWidth / 2, 400 - padding + 20);});// 绘制标题this.ctx.textAlign = 'center';this.ctx.font = '16px sans-serif';this.ctx.fillStyle = '#2c3e50';this.ctx.fillText('季度销售数据', 200, 30);}private updateData() {// 随机更新数据this.chartData = this.chartData.map(item => ({...item,value: Math.floor(Math.random() * 200) + 50}));this.drawBarChart();}
}

6. 最佳实践与性能建议

  1. 减少重绘区域:使用clearRect()只清除需要更新的区域,而不是整个画布
  2. 使用离屏Canvas:对静态内容或复杂图案进行预渲染
  3. 避免频繁的样式更改:批量绘制相同样式的图形
  4. 使用requestAnimationFrame:实现平滑的动画效果
  5. 优化路径绘制:使用beginPath()closePath()管理路径状态
  6. 合理使用透明度:过多的透明度计算会增加性能开销
  7. 分层渲染:将静态内容和动态内容分离到不同的Canvas层

通过掌握这些Canvas绘制和动画开发技巧,您将能够在HarmonyOS应用中创建丰富多样的图形界面和流畅的交互体验。记得在实际开发中根据具体需求选择合适的优化策略,平衡视觉效果和性能表现。

需要参加鸿蒙认证的请点击 鸿蒙认证链接

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/97787.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/97787.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CCAFusion:用于红外与可见光图像融合的跨模态坐标注意力网络

CCAFusion&#xff1a;用于红外与可见光图像融合的跨模态坐标注意力网络 CCAFusion: Cross-Modal Coordinate Attention Network for Infrared and Visible Image Fusion 摘要 红外与可见光图像融合旨在生成一幅包含全面信息的图像&#xff0c;该图像既能保留丰富的纹理特征&a…

ESP32-P4小智编译历险记:从“编译失败“到“成功烧录“的奇幻之旅,xiaozhi智能聊天机器人编译避坑心得

🚀 ESP32-P4:AI小智编译历险记:从"编译失败"到"成功烧录"的奇幻之旅 要编译其他芯片esp32s3-s2-c3,遇到问题也可以在这里交流 “每一个编译错误都是成长的机会,每一次成功都是坚持的胜利!” —— 某位被编译器折磨的程序员 源码地址:https://githu…

DIFY 项目中通过 Makefile 调用 Dockerfile 并使用 sudo make build-web 命令构建 web 镜像的方法和注意事项

DIFY 项目中通过 Makefile 调用 Dockerfile 并使用 sudo make build-web 命令构建 web 镜像的场景,以下是具体方法和注意事项总结: 一、通过 sudo make build-web 构建 web 镜像的核心方法 1. 理解 Makefile 与 Dockerfile 的关联 Makefile 的作用:DIFY 的 Makefile 中定义…

重学前端015 --- 响应式网页设计 CSS变换

文章目录skew()transformcursortransition.arm .left {} 和 .arm.left {} 区别skew() skew 倾斜变换函数&#xff0c;该函数有两个参数。第一个是剪切x轴的角度&#xff0c;第二个是剪切y轴的角度。 transform: skew(0deg, 44deg);transform .arm.left {top: 35%;left: 5%;t…

【GMX v1实战】时序风险结算与资本成本:深度解析 GMX 永续合约的资金费率机制

在去中心化衍生品交易平台GMX中&#xff0c;当你准备开立杠杆合约仓位&#xff08;无论是做多还是做空某个资产&#xff09;时&#xff0c;系统会默默执行一个关键前置动作——调用名为 ​​updateCumulativeFundingRate​​ 的函数。这个看似普通的“准备工作”&#xff0c;实…

中宇联云计算SD-WAN的售后服务怎么样

前言在数字化转型浪潮中&#xff0c;企业选择SD-WAN服务商不仅关注技术性能&#xff0c;更看重售后服务的质量与可靠性。中宇联云计算作为行业领先者&#xff0c;其SD-WAN售后服务体系已成为行业标杆。随着全球数字化进程加速&#xff0c;企业对广域网&#xff08;WAN&#xff…

【Kubernetes】K8s 集群外服务配置 Service 访问

在 Kubernetes 集群中&#xff0c;内部服务可通过 Service-name 进行访问。那么&#xff0c;对于集群外的服务&#xff0c;Pod 应该如何通过 Service 进行访问呢&#xff1f;一起来看看吧&#xff01;此处举例以 Pod 访问集群外部的 Mysql 数据库1、创建 Service# 创建 Service…

Linux 开发工具(1)

从开始讲Linux&#xff0c;我们的目标绝不止于写几个命令这么简单。我们的目的是在Linux系统上做开发。因此学习Linux的开发工具也是必不可少的。本章将重点讲解&#xff1a;包管理器apt(CentOS叫yum&#xff0c;这里用ubuntu举例)&#xff0c;vim编辑器。一.包管理器apt1.安装…

redis面试点记录

1、主从复制psync-> runid->runid是&#xff1f;则是全量->返回fullresync和runid和复制进度->bgsave命令准备RDB文件->之后的命令写入replication_buffer->发送RDB->发送replication_buffer的信息repl_backlog_buffer环型缓冲区&#xff0c;主节点只有一…

Elastic APM 与 Elasticsearch 集成:构建完整可观测性栈

引言 Elastic APM 深度依赖 Elasticsearch 作为数据后端&#xff0c;但正确集成可以解锁更强大的功能&#xff0c;如自定义查询、聚合分析和与其它 Elastic 工具的协同。本文探讨 APM 与 Elasticsearch 的集成细节&#xff0c;包括数据流、索引管理以及高级用例&#xff0c;帮助…

开源模型应用落地-基于DPO的Qwen3-4B意图理解精准对齐实践(二十)

一、前言 在大模型技术蓬勃发展的今天,如何让AI真正“理解”用户意图,而非仅仅生成流畅文本,已成为落地应用的核心瓶颈。尤其是在客服、搜索、智能助手等场景中,模型对用户query的深层语义解析能力,直接决定了交互体验的成败。然而,经过标准SFT(监督微调)训练的模型,往…

23种设计模式案例

一、创建型模式 1. 单例模式 (Singleton Pattern) 应用场景: 全局状态管理、全局配置、共享资源访问 // 全局状态管理器 class Store {constructor() {if (Store.instance) return Store.instance;this.state {};Store.instance this;}getState(key) { return this.state[key…

ctfshow_web13-----------文件上传.user.ini

打开题目发现是一个文件上传题扫描后发现存在upload.php.bak.bak是备份文件拿到源码正则过滤了php&#xff0c;文件大小<24,文件名小于9经尝试&#xff0c;改后缀php5,ptml均不行&#xff0c;使用.htaccess文件也不成功上传上传.user.ini&#xff0c;在文件中写上auto_prepe…

图像拼接案例,抠图案例

目录 一.图像拼接案例 1.图像拼接项目介绍 2.核心步骤 ①计算图片特征点及描述符 ②匹配特征点&#xff0c;使用暴力匹配器 ③筛选有效匹配 ④计算透视变换矩阵 ⑤应用变换和拼接 二.抠图案例 1.缩放旋转处理 2.转化为灰度图并二值化 3.找出所有轮廓&#xff0c;并在…

【左程云算法笔记016】双端队列-双链表和固定数组实现

目录 1&#xff09;双端队列的介绍 2&#xff09;双端队列用双链表的实现代码演示 3&#xff09;双端队列用固定数组的实现 代码演示 视频 【算法讲解016【入门】双端队列-双链表和固定数组实现】 Leecode leecode641 设计循环双端队列 1&#xff09;双端队列的介绍 可以…

ffplay视频输出和尺寸变换

视频输出模块 视频输出初始化的主要流程 我们开始分析视频&#xff08;图像&#xff09;的显示。 因为使⽤了SDL&#xff0c;⽽video的显示也依赖SDL的窗⼝显示系统&#xff0c;所以先从main函数的SDL初始化看起&#xff08;节选&#xff09;&#xff1a; int main(int argc, c…

协议_https协议

http http协议是将数据以明文的形式在网络上传输。若是传输的数据中包含一些敏感信息比如银行卡信息等可能会被有心人攻击造成信息泄露或被篡改。 总结&#xff1a;http协议进行数据传输难以保证数据的隐私性以及数据完整性&#xff0c;为了保证数据的准确定引入了https这一协…

阿里云 腾讯云 API 自动化查询指南

文章目录一、核心思路与架构建议二、经验与核心建议三、技术方案选型建议四、API使用详解4.1 阿里云4.2 腾讯云五、进阶&#xff1a;与内部系统联动免费个人运维知识库&#xff0c;欢迎您的订阅&#xff1a;literator_ray.flowus.cn 一、核心思路与架构建议 自动化流程可以概括…

【Unity 性能优化之路——概述(0)】

Unity性能优化概述性能优化不是某个环节的极致压榨&#xff0c;而是所有模块的协同共进。本文将为你建立完整的Unity性能优化知识体系。很多Unity开发者一提到性能优化&#xff0c;首先想到的就是Draw Call、Batches这些渲染指标。这没错&#xff0c;但它们只是性能优化中的一部…

灵码产品演示:软件工程架构分析

作者&#xff1a;了哥 演示目的演示灵码对于整个复杂软件工程项目的架构分析能力&#xff0c;输出项目的软件系统架构图。演示文档接口生成能力。演示准备 克隆工程地址到本地&#xff08;需提前安装好 git 工具&#xff0c; 建议本地配置 brew&#xff09;&#xff1a; git cl…