news 2026/7/26 23:00:29

HarmonyOS应用《玄象》开发实战:Canvas 星空绘制:arc / fillStyle / 随机散点算法

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
HarmonyOS应用《玄象》开发实战:Canvas 星空绘制:arc / fillStyle / 随机散点算法

阅读时长:约 19 分钟 | 难度:★★★★☆ | 篇章:第 2 篇 · 启动体验:Splash 与动画
对应源码:entry/src/main/ets/pages/SplashPage.ets中的drawStarField方法

前言

玄象项目启动页的"星空背景"是整个应用视觉调性的奠基。深邃夜空、百点繁星、银河流光——这套视觉语言用纯 Canvas API 实现,未引入任何图片资源。本篇将深入剖析玄象项目drawStarField方法的实现细节:从CanvasRenderingContext2DAPI、Math.random()随机散点算法、createLinearGradient银河渐变,到绘制性能优化与可复用性重构。掌握这套 Canvas 绘制方法论,您就能用纯代码绘制任何复杂视觉场景。

提示:玄象项目星空背景未引入图片,是因为 Canvas 矢量绘制具有"无极缩放、极小体积、动态可控"三大优势。对于启动页这种轻量场景,纯 Canvas 实现是最优解。

一、drawStarField 完整源码

1.1 源码全貌

