Răsfoiți Sursa

MiniPlayerBar抽离单独组件

onecold 4 luni în urmă
părinte
comite
e558b64b19
2 a modificat fișierele cu 676 adăugiri și 49 ștergeri
  1. 50 49
      entry/src/main/ets/pages/NewIndex.ets
  2. 626 0
      entry/src/main/ets/view/MiniPlayerBar.ets

+ 50 - 49
entry/src/main/ets/pages/NewIndex.ets

@@ -62,6 +62,7 @@ import { PointLightContentButton } from '../view/PointLight/PointLightContentBut
 import { PlayingIndicator } from '../view/PlayingIndicator';
 import { FindView } from '../view/FindView';
 import { hdsEffect } from '@kit.UIDesignKit';
+import { MiniPlayerBar } from '../view/MiniPlayerBar';
 import {
   resolveMiniPlayerMorphTarget,
 } from '../common/util/PlayerDismissHelper';
@@ -1096,56 +1097,56 @@ struct NewIndex {
   // 迷你播放条挂载后统一走这个 Builder,避免 build() 里堆叠过多手机/HiCar/orb 分支。
   @Builder
   private MiniPlayerBarBuilder() {
-    Row() {
-      Stack() {
-        // 完整播放条层在收拢时向右退出,展开时再从右侧拉回。
-        if (!this.isMiniPlayerOrbMode || this.miniPlayerFullContentOpacity > 0.02) {
-          Stack() {
-            if (this.curDisplayIsHiCar) {
-              this.HiCarPlayController()
-            } else {
-              this.PlayController()
-            }
-          }
-          .width('100%')
-          .height('100%')
-          .opacity(this.miniPlayerFullContentOpacity)
-          .translate({ x: this.miniPlayerFullContentTranslateX })
-        }
-
-        // orb 层固定贴右侧,负责接住收拢后的封面并作为展开入口。
-        if (this.isMiniPlayerOrbMode || this.miniPlayerOrbOpacity > 0.02) {
-          Row() {
-            this.MiniPlayerOrbControl()
-          }
-          .width('100%')
-          .height('100%')
-          .justifyContent(FlexAlign.End)
-          .alignItems(VerticalAlign.Center)
-        }
-
-        if (this.miniPlayerProxySize > 0) {
-          this.MiniPlayerMorphProxyDot()
-        }
+    MiniPlayerBar({
+      bottomBarHeight: this.bottomBarHeight,
+      bottomSafeHeight: this.bottomSafeHeight,
+      curDisplayIsHiCar: this.curDisplayIsHiCar,
+      cover: this.cover,
+      currentSong: this.currentSong,
+      themeColor: this.themeColor,
+      progressValue: this.progressValue,
+      controlPlayStatus: this.CONTROL_PlayStatus,
+      isShowPrecious: this.isShowPrecious,
+      isMiniPlayerOrbMode: this.isMiniPlayerOrbMode,
+      isMiniPlayerModeTransitioning: this.isMiniPlayerModeTransitioning,
+      miniPlayerScaleX: this.miniPlayerScaleX,
+      miniPlayerScaleY: this.miniPlayerScaleY,
+      miniPlayerOpacity: this.miniPlayerOpacity,
+      miniPlayerBorderRadius: this.miniPlayerBorderRadius,
+      miniPlayerProxySize: this.miniPlayerProxySize,
+      miniPlayerProxyOpacity: this.miniPlayerProxyOpacity,
+      miniPlayerContentScaleY: this.miniPlayerContentScaleY,
+      miniPlayerContentTranslateY: this.miniPlayerContentTranslateY,
+      miniPlayerFullContentTranslateX: this.miniPlayerFullContentTranslateX,
+      miniPlayerTranslateY: this.miniPlayerTranslateY,
+      miniPlayerSurfaceWidth: this.miniPlayerSurfaceWidth,
+      miniPlayerSurfaceHeight: this.miniPlayerSurfaceHeight,
+      miniPlayerFullContentOpacity: this.miniPlayerFullContentOpacity,
+      miniPlayerOrbOpacity: this.miniPlayerOrbOpacity,
+      miniPlayerOrbScale: this.miniPlayerOrbScale,
+      miniPlayerOrbTranslateX: this.miniPlayerOrbTranslateX,
+      onOpenPlayer: (): void => {
+        this.setShowPlayTrue()
+      },
+      onCollapseToOrb: (): void => {
+        this.collapseMiniPlayerToOrb()
+      },
+      onOrbTap: (): void => {
+        this.handleMiniPlayerOrbTap()
+      },
+      onPlayPrevious: (): void => {
+        this.getUIContext().getHostContext()!.eventHub.emit('playPrevious');
+      },
+      onPlayOrPause: (): void => {
+        this.getUIContext().getHostContext()!.eventHub.emit('playOrPause');
+      },
+      onPlayNext: (): void => {
+        this.getUIContext().getHostContext()!.eventHub.emit('playNext');
+      },
+      onOpenPlayList: (): void => {
+        this.getUIContext().getHostContext()!.eventHub.emit('openPlayList');
       }
-      .width(this.miniPlayerSurfaceWidth)
-      .height(this.miniPlayerSurfaceHeight)
-      .borderRadius(this.miniPlayerBorderRadius)
-      .backgroundBlurStyle(BlurStyle.BACKGROUND_ULTRA_THICK)
-      .backgroundImage(StrUtil.isEmpty(this.cover) ? $r('app.media.alt') : this.cover)
-      .backgroundImageSize({ width: '100%' })
-      .scale({ x: this.miniPlayerScaleX, y: this.miniPlayerScaleY, centerX: '50%', centerY: '50%' })
-      .opacity(Math.min(0.98, this.miniPlayerOpacity))
-      .clip(true)
-      .clickEffect({ level: ClickEffectLevel.HEAVY })
-    }
-    .width('90%')
-    .height(this.bottomBarHeight)
-    .justifyContent(FlexAlign.End)
-    .alignItems(VerticalAlign.Center)
-    .translate({ y: this.miniPlayerTranslateY })
-    .margin({ bottom: DeviceUtil.getDeviceType() === resourceManager.DeviceType.DEVICE_TYPE_2IN1 || this.curDisplayIsHiCar
-      ? 30 : this.bottomSafeHeight })
+    })
   }
 
   build() {

+ 626 - 0
entry/src/main/ets/view/MiniPlayerBar.ets

@@ -0,0 +1,626 @@
+import { DeviceUtil, StrUtil } from '@pura/harmony-utils'
+import { resourceManager } from '@kit.LocalizationKit'
+import { deviceInfo } from '@kit.BasicServicesKit'
+import { hdsEffect } from '@kit.UIDesignKit'
+import { VideoItem } from '../viewmodel/VideoItem'
+import { PlayStatus } from '../common/PlayStatus'
+import { CommonConstants } from '../common/constants/CommonConstants'
+import { PointLightContentButton } from './PointLight/PointLightContentButton'
+import { PlayingIndicator } from './PlayingIndicator'
+
+@Component
+export struct MiniPlayerBar {
+  @Prop bottomBarHeight: number = 64
+  @Prop bottomSafeHeight: number = 0
+  @Prop curDisplayIsHiCar: boolean = false
+  @Prop cover: string | undefined = ''
+  @Prop currentSong: VideoItem | undefined = undefined
+  @Prop themeColor: string = CommonConstants.DEFAULT_THEME_COLOR
+  @Prop progressValue: number = 0
+  @Prop controlPlayStatus: number = PlayStatus.INIT
+  @Prop isShowPrecious: boolean = false
+  @Prop isMiniPlayerOrbMode: boolean = false
+  @Prop isMiniPlayerModeTransitioning: boolean = false
+  @Prop miniPlayerScaleX: number = 1
+  @Prop miniPlayerScaleY: number = 1
+  @Prop miniPlayerOpacity: number = 1
+  @Prop miniPlayerBorderRadius: number = 200
+  @Prop miniPlayerProxySize: number = 0
+  @Prop miniPlayerProxyOpacity: number = 0
+  @Prop miniPlayerContentScaleY: number = 1
+  @Prop miniPlayerContentTranslateY: number = 0
+  @Prop miniPlayerFullContentTranslateX: number = 0
+  @Prop miniPlayerTranslateY: number = 0
+  @Prop miniPlayerSurfaceWidth: number = 0
+  @Prop miniPlayerSurfaceHeight: number = 0
+  @Prop miniPlayerFullContentOpacity: number = 1
+  @Prop miniPlayerOrbOpacity: number = 0
+  @Prop miniPlayerOrbScale: number = 1
+  @Prop miniPlayerOrbTranslateX: number = 0
+  onOpenPlayer: () => void = () => {}
+  onCollapseToOrb: () => void = () => {}
+  onOrbTap: () => void = () => {}
+  onPlayPrevious: () => void = () => {}
+  onPlayOrPause: () => void = () => {}
+  onPlayNext: () => void = () => {}
+  onOpenPlayList: () => void = () => {}
+
+  @State private pointLightOptions: hdsEffect.PointLightOptions | undefined = undefined
+
+  private resolveMiniPlayerOrbSize(): number {
+    const barHeightVp = this.bottomBarHeight > 0 ? this.bottomBarHeight : 70
+    return Math.max(54, Math.min(60, barHeightVp - 8))
+  }
+
+  private shouldBlockTransportAction(): boolean {
+    return this.isMiniPlayerModeTransitioning
+  }
+
+  @Builder
+  private buildMiniPlayerMorphProxyDot() {
+    Stack({ alignContent: Alignment.Center }) {
+      Image(StrUtil.isNotEmpty(this.cover) ? this.cover as string : $r('app.media.alt'))
+        .width('100%')
+        .height('100%')
+        .objectFit(ImageFit.Cover)
+        .alt($r('app.media.alt'))
+    }
+    .width(this.miniPlayerProxySize)
+    .height(this.miniPlayerProxySize)
+    .borderRadius(this.miniPlayerProxySize)
+    .clip(true)
+    .opacity(this.miniPlayerProxyOpacity)
+    .shadow({
+      radius: 18,
+      type: ShadowType.BLUR,
+      color: 'on_primary'
+    })
+  }
+
+  @Builder
+  private buildMiniPlayerOrbButtonContent(orbSize: number) {
+    Stack({ alignContent: Alignment.Center }) {
+      Image(StrUtil.isNotEmpty(this.cover) ? this.cover as string : $r('app.media.alt'))
+        .width('100%')
+        .height('100%')
+        .objectFit(ImageFit.Cover)
+        .alt($r('app.media.alt'))
+        .borderRadius(100)
+      Column()
+        .width('100%')
+        .height('100%')
+        .backgroundColor('#66000000')
+
+      Progress({
+        value: Math.floor(this.progressValue),
+        total: 100,
+        type: ProgressType.Ring,
+      })
+        .color(Color.White)
+        .width('100%')
+        .height('100%')
+        .style({ strokeWidth: 3 })
+
+      if (this.controlPlayStatus === PlayStatus.PLAY) {
+        PlayingIndicator({
+          isActive: true,
+          indicatorSize: Math.max(16, Math.floor(orbSize * 0.34)),
+          indicatorColor: '#FFFFFF'
+        })
+      }
+    }
+    .width('100%')
+    .height('100%')
+    .clip(true)
+    .borderRadius(100)
+  }
+
+  @Builder
+  private miniPlayerOrbControl() {
+    PointLightContentButton({
+      pointColor: this.themeColor,
+      buttonRadius: this.resolveMiniPlayerOrbSize() / 2,
+      pointLightHeight: 96,
+      pressScale: 0.94,
+      useShadow: true,
+      builder: (): void => {
+        this.buildMiniPlayerOrbButtonContent(this.resolveMiniPlayerOrbSize())
+      }
+    })
+      .width(this.resolveMiniPlayerOrbSize())
+      .height(this.resolveMiniPlayerOrbSize())
+      .opacity(this.miniPlayerOrbOpacity)
+      .borderRadius(this.resolveMiniPlayerOrbSize() / 2)
+      .scale({ x: this.miniPlayerOrbScale, y: this.miniPlayerOrbScale, centerX: '50%', centerY: '50%' })
+      .translate({ x: this.miniPlayerOrbTranslateX })
+      .onClick((): void => {
+        this.onOrbTap()
+      })
+  }
+
+  @Builder
+  private buildMiniPlayerCoverContent() {
+    Stack({ alignContent: Alignment.Center }) {
+      Image(StrUtil.isNotEmpty(this.currentSong?.pixelMapPath) ?
+        this.currentSong?.pixelMapPath : $r('app.media.alt'))
+        .width(48)
+        .height(48)
+        .objectFit(ImageFit.Contain)
+        .alt($r('app.media.alt'))
+        .fillColor(this.themeColor)
+        .borderRadius(8)
+        .shadow({
+          radius: 15,
+          type: ShadowType.BLUR,
+          color: 'on_primary'
+        })
+    }
+    .width(48)
+    .height(48)
+  }
+
+  @Builder
+  private miniPlayerCoverButton() {
+    PointLightContentButton({
+      pointColor: this.themeColor,
+      buttonRadius: 10,
+      pointLightHeight: 88,
+      pressScale: 0.92,
+      useShadow: false,
+      builder: (): void => {
+        this.buildMiniPlayerCoverContent()
+      }
+    })
+      .width(48)
+      .height(48)
+      .margin({ left: 5 })
+      .zIndex(3)
+      .onClick((): void => {
+        this.onCollapseToOrb()
+      })
+  }
+
+  @Builder
+  private buildPlayPreviousControlContent() {
+    Row() {
+      SymbolGlyph($r('sys.symbol.backward_end_fill'))
+        .fontSize(28)
+        .fontColor([Color.White])
+    }
+    .width('100%')
+    .height('100%')
+    .justifyContent(FlexAlign.Center)
+    .alignItems(VerticalAlign.Center)
+  }
+
+  @Builder
+  private buildPlayToggleControlContent() {
+    Row() {
+      Stack() {
+        Progress({
+          value: Math.floor(this.progressValue),
+          type: ProgressType.Ring,
+        })
+          .color(this.themeColor)
+          .height(38)
+          .aspectRatio(CommonConstants.ASPECT_RATIO)
+        Image(this.controlPlayStatus === PlayStatus.PLAY ? $r('app.media.hm_pause') : $r('app.media.hm_play2'))
+          .height(36)
+          .width(36)
+          .fillColor(Color.White)
+      }
+    }
+    .width('100%')
+    .height('100%')
+    .justifyContent(FlexAlign.Center)
+    .alignItems(VerticalAlign.Center)
+  }
+
+  @Builder
+  private buildPlayNextControlContent() {
+    Row() {
+      SymbolGlyph($r('sys.symbol.forward_end_fill'))
+        .fontSize(28)
+        .fontColor([Color.White])
+    }
+    .width('100%')
+    .height('100%')
+    .justifyContent(FlexAlign.Center)
+    .alignItems(VerticalAlign.Center)
+  }
+
+  @Builder
+  private buildPlayListControlContent() {
+    Row() {
+      SymbolGlyph($r('sys.symbol.music_note_list'))
+        .fontSize(28)
+        .fontColor([Color.White])
+    }
+    .width('100%')
+    .height('100%')
+    .justifyContent(FlexAlign.Center)
+    .alignItems(VerticalAlign.Center)
+  }
+
+  @Builder
+  private playConLeft() {
+    Row() {
+      this.miniPlayerCoverButton()
+
+      Column() {
+        Text(this.currentSong?.name)
+          .fontSize(16)
+          .textOverflow({ overflow: TextOverflow.MARQUEE })
+          .maxLines(1)
+          .fontColor(Color.White)
+          .fontWeight(FontWeight.Bolder)
+        Row() {
+          Text(this.currentSong?.artist)
+            .margin({ top: 2 })
+            .fontSize(13)
+            .textAlign(TextAlign.Start)
+            .maxLines(1)
+            .fontWeight(FontWeight.Bold)
+            .textOverflow({ overflow: TextOverflow.MARQUEE })
+            .fontColor(Color.White)
+        }
+        .visibility(this.currentSong?.artist ? Visibility.Visible : Visibility.None)
+      }
+      .padding({ left: 8 })
+      .alignItems(HorizontalAlign.Start)
+      .margin({ right: 22 })
+      .onClick((): void => {
+        this.onOpenPlayer()
+      })
+    }
+    .padding({ right: 18 })
+    .layoutWeight(1)
+    .alignItems(VerticalAlign.Center)
+    .justifyContent(FlexAlign.Start)
+  }
+
+  @Builder
+  private playConRigth() {
+    Row() {
+      PointLightContentButton({
+        pointColor: this.themeColor,
+        buttonRadius: 22,
+        pointLightHeight: 56,
+        pressScale: 0.88,
+        builder: (): void => {
+          this.buildPlayPreviousControlContent()
+        }
+      })
+        .width(44)
+        .height(44)
+        .visibility(this.isShowPrecious ? Visibility.Visible : Visibility.None)
+        .displayPriority(2)
+        .onClick((): void => {
+          if (this.shouldBlockTransportAction()) {
+            return
+          }
+          this.onPlayPrevious()
+        })
+
+      PointLightContentButton({
+        pointColor: this.themeColor,
+        buttonRadius: 23,
+        pointLightHeight: 62,
+        pressScale: 0.9,
+        builder: (): void => {
+          this.buildPlayToggleControlContent()
+        }
+      })
+        .width(46)
+        .height(46)
+        .displayPriority(3)
+        .onClick((): void => {
+          if (this.shouldBlockTransportAction()) {
+            return
+          }
+          this.onPlayOrPause()
+        })
+
+      PointLightContentButton({
+        pointColor: this.themeColor,
+        buttonRadius: 22,
+        pointLightHeight: 56,
+        pressScale: 0.88,
+        builder: (): void => {
+          this.buildPlayNextControlContent()
+        }
+      })
+        .width(44)
+        .height(44)
+        .displayPriority(2)
+        .onClick((): void => {
+          if (this.shouldBlockTransportAction()) {
+            return
+          }
+          this.onPlayNext()
+        })
+
+      PointLightContentButton({
+        pointColor: this.themeColor,
+        buttonRadius: 21,
+        pointLightHeight: 52,
+        pressScale: 0.88,
+        builder: (): void => {
+          this.buildPlayListControlContent()
+        }
+      })
+        .width(42)
+        .height(42)
+        .displayPriority(1)
+        .onClick((): void => {
+          if (this.shouldBlockTransportAction()) {
+            return
+          }
+          this.onOpenPlayList()
+        })
+    }
+    .margin({ left: 5 })
+    .justifyContent(FlexAlign.End)
+    .alignItems(VerticalAlign.Center)
+  }
+
+  @Builder
+  private hiCarLeadingCluster() {
+    Row() {
+      this.miniPlayerCoverButton()
+      this.hiCarPlayConRigth()
+      Row()
+        .layoutWeight(1)
+    }
+    .width('100%')
+    .height('100%')
+    .justifyContent(FlexAlign.Start)
+    .alignItems(VerticalAlign.Center)
+  }
+
+  @Builder
+  private hiCarCenterInfo() {
+    Column() {
+      Text(this.currentSong?.name)
+        .fontSize(16)
+        .textOverflow({ overflow: TextOverflow.MARQUEE })
+        .maxLines(1)
+        .textAlign(TextAlign.Center)
+        .fontColor(Color.White)
+        .fontWeight(FontWeight.Bolder)
+      Row() {
+        Text(this.currentSong?.artist)
+          .margin({ top: 2 })
+          .fontSize(13)
+          .textAlign(TextAlign.Center)
+          .maxLines(1)
+          .fontWeight(FontWeight.Bold)
+          .textOverflow({ overflow: TextOverflow.MARQUEE })
+          .fontColor(Color.White)
+      }
+      .visibility(this.currentSong?.artist ? Visibility.Visible : Visibility.None)
+    }
+    .width('42%')
+    .alignItems(HorizontalAlign.Center)
+    .onClick((): void => {
+      this.onOpenPlayer()
+    })
+  }
+
+  @Builder
+  private hiCarPlayConRigth() {
+    Row({ space: 8 }) {
+      PointLightContentButton({
+        pointColor: this.themeColor,
+        buttonRadius: 22,
+        pointLightHeight: 56,
+        pressScale: 0.88,
+        builder: (): void => {
+          this.buildPlayPreviousControlContent()
+        }
+      })
+        .width(44)
+        .height(44)
+        .visibility(this.isShowPrecious ? Visibility.Visible : Visibility.None)
+        .displayPriority(2)
+        .onClick((): void => {
+          if (this.shouldBlockTransportAction()) {
+            return
+          }
+          this.onPlayPrevious()
+        })
+      PointLightContentButton({
+        pointColor: this.themeColor,
+        buttonRadius: 23,
+        pointLightHeight: 62,
+        pressScale: 0.9,
+        builder: (): void => {
+          this.buildPlayToggleControlContent()
+        }
+      })
+        .width(46)
+        .height(46)
+        .displayPriority(3)
+        .onClick((): void => {
+          if (this.shouldBlockTransportAction()) {
+            return
+          }
+          this.onPlayOrPause()
+        })
+
+      PointLightContentButton({
+        pointColor: this.themeColor,
+        buttonRadius: 22,
+        pointLightHeight: 56,
+        pressScale: 0.88,
+        builder: (): void => {
+          this.buildPlayNextControlContent()
+        }
+      })
+        .width(44)
+        .height(44)
+        .displayPriority(2)
+        .onClick((): void => {
+          if (this.shouldBlockTransportAction()) {
+            return
+          }
+          this.onPlayNext()
+        })
+
+      PointLightContentButton({
+        pointColor: this.themeColor,
+        buttonRadius: 21,
+        pointLightHeight: 52,
+        pressScale: 0.88,
+        builder: (): void => {
+          this.buildPlayListControlContent()
+        }
+      })
+        .width(42)
+        .height(42)
+        .displayPriority(1)
+        .onClick((): void => {
+          if (this.shouldBlockTransportAction()) {
+            return
+          }
+          this.onOpenPlayList()
+        })
+    }
+    .margin({ left: 16 })
+    .justifyContent(FlexAlign.Start)
+    .alignItems(VerticalAlign.Center)
+  }
+
+  @Builder
+  private playController() {
+    Row() {
+      this.playConLeft()
+      this.playConRigth()
+    }
+    .width('100%')
+    .height(this.bottomBarHeight)
+    .scale({ x: 1, y: this.miniPlayerContentScaleY, centerX: '50%', centerY: '50%' })
+    .translate({ y: this.miniPlayerContentTranslateY })
+    .hitTestBehavior(HitTestMode.Transparent)
+    .zIndex(2)
+    .opacity(0.9)
+    .onClick((): void => {
+      this.onOpenPlayer()
+    })
+    .onTouch((event: TouchEvent): void => {
+      if (event.type === TouchType.Down) {
+        this.pointLightOptions = {
+          color: this.themeColor,
+          intensity: 1,
+          height: 60
+        }
+      } else if (event.type === TouchType.Up || event.type === TouchType.Cancel) {
+        this.pointLightOptions = undefined
+      }
+    })
+    .visualEffect(deviceInfo.sdkApiVersion >= 20
+      ? new hdsEffect.HdsEffectBuilder()
+        .pointLight({
+          options: this.pointLightOptions,
+          illuminatedType: hdsEffect.PointLightIlluminatedType.BORDER_CONTENT
+        })
+        .buildEffect()
+      : undefined)
+    .padding({
+      left: 16,
+      right: 16
+    })
+  }
+
+  @Builder
+  private hiCarPlayController() {
+    Stack({ alignContent: Alignment.Center }) {
+      this.hiCarLeadingCluster()
+      this.hiCarCenterInfo()
+    }
+    .opacity(0.9)
+    .width('100%')
+    .height(this.bottomBarHeight)
+    .hitTestBehavior(HitTestMode.Transparent)
+    .zIndex(2)
+    .onClick((): void => {
+      this.onOpenPlayer()
+    })
+    .onTouch((event: TouchEvent): void => {
+      if (event.type === TouchType.Down) {
+        this.pointLightOptions = {
+          color: this.themeColor,
+          intensity: 1,
+          height: 60
+        }
+      } else if (event.type === TouchType.Up || event.type === TouchType.Cancel) {
+        this.pointLightOptions = undefined
+      }
+    })
+    .visualEffect(deviceInfo.sdkApiVersion >= 20
+      ? new hdsEffect.HdsEffectBuilder()
+        .pointLight({
+          options: this.pointLightOptions,
+          illuminatedType: hdsEffect.PointLightIlluminatedType.BORDER_CONTENT
+        })
+        .buildEffect()
+      : undefined)
+    .padding({
+      left: 16,
+      right: 16
+    })
+  }
+
+  build() {
+    Row() {
+      Stack() {
+        if (!this.isMiniPlayerOrbMode || this.miniPlayerFullContentOpacity > 0.02) {
+          Stack() {
+            if (this.curDisplayIsHiCar) {
+              this.hiCarPlayController()
+            } else {
+              this.playController()
+            }
+          }
+          .width('100%')
+          .height('100%')
+          .opacity(this.miniPlayerFullContentOpacity)
+          .translate({ x: this.miniPlayerFullContentTranslateX })
+        }
+
+        if (this.isMiniPlayerOrbMode || this.miniPlayerOrbOpacity > 0.02) {
+          Row() {
+            this.miniPlayerOrbControl()
+          }
+          .width('100%')
+          .height('100%')
+          .justifyContent(FlexAlign.End)
+          .alignItems(VerticalAlign.Center)
+        }
+
+        if (this.miniPlayerProxySize > 0) {
+          this.buildMiniPlayerMorphProxyDot()
+        }
+      }
+      .width(this.miniPlayerSurfaceWidth)
+      .height(this.miniPlayerSurfaceHeight)
+      .borderRadius(this.miniPlayerBorderRadius)
+      .backgroundBlurStyle(BlurStyle.BACKGROUND_ULTRA_THICK)
+      .backgroundImage(StrUtil.isEmpty(this.cover) ? $r('app.media.alt') : this.cover)
+      .backgroundImageSize({ width: '100%' })
+      .scale({ x: this.miniPlayerScaleX, y: this.miniPlayerScaleY, centerX: '50%', centerY: '50%' })
+      .opacity(Math.min(0.98, this.miniPlayerOpacity))
+      .clip(true)
+      .clickEffect({ level: ClickEffectLevel.HEAVY })
+    }
+    .width('90%')
+    .height(this.bottomBarHeight)
+    .justifyContent(FlexAlign.End)
+    .alignItems(VerticalAlign.Center)
+    .translate({ y: this.miniPlayerTranslateY })
+    .margin({
+      bottom: DeviceUtil.getDeviceType() === resourceManager.DeviceType.DEVICE_TYPE_2IN1 || this.curDisplayIsHiCar
+        ? 30 : this.bottomSafeHeight
+    })
+  }
+}