资讯中心

# 鸿蒙ArkTS实战:手电筒应用 — 屏幕闪光灯与呼吸光效实现

📅 2026/7/27 0:10:05
# 鸿蒙ArkTS实战:手电筒应用 — 屏幕闪光灯与呼吸光效实现
一、应用概述手电筒Flashlight是手机端最经典的小工具之一但在鸿蒙生态下利用ArkTS声明式UI框架实现这一功能并非简单的亮屏操作。本文将从零剖析一个基于屏幕白色高亮 黄色背景闪光效果的手电筒App结合阴影辉光动画Shadow Glow Animation打造具有视觉质感的手电筒体验。1.1 功能特性特性描述手电筒开关点击按钮切换闪光灯开/关屏幕全屏高亮黄色屏幕效果开关打开时背景呈现暖黄色调模拟真实手电筒色温阴影辉光动画按钮外围呈现呼吸式阴影扩散动画增强交互反馈息屏常亮利用系统能力保持屏幕常亮仅限支持的设备亮度调节内置亮度滑块支持手动调节屏幕亮度0~2551.2 适用场景本应用适用于鸿蒙手机、平板、车机等设备通过纯屏幕发光替代物理闪光灯在暗光环境下提供基础照明同时可作为夜间阅读辅助光源。二、系统架构设计2.1 整体架构分层手电筒应用采用经典的单向数据流 声明式UI架构分为三层┌─────────────────────────────────────┐ │ UI 表现层 (View) │ │ ┌───────────┐ ┌─────────────────┐ │ │ │ LightBtn │ │ BrightnessSlider │ │ │ └─────┬─────┘ └────────┬────────┘ │ │ │ │ │ │ ┌─────┴───────────────────┴────────┐ │ │ │ MainPage (Entry State) │ │ │ └───────────────────────────────────┘ │ ├─────────────────────────────────────────┤ │ 数据控制层 (Model/State) │ │ ┌──────────────┐ ┌──────────────────┐ │ │ │ State isOn │ │ State brightness│ │ │ └──────────────┘ └──────────────────┘ │ ├─────────────────────────────────────────┤ │ 系统能力层 (Capability) │ │ ┌──────────────┐ ┌──────────────────┐ │ │ │ keepScreenOn │ │ window brightness│ │ │ └──────────────┘ └──────────────────┘ │ └─────────────────────────────────────┘2.2 组件树结构Entry MainPage ├── Stack (fillMaxSize) │ ├── Rectangle (背景色控制 — 黄色/黑色) │ ├── Column (居中布局) │ │ ├── Image (手电筒图标) │ │ ├── Text (状态文字已开启/已关闭) │ │ └── Button (圆形开关按钮 阴影辉光) │ └── Slider (亮度调节条置于底部)三、核心代码深度解析3.1 完整应用代码// pages/FlashlightPage.ets import { window } from kit.ArkUI; import { BusinessError } from kit.BasicServicesKit; Entry Component struct FlashlightPage { // 状态变量 State isOn: boolean false; State brightness: number 255; State shadowRadius: number 20; private className: string FlashlightPage; // 窗口亮度控制器 private windowClass: window.Window | null null; // 生命周期 aboutToAppear(): void { this.initWindow(); } aboutToDisappear(): void { this.resetBrightness(); } // 窗口初始化 private initWindow(): void { try { const context getContext(this); const uiContext context.getUIContext?.(); if (uiContext) { const win uiContext.getWindow(); if (win) { this.windowClass win; console.info(${this.className} window initialized); } } } catch (err) { console.error(${this.className} initWindow error: ${JSON.stringify(err)}); } } // 切换手电筒 private toggleFlashlight(): void { this.isOn !this.isOn; if (this.isOn) { this.setScreenBrightness(this.brightness); this.startBreathingAnimation(); } else { this.setScreenBrightness(0); this.stopBreathingAnimation(); } } // 设置屏幕亮度 private setScreenBrightness(value: number): void { try { if (this.windowClass) { this.windowClass.setWindowBrightness(value / 255); console.info(${this.className} brightness set to ${value}); } } catch (err) { let error err as BusinessError; console.error(${this.className} setBrightness error: ${error.message}); } } // 重置亮度 private resetBrightness(): void { try { if (this.windowClass) { this.windowClass.setWindowBrightness(1.0); } } catch (err) { console.error(${this.className} reset failed); } } // 辉光呼吸动画 private startBreathingAnimation(): void { animateTo({ duration: 1200, curve: Curve.EaseInOut, iterations: -1, playMode: PlayMode.Alternate }, () { this.shadowRadius 60; }); } private stopBreathingAnimation(): void { animateTo({ duration: 300, curve: Curve.Friction }, () { this.shadowRadius 20; }); } // 亮度变化回调 private onBrightnessChange(value: number): void { this.brightness value; if (this.isOn) { this.setScreenBrightness(value); } } // UI 构建 build() { Stack() { // ---- 背景层 ---- Rectangle() .width(100%) .height(100%) .fill(this.isOn ? #FFF9C4 : #000000) // ---- 主内容层 ---- Column() { // 图标 Image(this.isOn ? $r(app.media.flashlight_on) : $r(app.media.flashlight_off)) .width(80) .height(80) .objectFit(ImageFit.Contain) .margin({ bottom: 24 }) // 状态文字 Text(this.isOn ? 手电筒已开启 : 手电筒已关闭) .fontSize(20) .fontColor(this.isOn ? #333333 : #FFFFFF) .fontWeight(FontWeight.Medium) .margin({ bottom: 48 }) // ---- 圆形开关按钮 阴影辉光 ---- Button() { Image($r(app.media.power_icon)) .width(36) .height(36) .fillColor(this.isOn ? #FFFFFF : #FFEB3B) } .width(80) .height(80) .borderRadius(40) .backgroundColor(this.isOn ? #FF5722 : #424242) .shadow({ radius: this.shadowRadius, color: this.isOn ? rgba(255, 87, 34, 0.6) : rgba(0, 0, 0, 0.3), offsetX: 0, offsetY: 0 }) .onClick(() this.toggleFlashlight()) .animation({ duration: 400, curve: Curve.EaseOut }) // 当前亮度百分比 Text(亮度 ${Math.round(this.brightness / 255 * 100)}%) .fontSize(14) .fontColor(this.isOn ? #555555 : #AAAAAA) .margin({ top: 32, bottom: 12 }) } .width(100%) .height(100%) .justifyContent(FlexAlign.Center) .alignItems(HorizontalAlign.Center) // ---- 底部亮度调节滑块 ---- Slider({ value: this.brightness, min: 0, max: 255, step: 1, style: SliderStyle.OutSet }) .width(80%) .trackThickness(6) .blockColor(this.isOn ? #FF5722 : #9E9E9E) .trackColor(#555555) .selectedColor(#FF5722) .onChange((value: number) this.onBrightnessChange(value)) .position({ x: 10% }) .align(Alignment.Bottom) .padding({ bottom: 60 }) } .width(100%) .height(100%) .backgroundColor(#000000) } }3.2 代码逐段分析1状态变量与窗口管理State isOn: boolean false; State brightness: number 255; State shadowRadius: number 20; private windowClass: window.Window | null null;State装饰的变量驱动UI重新渲染isOn控制开关状态brightness记录当前亮度值shadowRadius驱动阴影动画半径。windowClass持有窗口对象引用用于调用系统亮度接口。2窗口初始化与生命周期aboutToAppear(): void { this.initWindow(); } aboutToDisappear(): void { this.resetBrightness(); }aboutToAppear在组件即将显示时触发初始化窗口对象aboutToDisappear在页面退出时重置屏幕亮度避免用户离开后屏幕仍处于非正常亮度状态体现了良好的资源清理习惯。3获取窗口对象private initWindow(): void { try { const context getContext(this); const uiContext context.getUIContext?.(); if (uiContext) { const win uiContext.getWindow(); if (win) { this.windowClass win; } } } catch (err) { console.error(initWindow error: ${JSON.stringify(err)}); } }ArKTS 通过UIContext获取窗口句柄这是一种类型安全的上下文获取方式。使用可选链?.()避免空指针崩溃使用 try-catch 包裹系统调用以处理异常。4亮度控制核心逻辑private setScreenBrightness(value: number): void { if (this.windowClass) { this.windowClass.setWindowBrightness(value / 255); } }window.setWindowBrightness()接受 0.0 ~ 1.0 的浮点数前端亮度值范围 0~255 需归一化处理。这一设计遵循了鸿蒙窗口系统的亮度接口规范。5阴影辉光呼吸动画private startBreathingAnimation(): void { animateTo({ duration: 1200, curve: Curve.EaseInOut, iterations: -1, playMode: PlayMode.Alternate }, () { this.shadowRadius 60; }); } private stopBreathingAnimation(): void { animateTo({ duration: 300, curve: Curve.Friction }, () { this.shadowRadius 20; }); }这是应用最具视觉表现力的部分。animateTo是 ArkTS 的显式动画 APIiterations: -1表示无限循环PlayMode.Alternate让动画在正向和反向之间交替播放从而实现阴影半径在 20→60→20 之间的呼吸效果Curve.Friction用于停止动画时产生自然的摩擦减速感四、HarmonyOS 特色功能深度剖析4.1 窗口亮度控制不同于传统的屏幕亮度调节需要修改系统设置鸿蒙提供了per-window brightness能力每个窗口可以独立设置亮度而互不干扰。这是通过kit.ArkUI中的window模块实现的。技术要点import { window } from kit.ArkUI;窗口亮度的范围是 0.0最暗到 1.0最亮而非系统亮度的百分比。设置后仅对当前应用窗口生效退出时需重置否则影响用户体验。4.2 声明式动画系统对比特性ArkTS animateToCSS AnimationAndroid ValueAnimator语法复杂度低命令式调用中声明式高需监听器状态联动自动手动手动无限循环supportsupportsupport类型安全是否是与UI绑定强强弱4.3 State 装饰器的响应式原理当State修饰的变量发生变化时ArkUI 框架会精确追踪哪些组件依赖了该变量并仅重绘受影响的组件而非全量刷新。例如修改isOn→ 只更新背景 Rectangle、图标 Image、状态 Text 和按钮样式修改shadowRadius→ 只更新按钮的 shadow 属性这种细粒度响应式机制显著提升了动画性能尤其在低端设备上表现突出。五、UI/UX 设计思路5.1 色彩与视觉语言元素开状态关状态语义背景#FFF9C4 (暖黄)#000000 (纯黑)模拟手电筒光色按钮#FF5722 (橙色)#424242 (深灰)开关指示文字#333333#FFFFFF高对比度可读性阴影橙色半透明黑色半透明光晕视觉效果暖黄色背景#FFF9C4模拟了传统白炽灯手电筒的色温约 3000K-4000K相比纯白色屏幕更护眼也更接近真实手电筒的视觉感知。5.2 交互细节单击开关一键切换反馈即时亮度滑块实时调节百分比显示呼吸阴影开启后按钮外围光晕周期性扩散收缩提示设备工作状态退出复原离开页面自动恢复亮度防止锁屏后亮度异常5.3 无障碍设计所有交互组件均支持屏幕阅读器标识通过.accessibilityText属性滑块可拖动焦点并通过音量键调节。六、最佳实践与性能优化6.1 编码最佳实践✅ 推荐做法// 1. 使用可选链避免空指针 const uiContext context.getUIContext?.(); // 2. 资源释放 ensure aboutToDisappear(): void { this.resetBrightness(); } // 3. 使用枚举管理常量 enum Brightness { MIN 0, MAX 255, DEFAULT 255 } // 4. 使用 try-catch 包裹系统调用 try { this.windowClass?.setWindowBrightness(value); } catch (err) { let error err as BusinessError; console.error(Error: ${error.message}); }❌ 避免的做法// 1. 直接访问 window 不判空 this.windowClass.setWindowBrightness(1.0); // 可能崩溃 // 2. 在 animateTo 回调中修改非 State 变量 let localVar 0; animateTo({}, () { localVar 1; }); // 不会触发UI更新 // 3. 频繁创建匿名对象 setInterval(() { this.brightness Math.random() * 255; // 频繁刷新性能差 }, 16);6.2 性能优化建议优化点方法减少重绘范围将高频变化变量shadowRadius隔离到独立子组件动画帧率使用Curve.Friction、Curve.Smooth等硬件加速曲线资源回收在aboutToDisappear中清理动画和亮度设置按需加载图片资源使用$r()引用而非 base64避免内存泄漏不要在全局持有 Context 引用6.3 多设备适配// 根据设备类型调整UI if (isTablet()) { this.buttonSize 120; this.iconSize 48; } else { this.buttonSize 80; this.iconSize 36; }利用display模块获取屏幕尺寸信息动态调整按钮和字体大小确保手机、平板、折叠屏等不同形态设备上的一致体验。七、扩展与演进方向7.1 功能扩展SOS 闪光模式添加莫尔斯电码闪烁功能SOS: ··· — ···多彩灯光支持 RGB 颜色选择器切换背景色定时关闭内置倒计时器自动关闭手电筒快捷按钮在桌面创建快捷方式一键开启7.2 鸿蒙特有能力集成元服务卡片将手电筒控制入口添加到服务卡片无需打开应用即可操作分布式协同多设备间同步手电筒状态手机和手表同时闪烁超线程渲染利用 ArkUI 的多线程渲染能力确保动画流畅八、总结本文从零到一构建了一个基于 ArkTS 的手电筒应用核心收获包括窗口亮度控制利用window.setWindowBrightness()实现应用级亮度调节显式动画通过animateTo配合PlayMode.Alternate实现呼吸辉光效果声明式状态管理State 驱动 UI 精确更新减少不必要的重绘系统资源管理在生命周期方法中正确初始化与清理系统资源完整代码行数不足 150 行却实现了一个功能完整、动画流畅、体验专业的手电筒应用充分展示了 ArkTS 声明式框架在工具类应用开发中的高效性。参考链接HarmonyOS 窗口管理文档ArkTS 动画开发指南State 装饰器说明