privatedrawStarField=(canvas:CanvasRenderingContext2D,canvasWidth:number,canvasHeight:number)=>{// 1. 绘制深色背景canvas.fillStyle=Colors.BG_DARK;canvas.fillRect(0,0,canvasWidth,canvasHeight);// 2. 绘制星星for(leti=0;i<100;i++){constx=Math.random()*canvasWidth;consty=Math.random()*canvasHeight*0.7;constsize=Math.random()*2+0.5;constalpha=Math.random()*0.8+0.2;canvas.fillStyle=`rgba(255, 255, 220,${alpha})`;canvas.beginPath();canvas.arc(x,y,size,0,Math.PI*2);canvas.fill();}// 3. 银河效果constgradient=canvas.createLinearGradient(0,0,canvasWidth,canvasHeight*0.5);gradient.addColorStop(0,'rgba(212, 168, 67, 0)');gradient.addColorStop(0.5,'rgba(212, 168, 67, 0.05)');gradient.addColorStop(1,'rgba(212, 168, 67, 0)');canvas.fillStyle=gradient;canvas.fillRect(0,0,canvasWidth,canvasHeight*0.5);}

1.2 三大绘制阶段

玄象项目星空绘制分为三大阶段:

阶段操作视觉效果
背景填充fillRect全屏深色深邃夜空
星点散布100 个随机星点繁星点点
银河渐变线性渐变带银河流光

二、CanvasRenderingContext2D 核心 API

2.1 fillStyle 填充样式

canvas.fillStyle=Colors.BG_DARK;// 字符串色值canvas.fillStyle='rgba(255, 255, 220, 0.5)';// RGBA 字符串canvas.fillStyle=gradient;// 渐变对象

fillStyle接受三种类型的值:

  1. CSS 颜色字符串'#D4A843''rgba(255,255,220,0.5)'
  2. CanvasGradient 对象:通过createLinearGradient创建
  3. CanvasPattern 对象:通过createPattern创建

2.2 fillRect 绘制矩形

canvas.fillRect(0,0,canvasWidth,canvasHeight);

fillRect(x, y, width, height)使用当前fillStyle填充矩形区域。

2.3 beginPath / arc / fill 绘制圆形

canvas.beginPath();canvas.arc(x,y,size,0,Math.PI*2);canvas.fill();

玄象项目用三步绘制每个星点:

  1. beginPath:开始一条新路径
  2. arc(x, y, radius, startAngle, endAngle):绘制圆形路径
  3. fill:填充当前路径

2.4 arc 角度参数详解

arc的角度参数:

参数含义
x圆心 x 坐标
y圆心 y 坐标
radius半径
startAngle起始角度(弧度)
endAngle结束角度(弧度)

绘制完整圆:startAngle = 0endAngle = Math.PI * 2

提示:Math.PI * 2等于 360 度。玄象项目绘制完整星点使用此值。

三、随机散点算法

3.1 星点参数随机化

for(leti=0;i<100;i++){constx=Math.random()*canvasWidth;consty=Math.random()*canvasHeight*0.7;constsize=Math.random()*2+0.5;constalpha=Math.random()*0.8+0.2;// 绘制星点}

玄象项目为每个星点生成 4 个随机参数:

参数计算公式取值范围
xMath.random() * canvasWidth[0, canvasWidth]
yMath.random() * canvasHeight * 0.7[0, 0.7 × canvasHeight]
sizeMath.random() * 2 + 0.5[0.5, 2.5]
alphaMath.random() * 0.8 + 0.2[0.2, 1.0]

3.2 y 坐标限制的设计意图

consty=Math.random()*canvasHeight*0.7;

玄象项目刻意将星点的 y 坐标限制在屏幕上方 70% 区域,原因:

  1. 避开底部星图区:下方 40% 区域有独立的星图装饰,星点不应与之重叠。
  2. 模拟真实星空:地平线附近星空稀疏,高处星空密集。

3.3 星点尺寸的分布规律

constsize=Math.random()*2+0.5;

玄象项目星点尺寸分布:

  • 小星点(0.5-1.0):占 25%,远处微星
  • 中星点(1.0-2.0):占 50%,常态星点
  • 大星点(2.0-2.5):占 25%,明亮主星

提示:Math.random()返回 [0, 1) 区间的浮点数。Math.random() * 2 + 0.5的取值范围是 [0.5, 2.5)。

3.4 透明度的随机化

constalpha=Math.random()*0.8+0.2;canvas.fillStyle=`rgba(255, 255, 220,${alpha})`;

玄象项目通过随机透明度营造星点闪烁感

  • 高透明度(0.8-1.0):明亮星点
  • 中透明度(0.5-0.8):常态星点
  • 低透明度(0.2-0.5):微弱星点

3.5 模板字符串拼接 RGBA

canvas.fillStyle=`rgba(255, 255, 220,${alpha})`;

玄象项目使用模板字符串动态拼接颜色:

  • 255, 255, 220:暖白色 RGB 值,模拟星光色温
  • ${alpha}:随机透明度

提示:玄象项目使用(255, 255, 220)而非(255, 255, 255),是因为暖白色更符合东方夜空的视觉调性。

四、银河渐变带

4.1 createLinearGradient 线性渐变

constgradient=canvas.createLinearGradient(0,0,canvasWidth,canvasHeight*0.5);

createLinearGradient(x0, y0, x1, y1)创建线性渐变:

参数含义玄象项目值
x0, y0渐变起点(0, 0) 左上角
x1, y1渐变终点(canvasWidth, 0.5 × canvasHeight) 右中部

渐变方向:从左上角到右中部,斜向 45 度流向。

4.2 addColorStop 颜色断点

gradient.addColorStop(0,'rgba(212, 168, 67, 0)');gradient.addColorStop(0.5,'rgba(212, 168, 67, 0.05)');gradient.addColorStop(1,'rgba(212, 168, 67, 0)');

addColorStop(offset, color)添加颜色断点:

offsetalpha视觉效果
00完全透明
0.50.05微弱金光
10完全透明

4.3 银河带的设计意图

玄象项目银河带的设计意图:

  1. 强化中心:渐变带从透明到微亮再到透明,形成中间亮、两端暗的"光带"。
  2. 斜向流向:45 度斜向渐变模拟银河流动方向。
  3. 微妙金光rgba(212, 168, 67, 0.05)透明度仅 5%,避免抢夺主视觉。

4.4 银河带的覆盖区域

canvas.fillRect(0,0,canvasWidth,canvasHeight*0.5);

银河带覆盖屏幕上方 50% 区域,与星点分布区域(上方 70%)部分重叠,营造星点在银河中漂浮的视觉。

五、Canvas 绘制性能优化

5.1 减少状态切换

玄象项目星空绘制存在的性能优化点:

// 当前实现:每次循环都修改 fillStylefor(leti=0;i<100;i++){canvas.fillStyle=`rgba(255, 255, 220,${alpha})`;// 状态切换// ...}// 优化方案 1:按 alpha 分组绘制constgroups={};for(leti=0;i<100;i++){constalpha=(Math.floor(Math.random()*8)+2)/10;// 离散化if(!groups[alpha])groups[alpha]=[];groups[alpha].push({x,y,size});}for(constalphaingroups){canvas.fillStyle=`rgba(255, 255, 220,${alpha})`;// 一次设置for(conststarofgroups[alpha]){canvas.beginPath();canvas.arc(star.x,star.y,star.size,0,Math.PI*2);canvas.fill();}}

5.2 使用 Path2D 批量绘制

constpath=newPath2D();for(leti=0;i<100;i++){path.arc(x,y,size,0,Math.PI*2);}canvas.fill(path);// 一次填充所有路径

5.3 离屏 Canvas 预渲染

对于静态星空,可使用离屏 Canvas预渲染一次:

privateoffscreenCanvas:OffscreenCanvas;aboutToAppear():void{this.offscreenCanvas=newOffscreenCanvas(canvasWidth,canvasHeight);constctx=this.offscreenCanvas.getContext('2d');this.drawStarField(ctx,canvasWidth,canvasHeight);}// 主 Canvas 直接 drawImagemainCtx.drawImage(this.offscreenCanvas,0,0);

提示:玄象项目启动页仅渲染一次,性能优化收益不大。但在动画场景(如每帧重绘的星空)中,离屏 Canvas 可显著提升性能。

六、绘制函数的可复用性重构

6.1 当前实现的局限

玄象项目drawStarField箭头函数赋值给类成员

privatedrawStarField=(canvas,width,height)=>{// ...};

这种写法存在局限:

  1. 无法继承:箭头函数无法被子类重写。
  2. 无法静态化:箭头函数不属于原型方法。
  3. 测试困难:箭头函数难以 Mock。

6.2 重构为静态方法

// common/utils/StarFieldRenderer.etsexportclassStarFieldRenderer{staticrender(canvas:CanvasRenderingContext2D,width:number,height:number,options:StarFieldOptions):void{canvas.fillStyle=options.bgColor;canvas.fillRect(0,0,width,height);for(leti=0;i<options.starCount;i++){constx=Math.random()*width;consty=Math.random()*height*options.starAreaRatio;constsize=Math.random()*options.maxStarSize+options.minStarSize;constalpha=Math.random()*0.8+0.2;canvas.fillStyle=`rgba(${options.starColor},${alpha})`;canvas.beginPath();canvas.arc(x,y,size,0,Math.PI*2);canvas.fill();}}}

6.3 使用接口定义配置

interfaceStarFieldOptions{bgColor:string;// 背景色starCount:number;// 星点数量starColor:string;// 星点颜色 RGB(如 "255, 255, 220")minStarSize:number;// 最小星点尺寸maxStarSize:number;// 最大星点尺寸starAreaRatio:number;// 星点分布区域比例hasGalaxy:boolean;// 是否绘制银河galaxyColor:string;// 银河颜色}

提示:玄象项目若推出"星宿详情页"等其他需要星空背景的页面,可直接复用StarFieldRenderer,传入不同配置即可。

七、星空绘制的扩展方向

7.1 添加流星动画

privatemeteors:Meteor[]=[];privatedrawMeteors=(canvas:CanvasRenderingContext2D)=>{for(constmeteorofthis.meteors){constgradient=canvas.createLinearGradient(meteor.x,meteor.y,meteor.x-meteor.length*Math.cos(meteor.angle),meteor.y-meteor.length*Math.sin(meteor.angle));gradient.addColorStop(0,'rgba(255, 255, 220, 1)');gradient.addColorStop(1,'rgba(255, 255, 220, 0)');canvas.strokeStyle=gradient;canvas.lineWidth=2;canvas.beginPath();canvas.moveTo(meteor.x,meteor.y);canvas.lineTo(meteor.x-meteor.length*Math.cos(meteor.angle),meteor.y-meteor.length*Math.sin(meteor.angle));canvas.stroke();}};

7.2 添加星点闪烁动画

privatestarTwinkle=(canvas:CanvasRenderingContext2D,stars:Star[])=>{for(conststarofstars){star.alpha+=star.twinkleSpeed*star.twinkleDirection;if(star.alpha>1||star.alpha<0.2){star.twinkleDirection*=-1;}canvas.fillStyle=`rgba(255, 255, 220,${star.alpha})`;canvas.beginPath();canvas.arc(star.x,star.y,star.size,0,Math.PI*2);canvas.fill();}};

7.3 添加银河旋涡

privatedrawGalaxySwirl=(canvas:CanvasRenderingContext2D,centerX:number,centerY:number)=>{constarms=4;conststarsPerArm=200;for(letarm=0;arm<arms;arm++){for(leti=0;i<starsPerArm;i++){constangle=(arm/arms)*Math.PI*2+(i/starsPerArm)*Math.PI*4;constradius=(i/starsPerArm)*200;constx=centerX+Math.cos(angle)*radius;consty=centerY+Math.sin(angle)*radius;canvas.fillStyle=`rgba(212, 168, 67,${Math.random()*0.5+0.2})`;canvas.beginPath();canvas.arc(x,y,Math.random()*1.5+0.5,0,Math.PI*2);canvas.fill();}}};

八、玄象项目 Canvas 绘制规范总结

8.1 Canvas 绘制四步法

玄象项目 Canvas 绘制遵循"四步法":

  1. 状态设置fillStyle/strokeStyle/lineWidth
  2. 路径定义beginPath/moveTo/lineTo/arc
  3. 填充或描边fill/stroke
  4. 状态恢复:(可选)保存与恢复状态

8.2 Canvas 绘制规范表

场景API 组合
绘制矩形fillStyle+fillRect
绘制圆形fillStyle+beginPath+arc+fill
绘制线段strokeStyle+lineWidth+beginPath+moveTo+lineTo+stroke
绘制渐变createLinearGradient+addColorStop+ 赋值给fillStyle
绘制文本fillStyle+font+fillText

总结

本篇以玄象项目drawStarField方法为蓝本,深入剖析了 HarmonyOS Canvas 绘制的实现细节:从CanvasRenderingContext2D核心 API、Math.random()随机散点算法、createLinearGradient银河渐变,到绘制性能优化(按 alpha 分组、Path2D 批量、离屏 Canvas)与可复用性重构(静态方法 + 接口配置)。掌握这套 Canvas 绘制方法论,您就能用纯代码绘制任何复杂视觉场景。

下一篇:《13 · Canvas 太极图算法:阴阳半圆 + 鱼眼小圆的数学推导》,将带您深入玄象项目启动页中央的太极图绘制算法。

如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!


相关资源:

  • HarmonyOS 官方文档:CanvasRenderingContext2D
  • HarmonyOS 官方文档:Canvas 组件
  • MDN Canvas API:developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API
  • 开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/26 22:59:26

CZSC缠论量化分析插件:3分钟掌握通达信智能交易系统

CZSC缠论量化分析插件&#xff1a;3分钟掌握通达信智能交易系统 【免费下载链接】Indicator 通达信缠论可视化分析插件 项目地址: https://gitcode.com/gh_mirrors/ind/Indicator 在复杂多变的金融市场中&#xff0c;缠论作为中国特色技术分析理论&#xff0c;以其严谨的…

作者头像 李华
网站建设 2026/7/26 22:57:17

agent面试必备48-AI Agent 核心进阶:多智能体“任务分配策略”

&#x1f3af; AI Agent 核心进阶&#xff1a;多智能体“任务分配策略”全解析与面试通关指南 在搭建多智能体&#xff08;Multi-Agent&#xff09;系统时&#xff0c;如果我们已经搞定了“通信机制”&#xff0c;紧接着就会面临一个现实的“管理学”问题&#xff1a;当系统里有…

作者头像 李华
网站建设 2026/7/26 22:53:12

嵌入式消息队列(MSGQ)设计原理与多核通信实战指南

1. 消息队列在嵌入式系统中的核心价值与设计哲学在嵌入式系统开发&#xff0c;尤其是涉及实时操作系统&#xff08;RTOS&#xff09;和多核处理器的项目中&#xff0c;如何让不同的软件模块、线程乃至运行在不同核心上的任务安全、高效地“对话”&#xff0c;是一个绕不开的核心…

作者头像 李华
网站建设 2026/7/26 22:53:07

TMS320C54x DSP芯片支持库(CSL)核心模块详解与实战开发指南

1. 项目概述与CSL核心价值如果你正在使用TI的TMS320C54x系列DSP进行开发&#xff0c;那么“芯片支持库”这个词对你来说一定不陌生。它就像是你和DSP硬件之间的一位资深翻译官&#xff0c;把那些晦涩难懂的寄存器地址、位域操作&#xff0c;翻译成一句句清晰明了的C语言函数调用…

作者头像 李华
网站建设 2026/7/26 22:51:54

手把手实现Transformer:从原理到PyTorch实战

1. 项目概述作为一名从传统软件开发转型AI的工程师&#xff0c;我深刻理解学习Transformer架构时的困惑。这个看似复杂的模型&#xff0c;其实核心思想非常优雅。今天我将用最接地气的方式&#xff0c;带大家手撕Transformer代码&#xff0c;同时保证每个模块都能独立运行测试。…

作者头像 李华