HarmonyOS 6智能家居实战:基于悬浮导航与沉浸光感的“光影智家“全屋智能控制系统

文章目录

    • 每日一句正能量
    • [一、前言:当智能家居遇上HarmonyOS 6新特性](#一、前言:当智能家居遇上HarmonyOS 6新特性)
    • 二、智能家居场景下的特性适配设计
      • [2.1 空间-导航-光效映射体系](#2.1 空间-导航-光效映射体系)
      • [2.2 设备状态-光效编码协议](#2.2 设备状态-光效编码协议)
    • 三、核心组件实战
      • [3.1 空间感知悬浮导航(SpatialFloatNav.ets)](#3.1 空间感知悬浮导航(SpatialFloatNav.ets))
      • [3.2 设备控制光效面板(DeviceControlPanel.ets)](#3.2 设备控制光效面板(DeviceControlPanel.ets))
      • [3.3 场景切换沉浸过渡(SceneTransition.ets)](#3.3 场景切换沉浸过渡(SceneTransition.ets))
    • 四、主页面集成
      • [4.1 智能家居主页面(SmartHomePage.ets)](#4.1 智能家居主页面(SmartHomePage.ets))
    • 五、关键技术总结
      • [5.1 空间-导航-光效协同架构](#5.1 空间-导航-光效协同架构)
      • [5.2 设备状态可视化编码](#5.2 设备状态可视化编码)
      • [5.3 分布式设备同步](#5.3 分布式设备同步)
    • 六、调试与适配建议
    • 七、结语

每日一句正能量

放弃很容易,但坚持一定很酷。在想放弃的那一刻,想想当初为什么出发。成功往往就躲在"再坚持一下"的拐角处。咬紧牙关,跨过那道坎,你会感谢那个死磕到底的自己。早上好!

一、前言:当智能家居遇上HarmonyOS 6新特性

随着HarmonyOS生态在IoT领域的深度布局,智能家居控制应用正从"功能堆砌"向"场景化体验"演进。HarmonyOS 6(API 23)带来的**悬浮导航(Float Navigation)沉浸光感(Immersive Light Effects)**特性,为智能家居控制提供了全新的交互维度。

传统智能家居APP采用固定的底部Tab导航,在控制多设备时需要频繁切换页面,打断操作流。HarmonyOS 6的悬浮导航允许控制面板以"空间悬浮"的方式叠加在场景视图上,配合沉浸光感实现"房间即界面"------客厅暖光、卧室柔光、书房冷光,每个空间都有独特的视觉氛围,设备状态通过光效直观呈现。

本文将构建一款名为**"光影智家"**的全屋智能控制中心,展示如何:

  • 空间感知悬浮导航:根据当前所在房间自动切换导航内容和光效主题
  • 设备状态光效编码:灯光亮度、空调温度、窗帘开合度转化为可视化光效
  • 场景化沉浸控制:"观影模式""阅读模式"等场景一键切换,全屏光效过渡
  • 多设备协同光效:多个HarmonyOS设备间的控制状态同步与光效联动

二、智能家居场景下的特性适配设计

2.1 空间-导航-光效映射体系

房间类型 主色调 导航位置 光效特征 设备优先级
客厅 暖橙 #F5A623 底部悬浮 温馨呼吸光 灯光/电视/空调
卧室 柔紫 #9B59B6 侧边迷你 舒缓渐变 灯光/窗帘/净化器
书房 冷蓝 #4A90E2 右侧轨道 专注稳定 台灯/电脑/音响
厨房 明黄 #FFE66D 顶部快捷 明亮清晰 烟机/冰箱/灯具
浴室 青绿 #4ECDC4 底部防水 清新脉冲 浴霸/热水器/除湿

2.2 设备状态-光效编码协议

将设备运行状态映射为光效参数,实现"一眼即知":

typescript 复制代码
// 设备状态-光效映射
export const DeviceStateLightMap = {
  light: {
    off: { color: '#333333', pulse: 0, intensity: 0 },
    dim: { color: '#FFE4B5', pulse: 8000, intensity: 0.3 },
    normal: { color: '#FFF8DC', pulse: 6000, intensity: 0.6 },
    bright: { color: '#FFFFFF', pulse: 4000, intensity: 1.0 }
  },
  ac: {
    off: { color: '#666666', pulse: 0, intensity: 0 },
    cool: { color: '#00D2FF', pulse: 3000, intensity: 0.7 },
    heat: { color: '#FF6B6B', pulse: 3000, intensity: 0.7 },
    auto: { color: '#2ECC71', pulse: 5000, intensity: 0.5 }
  },
  curtain: {
    closed: { color: '#2C3E50', pulse: 0, intensity: 0.2 },
    half: { color: '#F39C12', pulse: 6000, intensity: 0.4 },
    open: { color: '#F1C40F', pulse: 4000, intensity: 0.8 }
  }
};

三、核心组件实战

3.1 空间感知悬浮导航(SpatialFloatNav.ets)

代码亮点:这是本应用的核心创新组件。它通过HarmonyOS分布式能力感知用户当前所在房间(通过路由器信号强度、设备蓝牙信标或手机定位),自动切换导航内容和光效主题。导航形态根据房间功能动态调整------客厅底部展开、卧室侧边迷你、书房右侧轨道,实现"空间即导航"的沉浸体验。

typescript 复制代码
// entry/src/main/ets/components/SpatialFloatNav.ets
import { distributedDeviceManager } from '@kit.DistributedServiceKit';
import { geolocation } from '@kit.LocationKit';

// 房间类型
export enum RoomType {
  LIVING_ROOM = 'living_room',
  BEDROOM = 'bedroom',
  STUDY = 'study',
  KITCHEN = 'kitchen',
  BATHROOM = 'bathroom',
  BALCONY = 'balcony'
}

// 房间配置
interface RoomConfig {
  type: RoomType;
  name: string;
  primaryColor: string;
  ambientColor: string;
  glowColor: string;
  navPosition: 'bottom' | 'left' | 'right' | 'top';
  navSize: number;
  devicePriority: string[];
}

// 导航项
interface RoomNavItem {
  id: string;
  icon: Resource;
  label: string;
  deviceType: string;
  roomId: string;
  isActive: boolean;
  quickActions: string[];
}

@Component
export struct SpatialFloatNav {
  // 回调
  onDeviceSelect?: (device: RoomNavItem) => void;
  onSceneSelect?: (scene: string) => void;
  onRoomChange?: (room: RoomConfig) => void;
  
  @State currentRoom: RoomConfig = this.getRoomConfig(RoomType.LIVING_ROOM);
  @State detectedRoom: RoomType = RoomType.LIVING_ROOM;
  @State navItems: RoomNavItem[] = [];
  @State isExpanded: boolean = true;
  @State currentLightColor: string = '#F5A623';
  @State ambientIntensity: number = 0.6;
  @State showRoomSwitcher: boolean = false;
  @State activeDeviceId: string = '';
  
  // 房间配置库
  private roomConfigs: Map<RoomType, RoomConfig> = new Map([
    [RoomType.LIVING_ROOM, {
      type: RoomType.LIVING_ROOM,
      name: '客厅',
      primaryColor: '#F5A623',
      ambientColor: '#5C3D1E',
      glowColor: '#FFD93D',
      navPosition: 'bottom',
      navSize: 80,
      devicePriority: ['light', 'tv', 'ac', 'curtain', 'speaker']
    }],
    [RoomType.BEDROOM, {
      type: RoomType.BEDROOM,
      name: '卧室',
      primaryColor: '#9B59B6',
      ambientColor: '#4A235A',
      glowColor: '#E056FD',
      navPosition: 'left',
      navSize: 72,
      devicePriority: ['light', 'curtain', 'purifier', 'humidifier', 'alarm']
    }],
    [RoomType.STUDY, {
      type: RoomType.STUDY,
      name: '书房',
      primaryColor: '#4A90E2',
      ambientColor: '#1E3A5F',
      glowColor: '#6BB6FF',
      navPosition: 'right',
      navSize: 64,
      devicePriority: ['desk_lamp', 'computer', 'speaker', 'ac', 'monitor']
    }],
    [RoomType.KITCHEN, {
      type: RoomType.KITCHEN,
      name: '厨房',
      primaryColor: '#FFE66D',
      ambientColor: '#5C4D1E',
      glowColor: '#FFF5B7',
      navPosition: 'top',
      navSize: 72,
      devicePriority: ['hood', 'fridge', 'light', 'oven', 'kettle']
    }],
    [RoomType.BATHROOM, {
      type: RoomType.BATHROOM,
      name: '浴室',
      primaryColor: '#4ECDC4',
      ambientColor: '#1E4D4A',
      glowColor: '#7EDDD6',
      navPosition: 'bottom',
      navSize: 64,
      devicePriority: ['heater', 'water_heater', 'dehumidifier', 'light', 'fan']
    }]
  ]);

  // 设备数据(模拟)
  private deviceDatabase: RoomNavItem[] = [
    { id: 'light_01', icon: $r('app.media.ic_light'), label: '主灯', deviceType: 'light', roomId: 'living_room', isActive: true, quickActions: ['on', 'off', 'dim', 'bright'] },
    { id: 'tv_01', icon: $r('app.media.ic_tv'), label: '电视', deviceType: 'tv', roomId: 'living_room', isActive: false, quickActions: ['on', 'off', 'volume'] },
    { id: 'ac_01', icon: $r('app.media.ic_ac'), label: '空调', deviceType: 'ac', roomId: 'living_room', isActive: true, quickActions: ['cool', 'heat', 'auto', 'off'] },
    { id: 'curtain_01', icon: $r('app.media.ic_curtain'), label: '窗帘', deviceType: 'curtain', roomId: 'living_room', isActive: false, quickActions: ['open', 'half', 'close'] },
    { id: 'speaker_01', icon: $r('app.media.ic_speaker'), label: '音响', deviceType: 'speaker', roomId: 'living_room', isActive: false, quickActions: ['play', 'pause', 'volume'] },
    { id: 'light_02', icon: $r('app.media.ic_light'), label: '床头灯', deviceType: 'light', roomId: 'bedroom', isActive: true, quickActions: ['on', 'off', 'dim', 'bright'] },
    { id: 'curtain_02', icon: $r('app.media.ic_curtain'), label: '窗帘', deviceType: 'curtain', roomId: 'bedroom', isActive: false, quickActions: ['open', 'half', 'close'] },
    { id: 'purifier_01', icon: $r('app.media.ic_purifier'), label: '净化器', deviceType: 'purifier', roomId: 'bedroom', isActive: true, quickActions: ['auto', 'sleep', 'turbo', 'off'] }
  ];

  aboutToAppear(): void {
    this.startRoomDetection();
    this.loadRoomDevices(this.currentRoom.type);
  }

  // 启动房间检测(分布式设备感知)
  private startRoomDetection(): void {
    // 方法1:通过分布式设备信号强度检测
    try {
      const devices = distributedDeviceManager.getAvailableDeviceListSync();
      // 分析设备信号强度判断最近房间
      this.analyzeDeviceProximity(devices);
    } catch (error) {
      console.error('Device detection failed:', error);
    }

    // 方法2:定时检测房间变化
    setInterval(() => {
      this.detectCurrentRoom();
    }, 10000);
  }

  private analyzeDeviceProximity(devices: any[]): void {
    // 简化实现:根据设备ID前缀判断房间
    // 实际应基于RSSI信号强度计算
    const roomSignals: Map<RoomType, number> = new Map();
    
    devices.forEach(device => {
      const deviceName = device.deviceName || '';
      if (deviceName.includes('living')) {
        roomSignals.set(RoomType.LIVING_ROOM, (roomSignals.get(RoomType.LIVING_ROOM) || 0) + 1);
      } else if (deviceName.includes('bedroom')) {
        roomSignals.set(RoomType.BEDROOM, (roomSignals.get(RoomType.BEDROOM) || 0) + 1);
      }
      // ... 其他房间
    });

    // 选择信号最强的房间
    let maxSignal = 0;
    let detectedRoom = RoomType.LIVING_ROOM;
    roomSignals.forEach((signal, room) => {
      if (signal > maxSignal) {
        maxSignal = signal;
        detectedRoom = room;
      }
    });

    if (detectedRoom !== this.detectedRoom) {
      this.detectedRoom = detectedRoom;
      this.switchRoom(detectedRoom);
    }
  }

  private detectCurrentRoom(): void {
    // 模拟房间检测(实际应结合多种传感器)
    const rooms = Array.from(this.roomConfigs.keys());
    const randomRoom = rooms[Math.floor(Math.random() * rooms.length)];
    
    if (randomRoom !== this.detectedRoom) {
      this.detectedRoom = randomRoom;
      this.switchRoom(randomRoom);
    }
  }

  private switchRoom(roomType: RoomType): void {
    const config = this.roomConfigs.get(roomType);
    if (config) {
      this.currentRoom = config;
      this.currentLightColor = config.primaryColor;
      this.loadRoomDevices(roomType);
      this.onRoomChange?.(config);
      
      // 同步光效
      AppStorage.setOrCreate('current_room_color', config.primaryColor);
      AppStorage.setOrCreate('room_changed', Date.now());
    }
  }

  private loadRoomDevices(roomType: RoomType): void {
    const roomId = roomType.toString();
    this.navItems = this.deviceDatabase.filter(d => d.roomId === roomId);
    
    // 按房间优先级排序
    const priority = this.currentRoom.devicePriority;
    this.navItems.sort((a, b) => {
      return priority.indexOf(a.deviceType) - priority.indexOf(b.deviceType);
    });
  }

  build() {
    Stack({ alignContent: Alignment.Bottom }) {
      // 内容层
      this.contentBuilder()

      // 房间切换器(顶部)
      if (this.showRoomSwitcher) {
        this.buildRoomSwitcher()
      }

      // 空间感知导航层
      if (this.currentRoom.navPosition === 'bottom') {
        this.buildBottomNav()
      } else if (this.currentRoom.navPosition === 'left') {
        this.buildLeftNav()
      } else if (this.currentRoom.navPosition === 'right') {
        this.buildRightNav()
      } else if (this.currentRoom.navPosition === 'top') {
        this.buildTopNav()
      }

      // 环境光效层
      this.buildAmbientLightLayer()
    }
    .width('100%')
    .height('100%')
  }

  @Builder
  contentBuilder(): void {
    Column() {
      Text('智能家居控制区域')
        .fontSize(16)
        .fontColor('rgba(255,255,255,0.3)')
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }

  // 底部导航(客厅/浴室)
  @Builder
  buildBottomNav(): void {
    Column() {
      // 房间指示器(可点击切换)
      Row() {
        Text(this.currentRoom.name)
          .fontSize(12)
          .fontColor(this.currentLightColor)
          .fontWeight(FontWeight.Medium)
        
        Image($r('app.media.ic_chevron_up'))
          .width(16)
          .height(16)
          .fillColor(this.currentLightColor)
          .rotate({ angle: this.showRoomSwitcher ? 180 : 0 })
          .animation({ duration: 200 })
      }
      .height(32)
      .padding({ left: 16, right: 16 })
      .backgroundColor('rgba(30, 30, 40, 0.6)')
      .backdropFilter($r('sys.blur.15'))
      .borderRadius(16)
      .onClick(() => {
        this.showRoomSwitcher = !this.showRoomSwitcher;
      })
      .margin({ bottom: 8 })

      // 设备导航栏
      Column() {
        Row({ space: 12 }) {
          ForEach(this.navItems, (item: RoomNavItem) => {
            this.buildDeviceButton(item)
          })
          
          // 场景快捷按钮
          this.buildSceneButton()
        }
        .width('100%')
        .height(72)
        .padding({ left: 16, right: 16 })
        .justifyContent(FlexAlign.SpaceAround)
      }
      .width('94%')
      .height(80)
      .backgroundColor('rgba(30, 30, 40, 0.8)')
      .backdropFilter($r('sys.blur.25'))
      .borderRadius(24)
      .shadow({
        radius: 20,
        color: 'rgba(0, 0, 0, 0.3)',
        offsetX: 0,
        offsetY: -4
      })
    }
    .width('100%')
    .height(120)
    .padding({ bottom: 12 })
    .animation({
      duration: 300,
      curve: Curve.EaseInOut
    })
  }

  // 左侧导航(卧室)
  @Builder
  buildLeftNav(): void {
    Row() {
      Column() {
        // 房间标识
        Column() {
          Text(this.currentRoom.name.substring(0, 1))
            .fontSize(20)
            .fontWeight(FontWeight.Bold)
            .fontColor(this.currentLightColor)
        }
        .width(48)
        .height(48)
        .backgroundColor(`${this.currentLightColor}20`)
        .borderRadius(24)
        .margin({ bottom: 16 })
        .onClick(() => {
          this.showRoomSwitcher = !this.showRoomSwitcher;
        })

        // 设备列表
        List({ space: 8 }) {
          ForEach(this.navItems, (item: RoomNavItem) => {
            ListItem() {
              this.buildDeviceIcon(item)
            }
          })
        }
        .width('100%')
        .layoutWeight(1)

        // 场景按钮
        Button() {
          Image($r('app.media.ic_scene'))
            .width(24)
            .height(24)
            .fillColor(this.currentLightColor)
        }
        .type(ButtonType.Circle)
        .backgroundColor(`${this.currentLightColor}20`)
        .width(48)
        .height(48)
        .onClick(() => {
          this.showScenePanel();
        })
      }
      .width(this.isExpanded ? 200 : 64)
      .height('70%')
      .backgroundColor('rgba(30, 30, 40, 0.8)')
      .backdropFilter($r('sys.blur.25'))
      .borderRadius({ topRight: 24, bottomRight: 24 })
      .padding(12)
      .animation({
        duration: 250,
        curve: Curve.EaseInOut
      })
      .onHover((isHover: boolean) => {
        this.isExpanded = isHover;
      })
    }
    .width(this.isExpanded ? 200 : 64)
    .height('100%')
    .position({ x: 0, y: 0 })
    .padding({ top: 100, bottom: 100 })
  }

  // 右侧导航(书房)
  @Builder
  buildRightNav(): void {
    Column() {
      Column() {
        // 设备图标列表
        List({ space: 12 }) {
          ForEach(this.navItems, (item: RoomNavItem) => {
            ListItem() {
              this.buildDeviceIcon(item)
            }
          })
        }
        .width('100%')
        .layoutWeight(1)

        // 房间切换
        Button() {
          Text(this.currentRoom.name.substring(0, 1))
            .fontSize(16)
            .fontWeight(FontWeight.Bold)
            .fontColor(this.currentLightColor)
        }
        .type(ButtonType.Circle)
        .backgroundColor(`${this.currentLightColor}20`)
        .width(44)
        .height(44)
        .onClick(() => {
          this.showRoomSwitcher = !this.showRoomSwitcher;
        })
      }
      .width(60)
      .height('60%')
      .backgroundColor('rgba(30, 30, 40, 0.75)')
      .backdropFilter($r('sys.blur.20'))
      .borderRadius({ topLeft: 20, bottomLeft: 20 })
      .padding({ top: 16, bottom: 16 })
      .shadow({
        radius: 20,
        color: 'rgba(0, 0, 0, 0.25)',
        offsetX: -4,
        offsetY: 0
      })
    }
    .width(60)
    .height('100%')
    .position({ x: '100%', y: 0 })
    .anchor('100%')
    .padding({ top: 80, bottom: 80 })
  }

  // 顶部导航(厨房)
  @Builder
  buildTopNav(): void {
    Column() {
      Row({ space: 16 }) {
        // 房间标识
        Row({ space: 8 }) {
          Image($r('app.media.ic_home'))
            .width(20)
            .height(20)
            .fillColor(this.currentLightColor)
          
          Text(this.currentRoom.name)
            .fontSize(16)
            .fontWeight(FontWeight.Medium)
            .fontColor(this.currentLightColor)
        }
        .onClick(() => {
          this.showRoomSwitcher = !this.showRoomSwitcher;
        })

        // 设备快捷按钮
        ForEach(this.navItems, (item: RoomNavItem) => {
          this.buildDeviceChip(item)
        })

        // 场景按钮
        Button('场景')
          .height(36)
          .backgroundColor(`${this.currentLightColor}20`)
          .fontColor(this.currentLightColor)
          .fontSize(13)
          .borderRadius(18)
          .onClick(() => {
            this.showScenePanel();
          })
      }
      .width('100%')
      .height(64)
      .padding({ left: 16, right: 16 })
      .backgroundColor('rgba(30, 30, 40, 0.8)')
      .backdropFilter($r('sys.blur.25'))
      .borderRadius({ bottomLeft: 20, bottomRight: 20 })
      .shadow({
        radius: 15,
        color: 'rgba(0, 0, 0, 0.2)',
        offsetX: 0,
        offsetY: 4
      })
    }
    .width('100%')
    .height(80)
    .position({ x: 0, y: 0 })
    .padding({ top: 40 })
  }

  // 设备按钮(底部导航用)
  @Builder
  buildDeviceButton(item: RoomNavItem): void {
    Column() {
      Stack() {
        // 激活光效
        if (item.isActive) {
          Column()
            .width(48)
            .height(48)
            .backgroundColor(this.currentLightColor)
            .opacity(0.2)
            .blur(10)
            .borderRadius(24)
        }

        Image(item.icon)
          .width(24)
          .height(24)
          .fillColor(item.isActive ? this.currentLightColor : '#B0B0B0')
      }
      .width(48)
      .height(48)

      Text(item.label)
        .fontSize(10)
        .fontColor(item.isActive ? '#FFFFFF' : 'rgba(255,255,255,0.6)')
        .margin({ top: 2 })
    }
    .onClick(() => {
      this.activeDeviceId = item.id;
      this.onDeviceSelect?.(item);
    })
    .onLongPress(() => {
      this.showQuickActions(item);
    })
  }

  // 设备图标(侧边导航用)
  @Builder
  buildDeviceIcon(item: RoomNavItem): void {
    Stack() {
      if (item.isActive) {
        Column()
          .width(44)
          .height(44)
          .backgroundColor(this.currentLightColor)
          .opacity(0.2)
          .blur(8)
          .borderRadius(22)
      }

      Image(item.icon)
        .width(22)
        .height(22)
        .fillColor(item.isActive ? this.currentLightColor : '#B0B0B0')
    }
    .width(44)
    .height(44)
    .onClick(() => {
      this.activeDeviceId = item.id;
      this.onDeviceSelect?.(item);
    })
  }

  // 设备芯片(顶部导航用)
  @Builder
  buildDeviceChip(item: RoomNavItem): void {
    Row({ space: 6 }) {
      Image(item.icon)
        .width(16)
        .height(16)
        .fillColor(item.isActive ? this.currentLightColor : '#B0B0B0')
      
      if (item.isActive) {
        Text(item.label)
          .fontSize(12)
          .fontColor('#FFFFFF')
      }
    }
    .height(32)
    .padding({ left: 10, right: 10 })
    .backgroundColor(item.isActive ? `${this.currentLightColor}30` : 'rgba(255,255,255,0.05)')
    .borderRadius(16)
    .onClick(() => {
      this.activeDeviceId = item.id;
      this.onDeviceSelect?.(item);
    })
  }

  // 场景按钮
  @Builder
  buildSceneButton(): void {
    Column() {
      Stack() {
        Column()
          .width(48)
          .height(48)
          .backgroundColor('linear-gradient(135deg, #667eea 0%, #764ba2 100%)')
          .opacity(0.3)
          .blur(10)
          .borderRadius(24)

        Image($r('app.media.ic_magic'))
          .width(24)
          .height(24)
          .fillColor('#FFFFFF')
      }
      .width(48)
      .height(48)

      Text('场景')
        .fontSize(10)
        .fontColor('rgba(255,255,255,0.8)')
        .margin({ top: 2 })
    }
    .onClick(() => {
      this.showScenePanel();
    })
  }

  // 房间切换器
  @Builder
  buildRoomSwitcher(): void {
    Column() {
      Row({ space: 12 }) {
        ForEach(Array.from(this.roomConfigs.values()), (config: RoomConfig) => {
          Column({ space: 8 }) {
            Column()
              .width(56)
              .height(56)
              .backgroundColor(config.primaryColor)
              .opacity(0.2)
              .borderRadius(16)
              .border({
                width: config.type === this.currentRoom.type ? 2 : 0,
                color: config.primaryColor
              })

            Text(config.name)
              .fontSize(12)
              .fontColor(config.type === this.currentRoom.type ? config.primaryColor : 'rgba(255,255,255,0.6)')
          }
          .onClick(() => {
            this.switchRoom(config.type);
            this.showRoomSwitcher = false;
          })
        })
      }
      .width('100%')
      .height(100)
      .padding(16)
      .backgroundColor('rgba(30, 30, 40, 0.9)')
      .backdropFilter($r('sys.blur.30'))
      .borderRadius(20)
      .shadow({
        radius: 20,
        color: 'rgba(0, 0, 0, 0.4)',
        offsetX: 0,
        offsetY: 10
      })
    }
    .width('92%')
    .position({ x: '50%', y: 100 })
    .anchor('50%')
    .animation({
      duration: 300,
      curve: Curve.Spring
    })
  }

  // 环境光效层
  @Builder
  buildAmbientLightLayer(): void {
    Column() {
      // 顶部环境光
      Column()
        .width('100%')
        .height(200)
        .backgroundColor(this.currentLightColor)
        .blur(120)
        .opacity(this.ambientIntensity * 0.15)
        .position({ x: 0, y: 0 })

      // 底部环境光
      Column()
        .width('100%')
        .height(150)
        .backgroundColor(this.currentLightColor)
        .blur(100)
        .opacity(this.ambientIntensity * 0.1)
        .position({ x: 0, y: '100%' })
        .anchor('100%')

      // 设备状态光效粒子
      ForEach(this.navItems.filter(i => i.isActive), (item: RoomNavItem, index: number) => {
        Column()
          .width(80 + index * 20)
          .height(80 + index * 20)
          .backgroundColor(this.getDeviceLightColor(item))
          .blur(60)
          .opacity(0.2)
          .position({
            x: `${20 + index * 15}%`,
            y: `${30 + index * 10}%`
          })
          .animation({
            duration: 4000 + index * 1000,
            curve: Curve.EaseInOut,
            iterations: -1,
            playMode: PlayMode.Alternate
          })
      })
    }
    .width('100%')
    .height('100%')
    .pointerEvents(PointerEvents.None)
  }

  private getDeviceLightColor(item: RoomNavItem): string {
    const stateMap: Record<string, Record<string, string>> = {
      light: { on: '#FFE4B5', off: '#333333' },
      ac: { cool: '#00D2FF', heat: '#FF6B6B', auto: '#2ECC71' },
      curtain: { open: '#F1C40F', half: '#F39C12', close: '#2C3E50' }
    };
    return stateMap[item.deviceType]?.on || this.currentLightColor;
  }

  private showQuickActions(item: RoomNavItem): void {
    // 显示快捷操作面板
    AppStorage.setOrCreate('quick_actions_device', JSON.stringify(item));
    AppStorage.setOrCreate('show_quick_actions', true);
  }

  private showScenePanel(): void {
    // 显示场景面板
    AppStorage.setOrCreate('show_scene_panel', true);
  }
}

3.2 设备控制光效面板(DeviceControlPanel.ets)

代码亮点:实现"设备即光效"的控制理念。每个设备的调节滑块、开关按钮都带有动态光效反馈------调节灯光亮度时,背景光效实时同步变化;调节空调温度时,冷暖气色渐变呈现。这种即时视觉反馈让用户"看见"控制效果,而非仅依赖数字。

typescript 复制代码
// entry/src/main/ets/components/DeviceControlPanel.ets

// 设备控制项
interface ControlItem {
  id: string;
  type: 'slider' | 'switch' | 'button_group' | 'color_picker';
  label: string;
  value: number | boolean | string;
  min?: number;
  max?: number;
  step?: number;
  unit?: string;
  options?: string[];
  icon: Resource;
}

@Component
export struct DeviceControlPanel {
  @Prop deviceName: string = '';
  @Prop deviceType: string = 'light';
  @State controls: ControlItem[] = [];
  @State panelColor: string = '#F5A623';
  @State lightIntensity: number = 0.6;
  @State isOn: boolean = true;

  aboutToAppear(): void {
    this.initializeControls();
    this.syncPanelColor();
  }

  private initializeControls(): void {
    // 根据设备类型初始化控制项
    switch (this.deviceType) {
      case 'light':
        this.controls = [
          { id: 'power', type: 'switch', label: '开关', value: true, icon: $r('app.media.ic_power') },
          { id: 'brightness', type: 'slider', label: '亮度', value: 80, min: 0, max: 100, step: 1, unit: '%', icon: $r('app.media.ic_brightness') },
          { id: 'color_temp', type: 'slider', label: '色温', value: 4000, min: 2700, max: 6500, step: 100, unit: 'K', icon: $r('app.media.ic_temperature') },
          { id: 'color', type: 'color_picker', label: '颜色', value: '#FFE4B5', icon: $r('app.media.ic_color') }
        ];
        break;
      case 'ac':
        this.controls = [
          { id: 'power', type: 'switch', label: '开关', value: true, icon: $r('app.media.ic_power') },
          { id: 'temperature', type: 'slider', label: '温度', value: 24, min: 16, max: 30, step: 0.5, unit: '°C', icon: $r('app.media.ic_temperature') },
          { id: 'mode', type: 'button_group', label: '模式', value: 'cool', options: ['cool', 'heat', 'dry', 'fan', 'auto'], icon: $r('app.media.ic_mode') },
          { id: 'fan_speed', type: 'slider', label: '风速', value: 2, min: 1, max: 5, step: 1, unit: '档', icon: $r('app.media.ic_fan') }
        ];
        break;
      case 'curtain':
        this.controls = [
          { id: 'position', type: 'slider', label: '开合度', value: 50, min: 0, max: 100, step: 1, unit: '%', icon: $r('app.media.ic_curtain') },
          { id: 'preset', type: 'button_group', label: '预设', value: 'half', options: ['open', 'half', 'close'], icon: $r('app.media.ic_preset') }
        ];
        break;
    }
  }

  private syncPanelColor(): void {
    // 同步房间主色
    const roomColor = AppStorage.get('current_room_color') as string;
    if (roomColor) {
      this.panelColor = roomColor;
    }
  }

  private getControlColor(control: ControlItem): string {
    if (control.id === 'temperature' && this.deviceType === 'ac') {
      const temp = control.value as number;
      // 温度色温映射:低温蓝->高温红
      if (temp < 20) return '#00D2FF';
      if (temp < 24) return '#2ECC71';
      if (temp < 26) return '#F5A623';
      return '#FF6B6B';
    }
    if (control.id === 'brightness' && this.deviceType === 'light') {
      const brightness = control.value as number;
      return `rgba(255, 228, 181, ${brightness / 100})`;
    }
    return this.panelColor;
  }

  build() {
    Column({ space: 20 }) {
      // 面板头部
      this.buildPanelHeader()

      // 设备状态光效预览
      this.buildDevicePreview()

      // 控制项列表
      List({ space: 16 }) {
        ForEach(this.controls, (control: ControlItem) => {
          ListItem() {
            this.buildControlItem(control)
          }
        })
      }
      .width('100%')
      .layoutWeight(1)

      // 底部快捷场景
      this.buildQuickScenes()
    }
    .width('100%')
    .height('100%')
    .padding(24)
    .backgroundColor('rgba(20, 20, 30, 0.95)')
    .backdropFilter($r('sys.blur.30'))
    .borderRadius({ topLeft: 24, topRight: 24 })
  }

  @Builder
  buildPanelHeader(): void {
    Row({ space: 12 }) {
      // 设备图标(带光效)
      Stack() {
        Column()
          .width(48)
          .height(48)
          .backgroundColor(this.panelColor)
          .opacity(0.2)
          .blur(10)
          .borderRadius(24)

        Image($r('app.media.ic_device'))
          .width(24)
          .height(24)
          .fillColor(this.panelColor)
      }
      .width(48)
      .height(48)

      Column({ space: 4 }) {
        Text(this.deviceName)
          .fontSize(18)
          .fontWeight(FontWeight.Bold)
          .fontColor('#FFFFFF')

        Text(this.isOn ? '运行中' : '已关闭')
          .fontSize(12)
          .fontColor(this.isOn ? this.panelColor : 'rgba(255,255,255,0.4)')
      }
      .alignItems(HorizontalAlign.Start)
      .layoutWeight(1)

      // 关闭面板按钮
      Button() {
        Image($r('app.media.ic_close'))
          .width(20)
          .height(20)
          .fillColor('rgba(255,255,255,0.6)')
      }
      .type(ButtonType.Circle)
      .backgroundColor('rgba(255,255,255,0.05)')
      .width(36)
      .height(36)
    }
    .width('100%')
    .height(60)
  }

  @Builder
  buildDevicePreview(): void {
    Column() {
      // 设备光效预览
      Column() {
        // 模拟灯光效果
        Column()
          .width(120)
          .height(120)
          .backgroundColor(this.isOn ? this.panelColor : '#333333')
          .blur(this.isOn ? 40 : 0)
          .opacity(this.isOn ? 0.6 : 0.3)
          .borderRadius(60)
          .shadow({
            radius: this.isOn ? 30 : 0,
            color: this.panelColor,
            offsetX: 0,
            offsetY: 0
          })
          .animation({ duration: 300 })

        // 状态文字
        if (this.isOn) {
          Text('ON')
            .fontSize(14)
            .fontColor(this.panelColor)
            .fontWeight(FontWeight.Bold)
            .margin({ top: 12 })
        }
      }
      .width('100%')
      .height(180)
      .backgroundColor('rgba(255,255,255,0.02)')
      .borderRadius(20)
      .justifyContent(FlexAlign.Center)
    }
    .width('100%')
  }

  @Builder
  buildControlItem(control: ControlItem): void {
    Column({ space: 12 }) {
      // 标签行
      Row({ space: 8 }) {
        Image(control.icon)
          .width(18)
          .height(18)
          .fillColor('rgba(255,255,255,0.6)')

        Text(control.label)
          .fontSize(14)
          .fontColor('rgba(255,255,255,0.8)')
          .layoutWeight(1)

        if (control.type === 'slider') {
          Text(`${control.value}${control.unit || ''}`)
            .fontSize(14)
            .fontColor(this.getControlColor(control))
            .fontVariant(FontVariant.TabNums)
        }
      }
      .width('100%')

      // 控制组件
      if (control.type === 'switch') {
        Toggle({ type: ToggleType.Switch, isOn: control.value as boolean })
          .selectedColor(this.panelColor)
          .onChange((isOn: boolean) => {
            this.isOn = isOn;
            control.value = isOn;
            this.syncDeviceState();
          })
      } else if (control.type === 'slider') {
        Slider({
          value: control.value as number,
          min: control.min || 0,
          max: control.max || 100,
          step: control.step || 1,
          style: SliderStyle.OutSet
        })
          .width('100%')
          .selectedColor(this.getControlColor(control))
          .blockColor('#FFFFFF')
          .trackColor('rgba(255,255,255,0.1)')
          .onChange((value: number) => {
            control.value = value;
            this.syncDeviceState();
          })

        // 滑块光效反馈
        Column()
          .width('100%')
          .height(2)
          .backgroundColor(this.getControlColor(control))
          .blur(10)
          .opacity(0.5)
      } else if (control.type === 'button_group' && control.options) {
        Row({ space: 8 }) {
          ForEach(control.options, (option: string) => {
            Button(option)
              .height(36)
              .backgroundColor(control.value === option ? 
                `${this.panelColor}40` : 'rgba(255,255,255,0.05)')
              .fontColor(control.value === option ? this.panelColor : 'rgba(255,255,255,0.6)')
              .fontSize(13)
              .borderRadius(18)
              .layoutWeight(1)
              .onClick(() => {
                control.value = option;
                this.syncDeviceState();
              })
          })
        }
        .width('100%')
      } else if (control.type === 'color_picker') {
        this.buildColorPicker(control)
      }
    }
    .width('100%')
    .padding(16)
    .backgroundColor('rgba(255,255,255,0.03)')
    .borderRadius(16)
  }

  @Builder
  buildColorPicker(control: ControlItem): void {
    Row({ space: 8 }) {
      // 预设颜色
      ForEach(['#FFE4B5', '#FFD93D', '#FF6B6B', '#6BB6FF', '#E056FD', '#2ECC71'], (color: string) => {
        Button()
          .width(36)
          .height(36)
          .backgroundColor(color)
          .borderRadius(18)
          .border({
            width: control.value === color ? 3 : 0,
            color: '#FFFFFF'
          })
          .onClick(() => {
            control.value = color;
            this.panelColor = color;
            this.syncDeviceState();
          })
      })
    }
    .width('100%')
    .justifyContent(FlexAlign.SpaceAround)
  }

  @Builder
  buildQuickScenes(): void {
    Column({ space: 12 }) {
      Text('快捷场景')
        .fontSize(14)
        .fontColor('rgba(255,255,255,0.6)')
        .width('100%')

      Row({ space: 8 }) {
        ForEach(['阅读', '观影', '睡眠', '离家'], (scene: string) => {
          Button(scene)
            .height(40)
            .backgroundColor('rgba(255,255,255,0.05)')
            .fontColor('rgba(255,255,255,0.8)')
            .fontSize(13)
            .borderRadius(20)
            .layoutWeight(1)
            .onClick(() => {
              this.applyScene(scene);
            })
        })
      }
      .width('100%')
    }
    .width('100%')
    .padding({ top: 16 })
    .border({
      width: { top: 1 },
      color: 'rgba(255,255,255,0.05)'
    })
  }

  private syncDeviceState(): void {
    // 同步设备状态到后端/分布式设备
    const state = {
      deviceType: this.deviceType,
      isOn: this.isOn,
      controls: this.controls
    };
    AppStorage.setOrCreate('device_state_change', JSON.stringify(state));
  }

  private applyScene(scene: string): void {
    // 应用场景预设
    const sceneConfigs: Record<string, Partial<ControlItem>[]> = {
      '阅读': [
        { id: 'brightness', value: 100 },
        { id: 'color_temp', value: 4000 }
      ],
      '观影': [
        { id: 'brightness', value: 20 },
        { id: 'color_temp', value: 2700 }
      ],
      '睡眠': [
        { id: 'power', value: false }
      ],
      '离家': [
        { id: 'power', value: false }
      ]
    };

    const config = sceneConfigs[scene];
    if (config) {
      config.forEach(update => {
        const control = this.controls.find(c => c.id === update.id);
        if (control && update.value !== undefined) {
          control.value = update.value;
        }
      });
      this.syncDeviceState();
    }
  }
}

3.3 场景切换沉浸过渡(SceneTransition.ets)

代码亮点:实现"一键场景"的沉浸过渡效果。当用户切换"观影模式""睡眠模式"时,整个屏幕被场景主题色光效填满,设备图标以动画形式重新排列,营造"空间转换"的仪式感。这是HarmonyOS 6沉浸光感在场景化控制中的极致应用。

typescript 复制代码
// entry/src/main/ets/components/SceneTransition.ets

// 场景配置
interface SceneConfig {
  id: string;
  name: string;
  icon: Resource;
  primaryColor: string;
  secondaryColor: string;
  ambientColor: string;
  transitionDuration: number;
  deviceStates: Record<string, any>;
}

@Component
export struct SceneTransition {
  @State isTransitioning: boolean = false;
  @State progress: number = 0;
  @State fromColor: string = '#000000';
  @State toColor: string = '#000000';
  @State currentScene: SceneConfig | null = null;
  
  // 场景库
  private scenes: SceneConfig[] = [
    {
      id: 'home',
      name: '回家模式',
      icon: $r('app.media.ic_home'),
      primaryColor: '#F5A623',
      secondaryColor: '#FFD93D',
      ambientColor: '#5C3D1E',
      transitionDuration: 1500,
      deviceStates: { light: 'on', ac: 'auto', curtain: 'half' }
    },
    {
      id: 'movie',
      name: '观影模式',
      icon: $r('app.media.ic_movie'),
      primaryColor: '#9B59B6',
      secondaryColor: '#E056FD',
      ambientColor: '#4A235A',
      transitionDuration: 2000,
      deviceStates: { light: 'dim', tv: 'on', curtain: 'close' }
    },
    {
      id: 'sleep',
      name: '睡眠模式',
      icon: $r('app.media.ic_sleep'),
      primaryColor: '#4ECDC4',
      secondaryColor: '#7EDDD6',
      ambientColor: '#1E4D4A',
      transitionDuration: 3000,
      deviceStates: { light: 'off', ac: 'sleep', curtain: 'close' }
    },
    {
      id: 'wake',
      name: '起床模式',
      icon: $r('app.media.ic_sun'),
      primaryColor: '#FFE66D',
      secondaryColor: '#FFF5B7',
      ambientColor: '#5C4D1E',
      transitionDuration: 2000,
      deviceStates: { light: 'dim', curtain: 'open', purifier: 'on' }
    },
    {
      id: 'away',
      name: '离家模式',
      icon: $r('app.media.ic_away'),
      primaryColor: '#95A5A6',
      secondaryColor: '#BDC3C7',
      ambientColor: '#2C3E50',
      transitionDuration: 1000,
      deviceStates: { light: 'off', ac: 'off', curtain: 'close' }
    }
  ];

  async triggerScene(sceneId: string): Promise<void> {
    const scene = this.scenes.find(s => s.id === sceneId);
    if (!scene || this.isTransitioning) return;

    this.currentScene = scene;
    this.toColor = scene.primaryColor;
    this.isTransitioning = true;
    this.progress = 0;

    // 执行过渡动画
    await this.runTransition(scene.transitionDuration);

    // 应用设备状态
    this.applyDeviceStates(scene.deviceStates);

    // 完成
    this.isTransitioning = false;
    this.fromColor = scene.primaryColor;
  }

  private async runTransition(duration: number): Promise<void> {
    const startTime = Date.now();
    
    return new Promise(resolve => {
      const animate = () => {
        const elapsed = Date.now() - startTime;
        this.progress = Math.min(1, elapsed / duration);
        
        if (this.progress < 1) {
          requestAnimationFrame(animate);
        } else {
          resolve();
        }
      };
      animate();
    });
  }

  private applyDeviceStates(states: Record<string, any>): void {
    // 同步设备状态
    AppStorage.setOrCreate('scene_device_states', JSON.stringify(states));
    AppStorage.setOrCreate('scene_applied', this.currentScene?.id);
  }

  build() {
    Stack() {
      // 过渡遮罩层
      if (this.isTransitioning) {
        Column()
          .width('100%')
          .height('100%')
          .backgroundColor(this.toColor)
          .opacity(this.getTransitionOpacity())
          .blur(this.getTransitionBlur())
          .animation({ duration: 50 })

        // 场景图标放大动画
        if (this.currentScene) {
          Column() {
            Image(this.currentScene.icon)
              .width(80 * (1 + this.progress * 2))
              .height(80 * (1 + this.progress * 2))
              .fillColor('#FFFFFF')
              .opacity(1 - this.progress)
              .blur(this.progress * 10)

            Text(this.currentScene.name)
              .fontSize(24)
              .fontColor('#FFFFFF')
              .fontWeight(FontWeight.Bold)
              .margin({ top: 20 })
              .opacity(1 - this.progress)
              .translate({ y: this.progress * 50 })
          }
          .animation({ duration: 50 })
        }

        // 设备状态飞入动画
        if (this.currentScene && this.progress > 0.5) {
          ForEach(Object.entries(this.currentScene.deviceStates), ([device, state], index) => {
            Column() {
              Image($r(`app.media.ic_${device}`))
                .width(32)
                .height(32)
                .fillColor('#FFFFFF')

              Text(`${device}: ${state}`)
                .fontSize(12)
                .fontColor('rgba(255,255,255,0.8)')
            }
            .position({
              x: `${20 + index * 15}%`,
              y: `${40 + Math.sin(index) * 20}%`
            })
            .opacity((this.progress - 0.5) * 2)
            .scale({ x: this.progress, y: this.progress })
            .animation({ duration: 50 })
          })
        }
      }
    }
    .width('100%')
    .height('100%')
    .pointerEvents(this.isTransitioning ? PointerEvents.Auto : PointerEvents.None)
  }

  private getTransitionOpacity(): number {
    // 前半段渐入,后半段渐出
    if (this.progress < 0.5) {
      return this.progress * 2 * 0.9;
    } else {
      return (1 - this.progress) * 2 * 0.9;
    }
  }

  private getTransitionBlur(): number {
    // 中间最模糊
    return Math.sin(this.progress * Math.PI) * 50;
  }
}

四、主页面集成

4.1 智能家居主页面(SmartHomePage.ets)

typescript 复制代码
// entry/src/main/ets/pages/SmartHomePage.ets
import { SpatialFloatNav, RoomType } from '../components/SpatialFloatNav';
import { DeviceControlPanel } from '../components/DeviceControlPanel';
import { SceneTransition } from '../components/SceneTransition';

@Entry
@Component
struct SmartHomePage {
  @State currentRoom: RoomType = RoomType.LIVING_ROOM;
  @State selectedDevice: {name: string, type: string} | null = null;
  @State showDevicePanel: boolean = false;
  @State showSceneTransition: boolean = false;
  @State currentScene: string = '';
  @State roomColor: string = '#F5A623';

  // 场景过渡控制器
  private sceneTransitionRef: SceneTransition | null = null;

  aboutToAppear(): void {
    // 监听场景触发
    AppStorage.watch('show_scene_panel', () => {
      this.showSceneTransition = true;
    });
  }

  build() {
    Stack() {
      // 背景层
      Column()
        .width('100%')
        .height('100%')
        .backgroundColor('#0a0a0f')

      // 房间内容层
      this.buildRoomContent()

      // 空间感知导航
      SpatialFloatNav({
        onDeviceSelect: (device) => {
          this.selectedDevice = { name: device.label, type: device.deviceType };
          this.showDevicePanel = true;
        },
        onSceneSelect: (scene) => {
          this.triggerScene(scene);
        },
        onRoomChange: (room) => {
          this.roomColor = room.primaryColor;
        }
      })

      // 设备控制面板(底部弹出)
      if (this.showDevicePanel && this.selectedDevice) {
        DeviceControlPanel({
          deviceName: this.selectedDevice.name,
          deviceType: this.selectedDevice.type
        })
        .height('70%')
        .position({ x: 0, y: '100%' })
        .anchor('100%')
        .animation({
          duration: 300,
          curve: Curve.Spring
        })
        .gesture(
          SwipeGesture({ direction: SwipeDirection.Down })
            .onAction(() => {
              this.showDevicePanel = false;
            })
        )
      }

      // 场景过渡层
      SceneTransition()
    }
    .width('100%')
    .height('100%')
    .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
  }

  @Builder
  buildRoomContent(): void {
    Column({ space: 20 }) {
      // 房间标题
      Row({ space: 12 }) {
        Text(this.getRoomName(this.currentRoom))
          .fontSize(32)
          .fontWeight(FontWeight.Bold)
          .fontColor('#FFFFFF')

        // 在线设备数
        Row({ space: 4 }) {
          Column()
            .width(8)
            .height(8)
            .backgroundColor('#2ECC71')
            .borderRadius(4)

          Text('5 设备在线')
            .fontSize(14)
            .fontColor('rgba(255,255,255,0.5)')
        }
      }
      .width('100%')
      .padding({ top: 60, left: 24, right: 24 })

      // 场景快捷入口
      this.buildSceneShortcuts()

      // 设备网格
      this.buildDeviceGrid()
    }
    .width('100%')
    .height('100%')
  }

  @Builder
  buildSceneShortcuts(): void {
    Row({ space: 12 }) {
      ForEach(['回家', '观影', '睡眠', '离家'], (scene: string) => {
        Button() {
          Column({ space: 4 }) {
            Image($r(`app.media.ic_scene_${scene}`))
              .width(24)
              .height(24)
              .fillColor('#FFFFFF')

            Text(scene)
              .fontSize(12)
              .fontColor('#FFFFFF')
          }
        }
        .width(72)
        .height(72)
        .backgroundColor('rgba(255,255,255,0.05)')
        .borderRadius(16)
        .border({
          width: 1,
          color: 'rgba(255,255,255,0.1)'
        })
        .onClick(() => {
          this.triggerScene(scene);
        })
      })
    }
    .width('100%')
    .padding({ left: 24, right: 24 })
  }

  @Builder
  buildDeviceGrid(): void {
    Grid() {
      GridItem() {
        this.buildDeviceCard('主灯', 'light', true, '80%')
      }

      GridItem() {
        this.buildDeviceCard('空调', 'ac', true, '24°C')
      }

      GridItem() {
        this.buildDeviceCard('窗帘', 'curtain', false, '已关')
      }

      GridItem() {
        this.buildDeviceCard('电视', 'tv', false, '待机')
      }
    }
    .columnsTemplate('1fr 1fr')
    .columnsGap(12)
    .rowsGap(12)
    .padding(24)
    .layoutWeight(1)
  }

  @Builder
  buildDeviceCard(name: string, type: string, isOn: boolean, status: string): void {
    Column({ space: 12 }) {
      Row({ space: 8 }) {
        Image($r(`app.media.ic_${type}`))
          .width(24)
          .height(24)
          .fillColor(isOn ? this.roomColor : 'rgba(255,255,255,0.4)')

        Column()
          .width(8)
          .height(8)
          .backgroundColor(isOn ? '#2ECC71' : 'rgba(255,255,255,0.2)')
          .borderRadius(4)
      }
      .width('100%')
      .justifyContent(FlexAlign.SpaceBetween)

      Column({ space: 4 }) {
        Text(name)
          .fontSize(16)
          .fontColor('#FFFFFF')
          .fontWeight(FontWeight.Medium)

        Text(status)
          .fontSize(13)
          .fontColor(isOn ? this.roomColor : 'rgba(255,255,255,0.4)')
      }
      .width('100%')
      .alignItems(HorizontalAlign.Start)

      // 设备光效指示
      if (isOn) {
        Column()
          .width('100%')
          .height(3)
          .backgroundColor(this.roomColor)
          .borderRadius(2)
          .shadow({
            radius: 4,
            color: this.roomColor,
            offsetX: 0,
            offsetY: 0
          })
      }
    }
    .width('100%')
    .height(140)
    .padding(16)
    .backgroundColor('rgba(255,255,255,0.03)')
    .backdropFilter($r('sys.blur.10'))
    .borderRadius(20)
    .border({
      width: 1,
      color: isOn ? `${this.roomColor}30` : 'rgba(255,255,255,0.05)'
    })
    .onClick(() => {
      this.selectedDevice = { name, type };
      this.showDevicePanel = true;
    })
  }

  private getRoomName(room: RoomType): string {
    const names: Record<string, string> = {
      living_room: '客厅',
      bedroom: '卧室',
      study: '书房',
      kitchen: '厨房',
      bathroom: '浴室'
    };
    return names[room] || '客厅';
  }

  private triggerScene(scene: string): void {
    const sceneMap: Record<string, string> = {
      '回家': 'home',
      '观影': 'movie',
      '睡眠': 'sleep',
      '离家': 'away'
    };
    const sceneId = sceneMap[scene];
    if (sceneId) {
      this.currentScene = sceneId;
      // 触发场景过渡
      AppStorage.setOrCreate('trigger_scene', sceneId);
    }
  }
}

五、关键技术总结

5.1 空间-导航-光效协同架构

复制代码
空间感知层
    ├── 分布式设备信号检测
    ├── 蓝牙信标定位
    └── 用户手动选择
            ↓
导航适配层
    ├── 客厅 → 底部横条导航
    ├── 卧室 → 左侧迷你轨道
    ├── 书房 → 右侧工具轨道
    ├── 厨房 → 顶部快捷栏
    └── 浴室 → 底部防水导航
            ↓
光效渲染层
    ├── 房间主题色环境光
    ├── 设备状态光效粒子
    └── 场景过渡全屏光效

5.2 设备状态可视化编码

设备类型 状态维度 光效编码 视觉反馈
灯光 亮度 光晕半径+不透明度 亮度越高光晕越大
灯光 色温 颜色冷暖渐变 2700K暖黄->6500K冷白
空调 模式 脉冲颜色 制冷蓝/制热红/除湿绿
空调 温度 色彩渐变 16°C深蓝->30°C深红
窗帘 开合度 光效位置 关闭底部暗->打开顶部亮

5.3 分布式设备同步

typescript 复制代码
// 设备状态同步
class DeviceStateSync {
  async syncToDistributedDevices(state: DeviceState): Promise<void> {
    // 1. 获取同账号下的所有HarmonyOS设备
    const devices = await distributedDeviceManager.getTrustedDeviceListSync();
    
    // 2. 筛选在线的家居控制设备
    const onlineDevices = devices.filter(d => d.deviceType === 'smartHome');
    
    // 3. 同步状态
    for (const device of onlineDevices) {
      await this.sendStateToDevice(device.networkId, state);
    }
    
    // 4. 同步光效主题
    AppStorage.setOrCreate('sync_light_theme', {
      color: state.color,
      intensity: state.intensity,
      timestamp: Date.now()
    });
  }
}

六、调试与适配建议

  1. 多设备信号测试:在不同房间测试分布式设备信号强度,校准房间边界判定算法
  2. 光效功耗优化:夜间模式降低光效刷新率,延长OLED屏幕寿命
  3. 无障碍支持:为视障用户提供语音播报设备状态,光效切换时播放提示音
  4. 隐私保护:房间定位数据本地处理,不上传云端,支持一键清除定位历史

七、结语

HarmonyOS 6的悬浮导航与沉浸光感特性,为智能家居控制带来了从"功能操作"到"空间体验"的范式转变。通过"光影智家"的实战案例,我们展示了如何:

  • 构建空间感知的自适应导航,根据用户所在房间自动切换控制界面和光效主题
  • 实现设备状态的"光效编码",让设备运行状态通过视觉直观呈现
  • 打造场景切换的沉浸过渡,一键切换时全屏光效营造仪式感
  • 利用分布式能力实现多设备间的控制状态同步与光效联动

这些技术不仅提升了智能家居APP的操控效率,更让科技真正融入生活空间,创造"无感交互、有光相伴"的未来居住体验。期待HarmonyOS生态中的IoT开发者们能够利用这些新特性,创造出更多温暖人心的智能空间。


转载自:https://blog.csdn.net/u014727709/article/details/160312432

欢迎 👍点赞✍评论⭐收藏,欢迎指正*

相关推荐
雪芽蓝域zzs2 小时前
uni-app x 中使用 UTS 语言实现兼容鸿蒙的加密
华为·uni-app·harmonyos
条tiao条2 小时前
鸿蒙 ArkTS 学习入门
学习·华为·harmonyos
Francek Chen2 小时前
【华为Pura90系列】新品发布:Pura 90系列影像领衔,Pura X Max开启大阔折叠新赛道
人工智能·华为·harmonyos·pura 90
特立独行的猫a2 小时前
HarmonyOS / OpenHarmony 鸿蒙PC平台三方库移植:使用 Lycium 移植 pngquant 的实践总结
华为·harmonyos·pngquant·三方库移植·鸿蒙pc·lycim
摄影图2 小时前
智能家居科技单品图片素材 解锁便捷智慧居家体验
网络·科技·aigc·智能家居·贴图·插画
liulian09164 小时前
【Flutter for OpenHarmony第三方库】Flutter for OpenHarmony 多语言国际化适配实战指南
flutter·华为·学习方法·harmonyos
nashane12 小时前
HarmonyOS 6学习:解决异步场景下Toast提示框无法弹出的UI上下文丢失问题
学习·ui·harmonyos·harmony app
星辰徐哥15 小时前
鸿蒙金融理财全栈项目——上线与运维、用户反馈、持续迭代优化
运维·金融·harmonyos
枫叶丹415 小时前
【HarmonyOS Next之旅】DevEco Studio使用指南(三十八) -> 构建HAR
华为·harmonyos·deveco studio·harmonyos next