
阅读时长约 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.fillStyleColors.BG_DARK;canvas.fillRect(0,0,canvasWidth,canvasHeight);// 2. 绘制星星for(leti0;i100;i){constxMath.random()*canvasWidth;constyMath.random()*canvasHeight*0.7;constsizeMath.random()*20.5;constalphaMath.random()*0.80.2;canvas.fillStylergba(255, 255, 220,${alpha});canvas.beginPath();canvas.arc(x,y,size,0,Math.PI*2);canvas.fill();}// 3. 银河效果constgradientcanvas.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.fillStylegradient;canvas.fillRect(0,0,canvasWidth,canvasHeight*0.5);}1.2 三大绘制阶段玄象项目星空绘制分为三大阶段阶段操作视觉效果背景填充fillRect全屏深色深邃夜空星点散布100 个随机星点繁星点点银河渐变线性渐变带银河流光二、CanvasRenderingContext2D 核心 API2.1 fillStyle 填充样式canvas.fillStyleColors.BG_DARK;// 字符串色值canvas.fillStylergba(255, 255, 220, 0.5);// RGBA 字符串canvas.fillStylegradient;// 渐变对象fillStyle接受三种类型的值CSS 颜色字符串#D4A843或rgba(255,255,220,0.5)CanvasGradient 对象通过createLinearGradient创建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();玄象项目用三步绘制每个星点beginPath开始一条新路径arc(x, y, radius, startAngle, endAngle)绘制圆形路径fill填充当前路径2.4 arc 角度参数详解arc的角度参数参数含义x圆心 x 坐标y圆心 y 坐标radius半径startAngle起始角度弧度endAngle结束角度弧度绘制完整圆startAngle 0endAngle Math.PI * 2。提示Math.PI * 2等于 360 度。玄象项目绘制完整星点使用此值。三、随机散点算法3.1 星点参数随机化for(leti0;i100;i){constxMath.random()*canvasWidth;constyMath.random()*canvasHeight*0.7;constsizeMath.random()*20.5;constalphaMath.random()*0.80.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 坐标限制的设计意图constyMath.random()*canvasHeight*0.7;玄象项目刻意将星点的 y 坐标限制在屏幕上方 70% 区域原因避开底部星图区下方 40% 区域有独立的星图装饰星点不应与之重叠。模拟真实星空地平线附近星空稀疏高处星空密集。3.3 星点尺寸的分布规律constsizeMath.random()*20.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 透明度的随机化constalphaMath.random()*0.80.2;canvas.fillStylergba(255, 255, 220,${alpha});玄象项目通过随机透明度营造星点闪烁感高透明度0.8-1.0明亮星点中透明度0.5-0.8常态星点低透明度0.2-0.5微弱星点3.5 模板字符串拼接 RGBAcanvas.fillStylergba(255, 255, 220,${alpha});玄象项目使用模板字符串动态拼接颜色255, 255, 220暖白色 RGB 值模拟星光色温${alpha}随机透明度提示玄象项目使用(255, 255, 220)而非(255, 255, 255)是因为暖白色更符合东方夜空的视觉调性。四、银河渐变带4.1 createLinearGradient 线性渐变constgradientcanvas.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 银河带的设计意图玄象项目银河带的设计意图强化中心渐变带从透明到微亮再到透明形成中间亮、两端暗的光带。斜向流向45 度斜向渐变模拟银河流动方向。微妙金光rgba(212, 168, 67, 0.05)透明度仅 5%避免抢夺主视觉。4.4 银河带的覆盖区域canvas.fillRect(0,0,canvasWidth,canvasHeight*0.5);银河带覆盖屏幕上方 50% 区域与星点分布区域上方 70%部分重叠营造星点在银河中漂浮的视觉。五、Canvas 绘制性能优化5.1 减少状态切换玄象项目星空绘制存在的性能优化点// 当前实现每次循环都修改 fillStylefor(leti0;i100;i){canvas.fillStylergba(255, 255, 220,${alpha});// 状态切换// ...}// 优化方案 1按 alpha 分组绘制constgroups{};for(leti0;i100;i){constalpha(Math.floor(Math.random()*8)2)/10;// 离散化if(!groups[alpha])groups[alpha][];groups[alpha].push({x,y,size});}for(constalphaingroups){canvas.fillStylergba(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 批量绘制constpathnewPath2D();for(leti0;i100;i){path.arc(x,y,size,0,Math.PI*2);}canvas.fill(path);// 一次填充所有路径5.3 离屏 Canvas 预渲染对于静态星空可使用离屏 Canvas预渲染一次privateoffscreenCanvas:OffscreenCanvas;aboutToAppear():void{this.offscreenCanvasnewOffscreenCanvas(canvasWidth,canvasHeight);constctxthis.offscreenCanvas.getContext(2d);this.drawStarField(ctx,canvasWidth,canvasHeight);}// 主 Canvas 直接 drawImagemainCtx.drawImage(this.offscreenCanvas,0,0);提示玄象项目启动页仅渲染一次性能优化收益不大。但在动画场景如每帧重绘的星空中离屏 Canvas 可显著提升性能。六、绘制函数的可复用性重构6.1 当前实现的局限玄象项目drawStarField是箭头函数赋值给类成员privatedrawStarField(canvas,width,height){// ...};这种写法存在局限无法继承箭头函数无法被子类重写。无法静态化箭头函数不属于原型方法。测试困难箭头函数难以 Mock。6.2 重构为静态方法// common/utils/StarFieldRenderer.etsexportclassStarFieldRenderer{staticrender(canvas:CanvasRenderingContext2D,width:number,height:number,options:StarFieldOptions):void{canvas.fillStyleoptions.bgColor;canvas.fillRect(0,0,width,height);for(leti0;ioptions.starCount;i){constxMath.random()*width;constyMath.random()*height*options.starAreaRatio;constsizeMath.random()*options.maxStarSizeoptions.minStarSize;constalphaMath.random()*0.80.2;canvas.fillStylergba(${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, 220minStarSize:number;// 最小星点尺寸maxStarSize:number;// 最大星点尺寸starAreaRatio:number;// 星点分布区域比例hasGalaxy:boolean;// 是否绘制银河galaxyColor:string;// 银河颜色}提示玄象项目若推出星宿详情页等其他需要星空背景的页面可直接复用StarFieldRenderer传入不同配置即可。七、星空绘制的扩展方向7.1 添加流星动画privatemeteors:Meteor[][];privatedrawMeteors(canvas:CanvasRenderingContext2D){for(constmeteorofthis.meteors){constgradientcanvas.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.strokeStylegradient;canvas.lineWidth2;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.alphastar.twinkleSpeed*star.twinkleDirection;if(star.alpha1||star.alpha0.2){star.twinkleDirection*-1;}canvas.fillStylergba(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){constarms4;conststarsPerArm200;for(letarm0;armarms;arm){for(leti0;istarsPerArm;i){constangle(arm/arms)*Math.PI*2(i/starsPerArm)*Math.PI*4;constradius(i/starsPerArm)*200;constxcenterXMath.cos(angle)*radius;constycenterYMath.sin(angle)*radius;canvas.fillStylergba(212, 168, 67,${Math.random()*0.50.2});canvas.beginPath();canvas.arc(x,y,Math.random()*1.50.5,0,Math.PI*2);canvas.fill();}}};八、玄象项目 Canvas 绘制规范总结8.1 Canvas 绘制四步法玄象项目 Canvas 绘制遵循四步法状态设置fillStyle/strokeStyle/lineWidth路径定义beginPath/moveTo/lineTo/arc填充或描边fill/stroke状态恢复可选保存与恢复状态8.2 Canvas 绘制规范表场景API 组合绘制矩形fillStylefillRect绘制圆形fillStylebeginPatharcfill绘制线段strokeStylelineWidthbeginPathmoveTolineTostroke绘制渐变createLinearGradientaddColorStop 赋值给fillStyle绘制文本fillStylefontfillText总结本篇以玄象项目drawStarField方法为蓝本深入剖析了 HarmonyOS Canvas 绘制的实现细节从CanvasRenderingContext2D核心 API、Math.random()随机散点算法、createLinearGradient银河渐变到绘制性能优化按 alpha 分组、Path2D 批量、离屏 Canvas与可复用性重构静态方法 接口配置。掌握这套 Canvas 绘制方法论您就能用纯代码绘制任何复杂视觉场景。下一篇《13 · Canvas 太极图算法阴阳半圆 鱼眼小圆的数学推导》将带您深入玄象项目启动页中央的太极图绘制算法。如果这篇文章对你有帮助欢迎点赞、收藏⭐、关注你的支持是我持续创作的动力相关资源HarmonyOS 官方文档CanvasRenderingContext2DHarmonyOS 官方文档Canvas 组件MDN Canvas APIdeveloper.mozilla.org/zh-CN/docs/Web/API/Canvas_API开源鸿蒙跨平台社区https://openharmonycrossplatform.csdn.net