鸿蒙开发—黑马云音乐之播放页面(下)

news2024/9/20 18:43:47

目录

1.用户随意控制播放进度

2.歌曲暂停和播放控制和歌曲上一首、下一首播放控制

3.歌曲列表

4.歌曲列表数据动态化和背景雾化

5.唱针效果

6.结语


1.用户随意控制播放进度

 src/main/ets/services/AvPlayerManager.ets:

  // 让播放对象从某个时间点开始播放
  static async seek(time:number) {
    AvPlayerManager.avPlayer.seek(time)
  }

src/main/ets/pages/playPage.ets:

import { AvPlayerManager, songItemType } from '../services/AvPlayerManager'

@Entry
@Component
struct PlayPage {
  @State angle: number = 0 // 控制角度变化值 0 - 360度改变
  @State totalTime: number = 0
  @State currentTime: number = 0

  aboutToAppear() {
    this.totalTime = AvPlayerManager.avPlayer.duration // 表示当前正在播放歌曲的总时间,单位是毫秒

    //  不停的获取当前歌曲的已播放时长
    AvPlayerManager.avPlayer.on('timeUpdate', (time) => {
      //   time表示当前歌曲已经播放的时长,单位是毫秒
      this.currentTime = time
    })
  }

  // 作用:接收一个毫秒数值,转成成 00:00这种格式的字符串返回
  formatTime(time: number) {
    // 1. 计算出分钟数
    let seconds = time / 1000 //将毫秒数转成总秒数
    let min = Math.floor(seconds / 60) // 获取到了分钟部分

    // 2. 计算出除开分钟之外的剩余的秒数
    let s = Math.ceil(seconds % 60) // Math.ceil(19.89) -> 20

    return `${min.toString().padStart(2,'0')}:${s.toString().padStart(2,'0')}`
  }

  build() {
    Column() {
      //   CD唱片
      Stack() {
        Image($r('app.media.ic_cd'))
          .height(300)
          .borderRadius(300)

        Image(AvPlayerManager.songs[AvPlayerManager.index].img)
          .height(200)
          .borderRadius(200)
      }
      // 控制一个元素旋转 (这个属性一定要写在animation前面,否则不会触发动画)
      .rotate({ angle: this.angle })
      // 元素加载完毕之后触发一个回调函数 .onAppear
      .onAppear(() => {
        this.angle = 360 //将角度改成360都
      })
      // 增加旋转的动画效果
      .animation({
        duration: 10000, // 用10秒钟的时间让元素旋转一圈
        curve: Curve.Linear, // 匀速旋转
        iterations: -1 //表示设置旋转的圈数,如果是-1则表示不停的转,如果是正数则表示转的圈数
      })


      //   歌曲信息
      Column({ space: 5 }) {
        Text(AvPlayerManager.songs[AvPlayerManager.index].name)
          .fontSize(25)
          .fontWeight(800)
          .fontColor(Color.White)

        Text(AvPlayerManager.songs[AvPlayerManager.index].author)
          .fontSize(15)
          .fontColor(Color.White)
      }
      .width('100%')

      // 播放进度+控制按钮容器
      Column() {
        //   播放进度
        Row({ space: 5 }) {
          Text(this.formatTime(this.currentTime)) // 当前进度时间
            .fontColor(Color.White)

          Slider({
            min: 0,
            max: this.totalTime,
            value: this.currentTime
          })
            .onChange((time)=>{
              AvPlayerManager.seek(time)
            })
            .layoutWeight(1)

          Text(this.formatTime(this.totalTime)) //歌曲总时间
            .fontColor(Color.White)
        }
        .padding({ left: 10, right: 10, bottom: 20 })

        //   控制按钮
        Row() {
          Image($r('app.media.ic_repeat'))
            .width(30)

          Image($r('app.media.ic_prev'))
            .width(30)
            .fillColor(Color.White)

          Image($r('app.media.ic_play'))
            .width(45)
            .fillColor(Color.White)


          Image($r('app.media.ic_next'))
            .width(30)
            .fillColor(Color.White)

          Image($r('app.media.ic_song_list'))
            .fillColor(Color.White)
            .width(30)
        }
        .width('100%')
        .justifyContent(FlexAlign.SpaceAround)
        .margin({ bottom: -30 })
      }
      .width('100%')
      .justifyContent(FlexAlign.SpaceAround)
    }
    .justifyContent(FlexAlign.SpaceAround)
    .height('100%')
    .width('100%')
    // 线性渐变
    .linearGradient({
      angle: 180, // 颜色从上往下变化
      colors: [ // 渐变颜色数组
        ['#7f797a', 0.1], //灰色
        ['#b43038', 0.5], // 红色
        ['#b43038', 0.8], // 红色
        ['#b43038', 1], // 红色
      ]
    })
  }
}

关键是

 .onChange((time)=>{
              AvPlayerManager.seek(time)
            })

该方法能够跳转到AvPlayerManager的任意时间点。

2.歌曲暂停和播放控制和歌曲上一首、下一首播放控制

 src/main/ets/pages/playPage.ets:

import { AvPlayerManager, songItemType } from '../services/AvPlayerManager'
import emitter from '@ohos.events.emitter'

@Entry
@Component
struct PlayPage {
  @State angle: number = 0 // 控制角度变化值 0 - 360度改变
  @State totalTime: number = 0
  @State currentTime: number = 0
  @State isplaying: boolean = true
  @State currentMusic: songItemType = Object()

  aboutToAppear() {

    // 当页面进入的时候,将当前播放歌曲的数据赋值给全局变量currentMusci
    this.currentMusic = AvPlayerManager.songs[AvPlayerManager.index]
    // 注册emiiter接收上一首,下一首歌曲播放的时候新传入的歌曲对象
    emitter.on({ eventId: 0 }, (res) => {
      this.currentMusic = JSON.parse(res.data['item'])
    })


    this.totalTime = AvPlayerManager.avPlayer.duration // 表示当前正在播放歌曲的总时间,单位是毫秒

    //  不停的获取当前歌曲的已播放时长
    AvPlayerManager.avPlayer.on('timeUpdate', (time) => {
      //   time表示当前歌曲已经播放的时长,单位是毫秒
      this.currentTime = time
    })
  }

  // 作用:接收一个毫秒数值,转成成 00:00这种格式的字符串返回
  formatTime(time: number) {
    // 1. 计算出分钟数
    let seconds = time / 1000 //将毫秒数转成总秒数
    let min = Math.floor(seconds / 60) // 获取到了分钟部分

    // 2. 计算出除开分钟之外的剩余的秒数
    let s = Math.ceil(seconds % 60) // Math.ceil(19.89) -> 20

    return `${min.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`
  }

  build() {
    Column() {
      //   CD唱片
      Stack() {
        Image($r('app.media.ic_cd'))
          .height(300)
          .borderRadius(300)

        Image(this.currentMusic.img)
          .height(200)
          .borderRadius(200)
      }
      // 控制一个元素旋转 (这个属性一定要写在animation前面,否则不会触发动画)
      .rotate({ angle: this.angle })
      // 元素加载完毕之后触发一个回调函数 .onAppear
      .onAppear(() => {
        this.angle = 360 //将角度改成360都
      })
      // 增加旋转的动画效果
      .animation({
        duration: 10000, // 用10秒钟的时间让元素旋转一圈
        curve: Curve.Linear, // 匀速旋转
        iterations: -1 //表示设置旋转的圈数,如果是-1则表示不停的转,如果是正数则表示转的圈数
      })


      //   歌曲信息
      Column({ space: 5 }) {
        Text(this.currentMusic.name)
          .fontSize(25)
          .fontWeight(800)
          .fontColor(Color.White)

        Text(this.currentMusic.author)
          .fontSize(15)
          .fontColor(Color.White)
      }
      .width('100%')

      // 播放进度+控制按钮容器
      Column() {
        //   播放进度
        Row({ space: 5 }) {
          Text(this.formatTime(this.currentTime)) // 当前进度时间
            .fontColor(Color.White)

          Slider({
            min: 0,
            max: this.totalTime,
            value: this.currentTime
          })
            .onChange((time) => {
              AvPlayerManager.seek(time)
            })
            .layoutWeight(1)

          Text(this.formatTime(this.totalTime)) //歌曲总时间
            .fontColor(Color.White)
        }
        .padding({ left: 10, right: 10, bottom: 20 })

        //   控制按钮
        Row() {
          Image($r('app.media.ic_repeat'))
            .width(30)

          Image($r('app.media.ic_prev'))
            .width(30)
            .fillColor(Color.White)
            .onClick(() => {
              AvPlayerManager.pre()
            })

          Image(this.isplaying ? $r('app.media.ic_paused') : $r('app.media.ic_play'))
            .width(45)
            .fillColor(Color.White)
            .onClick(() => {
              if (this.isplaying == true) {
                // 暂停歌曲
                AvPlayerManager.pause()
                this.isplaying = false
              } else {
                // 播放歌曲
                AvPlayerManager.reWork()
                this.isplaying = true
              }
            })


          Image($r('app.media.ic_next'))
            .width(30)
            .fillColor(Color.White)
            .onClick(() => {
              AvPlayerManager.next()
            })

          Image($r('app.media.ic_song_list'))
            .fillColor(Color.White)
            .width(30)
        }
        .width('100%')
        .justifyContent(FlexAlign.SpaceAround)
        .margin({ bottom: -30 })
      }
      .width('100%')
      .justifyContent(FlexAlign.SpaceAround)
    }
    .justifyContent(FlexAlign.SpaceAround)
    .height('100%')
    .width('100%')
    // 线性渐变
    .linearGradient({
      angle: 180, // 颜色从上往下变化
      colors: [ // 渐变颜色数组
        ['#7f797a', 0.1], //灰色
        ['#b43038', 0.5], // 红色
        ['#b43038', 0.8], // 红色
        ['#b43038', 1], // 红色
      ]
    })
  }
}

主要实现:

@State currentMusic: songItemType = Object()

  aboutToAppear() {

    // 当页面进入的时候,将当前播放歌曲的数据赋值给全局变量currentMusci
    this.currentMusic = AvPlayerManager.songs[AvPlayerManager.index]
    // 注册emiiter接收上一首,下一首歌曲播放的时候新传入的歌曲对象
    emitter.on({ eventId: 0 }, (res) => {
      this.currentMusic = JSON.parse(res.data['item'])
    })

emitter.on能够接收AvPlayerManager发来的歌曲信息。

 Image(this.currentMusic.img)

 Text(this.currentMusic.name)

  Text(this.currentMusic.author)

常量改为变量。

 Image($r('app.media.ic_prev'))
            .width(30)
            .fillColor(Color.White)
            .onClick(() => {
              AvPlayerManager.pre()
            })

在上一首按钮增加onclick属性,调用 AvPlayerManager.pre(),实现上一首跳转。

 Image($r('app.media.ic_next'))
            .width(30)
            .fillColor(Color.White)
            .onClick(() => {
              AvPlayerManager.next()
            })

同样,下一首图片增加onclick属性,调用 AvPlayerManager.next(),实现下一首跳转。

3.歌曲列表

 点击PlayPage页面的三角箭头,能够显示出被折叠的歌曲。

src/main/ets/pages/playPage.ets:

import { AvPlayerManager, songItemType } from '../services/AvPlayerManager'
import emitter from '@ohos.events.emitter'

@Entry
@Component
struct PlayPage {
  @State angle: number = 0 // 控制角度变化值 0 - 360度改变
  @State totalTime: number = 0
  @State currentTime: number = 0
  @State isplaying: boolean = true
  @State currentMusic: songItemType = Object()
  @State h:number = 0 //控制底部歌曲列表容器的高度

  aboutToAppear() {

    // 当页面进入的时候,将当前播放歌曲的数据赋值给全局变量currentMusci
    this.currentMusic = AvPlayerManager.songs[AvPlayerManager.index]
    // 注册emiiter接收上一首,下一首歌曲播放的时候新传入的歌曲对象
    emitter.on({ eventId: 0 }, (res) => {
      this.currentMusic = JSON.parse(res.data['item'])
    })

    //  不停的获取当前歌曲的已播放时长
    AvPlayerManager.avPlayer.on('timeUpdate', (time) => {
      if (AvPlayerManager.avPlayer.state == 'playing') {
        this.isplaying = true
      } else {
        this.isplaying = false
      }
      //   time表示当前歌曲已经播放的时长,单位是毫秒
      this.currentTime = time

      // 放到这里能够解决切歌时候不能获取最新歌曲的总时长问题
      this.totalTime = AvPlayerManager.avPlayer.duration // 表示当前正在播放歌曲的总时间,单位是毫秒
    })
  }

  // 作用:接收一个毫秒数值,转成成 00:00这种格式的字符串返回
  formatTime(time: number) {
    // 1. 计算出分钟数
    let seconds = time / 1000 //将毫秒数转成总秒数
    let min = Math.floor(seconds / 60) // 获取到了分钟部分

    // 2. 计算出除开分钟之外的剩余的秒数
    let s = Math.ceil(seconds % 60) // Math.ceil(19.89) -> 20

    return `${min.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`
  }

  build() {
    Stack({alignContent:Alignment.Bottom}) {
      Column() {
        //   CD唱片
        Stack() {
          Image($r('app.media.ic_cd'))
            .height(300)
            .borderRadius(300)

          Image(this.currentMusic.img)
            .height(200)
            .borderRadius(200)
        }
        // 控制一个元素旋转 (这个属性一定要写在animation前面,否则不会触发动画)
        .rotate({ angle: this.angle })
        // 元素加载完毕之后触发一个回调函数 .onAppear
        .onAppear(() => {
          this.angle = 360 //将角度改成360都
        })
        // 增加旋转的动画效果
        .animation({
          duration: 10000, // 用10秒钟的时间让元素旋转一圈
          curve: Curve.Linear, // 匀速旋转
          iterations: -1 //表示设置旋转的圈数,如果是-1则表示不停的转,如果是正数则表示转的圈数
        })


        //   歌曲信息
        Column({ space: 5 }) {
          Text(this.currentMusic.name)
            .fontSize(25)
            .fontWeight(800)
            .fontColor(Color.White)

          Text(this.currentMusic.author)
            .fontSize(15)
            .fontColor(Color.White)
        }
        .width('100%')

        // 播放进度+控制按钮容器
        Column() {
          //   播放进度
          Row({ space: 5 }) {
            Text(this.formatTime(this.currentTime)) // 当前进度时间
              .fontColor(Color.White)

            Slider({
              min: 0,
              max: this.totalTime,
              value: this.currentTime
            })
              .onChange((time) => {
                AvPlayerManager.seek(time)
              })
              .layoutWeight(1)

            Text(this.formatTime(this.totalTime)) //歌曲总时间
              .fontColor(Color.White)
          }
          .padding({ left: 10, right: 10, bottom: 20 })

          //   控制按钮
          Row() {
            Image($r('app.media.ic_repeat'))
              .width(30)

            Image($r('app.media.ic_prev'))
              .width(30)
              .fillColor(Color.White)
              .onClick(() => {
                AvPlayerManager.pre()
              })

            Image(this.isplaying ? $r('app.media.ic_paused') : $r('app.media.ic_play'))
              .width(45)
              .fillColor(Color.White)
              .onClick(() => {
                if (this.isplaying == true) {
                  // 暂停歌曲
                  AvPlayerManager.pause()
                  // this.isplaying = false
                } else {
                  // 播放歌曲
                  AvPlayerManager.reWork()
                  // this.isplaying = true
                }
              })


            Image($r('app.media.ic_next'))
              .width(30)
              .fillColor(Color.White)
              .onClick(() => {
                AvPlayerManager.next()
              })

            Image($r('app.media.ic_song_list'))
              .fillColor(Color.White)
              .width(30)
              .onClick(()=>{
                this.h = 400
              })
          }
          .width('100%')
          .justifyContent(FlexAlign.SpaceAround)
          .margin({ bottom: -30 })
        }
        .width('100%')
        .justifyContent(FlexAlign.SpaceAround)
      }
      .justifyContent(FlexAlign.SpaceAround)
      .height('100%')
      .width('100%')
      // 线性渐变
      .linearGradient({
        angle: 180, // 颜色从上往下变化
        colors: [ // 渐变颜色数组
          ['#7f797a', 0.1], //灰色
          ['#b43038', 0.5], // 红色
          ['#b43038', 0.8], // 红色
          ['#b43038', 1], // 红色
        ]
      })

      // 播放歌曲列表容器
      Column() {
        // 上方
        Row(){
          Row({space:10}){
            Image($r('app.media.ic_play'))
              .height(25)
              .fillColor('#d2577c')

            Text('播放列表(16)')
              .fontColor(Color.White)
              .fontSize(18)
          }

          Image($r('app.media.ic_close'))
            .height(25)
            .onClick(()=>{
              // 关闭列表容器
              this.h = 0
            })
        }
        .padding(20)
        .width('100%')
        .justifyContent(FlexAlign.SpaceBetween)

        // 下方
        List(){
          ListItem(){
            Row() {
              Text('1')
                .fontColor(Color.White)
                .width(40)
                .textAlign(TextAlign.Center)
              Row({ space: 10 }) {
                Column({space:5}) {
                  Text('歌曲名称')
                    .fontColor('#ff9fa0a1')
                    .fontSize(16)
                    .width('100%')

                  Text('作者')
                    .fontColor('#ff9fa0a1')
                    .fontSize(13)
                    .width('100%')
                }
              }
              .layoutWeight(1)

              Image($r('app.media.ic_more'))
                .fillColor(Color.White)
                .width(20)
            }
            .width('100%')
            .padding({ top: 8, bottom: 8,right:10 })
          }
        }
      }
      .width('100%')
      .height(this.h)  //控制高度变化
      .backgroundColor('#ff2c2c2c')
      .animation({
        duration:500  //控制动画显示
      })

    }
  }
}

@State h:number = 0 //控制底部歌曲列表容器的高度

定义一个变量h,初始值为0

Image($r('app.media.ic_song_list'))
              .fillColor(Color.White)
              .width(30)
              .onClick(()=>{
                this.h = 400
              })

第一次点击,h赋值为400的正常高度,歌曲列表正常出现

Image($r('app.media.ic_close'))
            .height(25)
            .onClick(()=>{
              // 关闭列表容器
              this.h = 0
            })

再次点击,列表隐藏。

.width('100%')
      .height(this.h)  //控制高度变化
      .backgroundColor('#ff2c2c2c')
      .animation({
        duration:500  //控制动画显示
      })

然后增加过渡动画等属性,注意容器顺序别搞混。

4.歌曲列表数据动态化和背景雾化

同样在PlayPage页面

import { AvPlayerManager, songItemType } from '../services/AvPlayerManager'
import emitter from '@ohos.events.emitter'

@Entry
@Component
struct PlayPage {
  @State angle: number = 0 // 控制角度变化值 0 - 360度改变
  @State totalTime: number = 0
  @State currentTime: number = 0
  @State isplaying: boolean = true
  @State currentMusic: songItemType = Object()
  @State h: number = 0 //控制底部歌曲列表容器的高度

  aboutToAppear() {

    // 当页面进入的时候,将当前播放歌曲的数据赋值给全局变量currentMusci
    this.currentMusic = AvPlayerManager.songs[AvPlayerManager.index]
    // 注册emiiter接收上一首,下一首歌曲播放的时候新传入的歌曲对象
    emitter.on({ eventId: 0 }, (res) => {
      this.currentMusic = JSON.parse(res.data['item'])
    })

    //  不停的获取当前歌曲的已播放时长
    AvPlayerManager.avPlayer.on('timeUpdate', (time) => {
      if (AvPlayerManager.avPlayer.state == 'playing') {
        this.isplaying = true
      } else {
        this.isplaying = false
      }
      //   time表示当前歌曲已经播放的时长,单位是毫秒
      this.currentTime = time

      // 放到这里能够解决切歌时候不能获取最新歌曲的总时长问题
      this.totalTime = AvPlayerManager.avPlayer.duration // 表示当前正在播放歌曲的总时间,单位是毫秒
    })
  }

  // 作用:接收一个毫秒数值,转成成 00:00这种格式的字符串返回
  formatTime(time: number) {
    // 1. 计算出分钟数
    let seconds = time / 1000 //将毫秒数转成总秒数
    let min = Math.floor(seconds / 60) // 获取到了分钟部分

    // 2. 计算出除开分钟之外的剩余的秒数
    let s = Math.ceil(seconds % 60) // Math.ceil(19.89) -> 20

    return `${min.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`
  }

  build() {
    Stack(){
      Image(this.currentMusic.img)
        .height('100%')
        .width('100%')
        .blur(1000) // 将图片雾化-> 渐变

      Stack({ alignContent: Alignment.Bottom }) {
        Column() {
          //   CD唱片
          Stack() {
            Image($r('app.media.ic_cd'))
              .height(300)
              .borderRadius(300)

            Image(this.currentMusic.img)
              .height(200)
              .borderRadius(200)
          }
          // 控制一个元素旋转 (这个属性一定要写在animation前面,否则不会触发动画)
          .rotate({ angle: this.isplaying?this.angle:0 })
          // 元素加载完毕之后触发一个回调函数 .onAppear
          .onAppear(() => {
            this.angle = 360 //将角度改成360都
          })
          // 增加旋转的动画效果
          .animation({
            duration: 10000, // 用10秒钟的时间让元素旋转一圈
            curve: Curve.Linear, // 匀速旋转
            iterations: -1 //表示设置旋转的圈数,如果是-1则表示不停的转,如果是正数则表示转的圈数
          })


          //   歌曲信息
          Column({ space: 5 }) {
            Text(this.currentMusic.name)
              .fontSize(25)
              .fontWeight(800)
              .fontColor(Color.White)

            Text(this.currentMusic.author)
              .fontSize(15)
              .fontColor(Color.White)
          }
          .width('100%')

          // 播放进度+控制按钮容器
          Column() {
            //   播放进度
            Row({ space: 5 }) {
              Text(this.formatTime(this.currentTime)) // 当前进度时间
                .fontColor(Color.White)

              Slider({
                min: 0,
                max: this.totalTime,
                value: this.currentTime
              })
                .onChange((time) => {
                  AvPlayerManager.seek(time)
                })
                .layoutWeight(1)

              Text(this.formatTime(this.totalTime)) //歌曲总时间
                .fontColor(Color.White)
            }
            .padding({ left: 10, right: 10, bottom: 20 })

            //   控制按钮
            Row() {
              Image($r('app.media.ic_repeat'))
                .width(30)

              Image($r('app.media.ic_prev'))
                .width(30)
                .fillColor(Color.White)
                .onClick(() => {
                  AvPlayerManager.pre()
                })

              Image(this.isplaying ? $r('app.media.ic_paused') : $r('app.media.ic_play'))
                .width(45)
                .fillColor(Color.White)
                .onClick(() => {
                  if (this.isplaying == true) {
                    // 暂停歌曲
                    AvPlayerManager.pause()
                    // this.isplaying = false
                  } else {
                    // 播放歌曲
                    AvPlayerManager.reWork()
                    // this.isplaying = true
                  }
                })


              Image($r('app.media.ic_next'))
                .width(30)
                .fillColor(Color.White)
                .onClick(() => {
                  AvPlayerManager.next()
                })

              Image($r('app.media.ic_song_list'))
                .fillColor(Color.White)
                .width(30)
                .onClick(() => {
                  this.h = 400
                })
            }
            .width('100%')
            .justifyContent(FlexAlign.SpaceAround)
            .margin({ bottom: -30 })
          }
          .width('100%')
          .justifyContent(FlexAlign.SpaceAround)
        }
        .justifyContent(FlexAlign.SpaceAround)
        .height('100%')
        .width('100%')
        // 线性渐变
        // .linearGradient({
        //   angle: 180, // 颜色从上往下变化
        //   colors: [ // 渐变颜色数组
        //     ['#7f797a', 0.1], //灰色
        //     ['#b43038', 0.5], // 红色
        //     ['#b43038', 0.8], // 红色
        //     ['#b43038', 1], // 红色
        //   ]
        // })

        // 播放歌曲列表容器
        Column() {
          // 上方
          Row() {
            Row({ space: 10 }) {
              Image($r('app.media.ic_play'))
                .height(25)
                .fillColor('#d2577c')

              Text('播放列表(16)')
                .fontColor(Color.White)
                .fontSize(18)
            }

            Image($r('app.media.ic_close'))
              .height(25)
              .onClick(() => {
                // 关闭列表容器
                this.h = 0
              })
          }
          .padding(20)
          .width('100%')
          .justifyContent(FlexAlign.SpaceBetween)

          // 下方
          List() {
            ForEach(AvPlayerManager.songs, (item: songItemType, index: number) => {
              ListItem() {
                Row() {
                  Text((index+1).toString())
                    .fontColor(Color.White)
                    .width(40)
                    .textAlign(TextAlign.Center)
                  Row({ space: 10 }) {
                    Column({ space: 5 }) {
                      Text(item.name)
                        .fontColor(item.url == this.currentMusic.url
                          ?'#d2577c':'#ff9fa0a1')
                        .fontSize(16)
                        .width('100%')

                      Text(item.author)
                        .fontColor(item.url == this.currentMusic.url
                          ?'#d2577c':'#ff9fa0a1')
                        .fontSize(13)
                        .width('100%')
                    }
                  }
                  .layoutWeight(1)

                  Image($r('app.media.ic_more'))
                    .fillColor(Color.White)
                    .width(20)
                }
                .width('100%')
                .padding({ top: 8, bottom: 8, right: 10 })
              }
              .onClick(() => {
                AvPlayerManager.playMusic(item) // 播放歌曲
                AvPlayerManager.sendMusicData(item) // 通知index.est更换歌曲信息
                AvPlayerManager.index = index // 更新播放歌曲的索引
                this.currentMusic = item  // 改变当前歌曲数据
              })
            })
          }
        }
        .width('100%')
        .height(this.h)
        .backgroundColor('#ff2c2c2c')
        .animation({
          duration: 500
        })

      }
    }
  }
}

Stack(){
      Image(this.currentMusic.img)
        .height('100%')
        .width('100%')
        .blur(1000) // 将图片雾化-> 渐变

使用层叠容器,给背景图片增加blur模糊属性,达到雾化效果。

 Text(item.name)
                        .fontColor(item.url == this.currentMusic.url
                          ?'#d2577c':'#ff9fa0a1')

Text(item.author)
                        .fontColor(item.url == this.currentMusic.url
                          ?'#d2577c':'#ff9fa0a1')

三元表达式实现点击歌曲后,歌曲与歌手名字动态改变颜色。

5.唱针效果

import { AvPlayerManager, songItemType } from '../services/AvPlayerManager'
import emitter from '@ohos.events.emitter'

@Entry
@Component
struct PlayPage {
  @State angle: number = 0 // 控制角度变化值 0 - 360度改变
  @State angle1: number = -55 // 控制唱针

  @State totalTime: number = 0
  @State currentTime: number = 0
  @State isplaying: boolean = true
  @State currentMusic: songItemType = Object()
  @State h: number = 0 //控制底部歌曲列表容器的高度

  aboutToAppear() {

    // 当页面进入的时候,将当前播放歌曲的数据赋值给全局变量currentMusci
    this.currentMusic = AvPlayerManager.songs[AvPlayerManager.index]
    // 注册emiiter接收上一首,下一首歌曲播放的时候新传入的歌曲对象
    emitter.on({ eventId: 0 }, (res) => {
      this.currentMusic = JSON.parse(res.data['item'])
    })

    //  不停的获取当前歌曲的已播放时长
    AvPlayerManager.avPlayer.on('timeUpdate', (time) => {
      if (AvPlayerManager.avPlayer.state == 'playing') {
        this.isplaying = true  //表示当前正在播放歌曲
        this.angle1 = -40
      } else {
        this.isplaying = false
        this.angle1 = -55
      }
      //   time表示当前歌曲已经播放的时长,单位是毫秒
      this.currentTime = time

      // 放到这里能够解决切歌时候不能获取最新歌曲的总时长问题
      this.totalTime = AvPlayerManager.avPlayer.duration // 表示当前正在播放歌曲的总时间,单位是毫秒
    })
  }

  // 作用:接收一个毫秒数值,转成成 00:00这种格式的字符串返回
  formatTime(time: number) {
    // 1. 计算出分钟数
    let seconds = time / 1000 //将毫秒数转成总秒数
    let min = Math.floor(seconds / 60) // 获取到了分钟部分

    // 2. 计算出除开分钟之外的剩余的秒数
    let s = Math.ceil(seconds % 60) // Math.ceil(19.89) -> 20

    return `${min.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`
  }

  build() {
    Stack(){
      Image(this.currentMusic.img)
        .height('100%')
        .width('100%')
        .blur(1000) // 将图片雾化-> 渐变

      Stack({ alignContent: Alignment.Bottom }) {
        Column() {
         Stack({alignContent:Alignment.Top}){
           //   CD唱片
           Stack() {
             Image($r('app.media.ic_cd'))
               .height(300)
               .borderRadius(300)

             Image(this.currentMusic.img)
               .height(200)
               .borderRadius(200)
           }
           // 控制一个元素旋转 (这个属性一定要写在animation前面,否则不会触发动画)
           .rotate({ angle: this.isplaying?this.angle:0 })
           // 元素加载完毕之后触发一个回调函数 .onAppear
           .onAppear(() => {
             this.angle = 360 //将角度改成360都
           })
           // 增加旋转的动画效果
           .animation({
             duration: 10000, // 用10秒钟的时间让元素旋转一圈
             curve: Curve.Linear, // 匀速旋转
             iterations: -1 //表示设置旋转的圈数,如果是-1则表示不停的转,如果是正数则表示转的圈数
           })

         // 唱针
           Image($r('app.media.ic_stylus'))
             .height(220)
             .margin({top:-60})
             .rotate({
               angle:this.angle1, //旋转角度,调整过后发现 -40°可以落到cd唱片上,-55°离开cd唱片
               centerX:110,  // 设置旋转的中心点的x位置
               centerY:30  // 设置旋转的中心点的y位置
             })
             .animation({
               duration:500  //这里表示角度变化是在500毫秒以内完成
             })
         }


          //   歌曲信息
          Column({ space: 5 }) {
            Text(this.currentMusic.name)
              .fontSize(25)
              .fontWeight(800)
              .fontColor(Color.White)

            Text(this.currentMusic.author)
              .fontSize(15)
              .fontColor(Color.White)
          }
          .width('100%')

          // 播放进度+控制按钮容器
          Column() {
            //   播放进度
            Row({ space: 5 }) {
              Text(this.formatTime(this.currentTime)) // 当前进度时间
                .fontColor(Color.White)

              Slider({
                min: 0,
                max: this.totalTime,
                value: this.currentTime
              })
                .onChange((time) => {
                  AvPlayerManager.seek(time)
                })
                .layoutWeight(1)

              Text(this.formatTime(this.totalTime)) //歌曲总时间
                .fontColor(Color.White)
            }
            .padding({ left: 10, right: 10, bottom: 20 })

            //   控制按钮
            Row() {
              Image($r('app.media.ic_repeat'))
                .width(30)

              Image($r('app.media.ic_prev'))
                .width(30)
                .fillColor(Color.White)
                .onClick(() => {
                  AvPlayerManager.pre()
                })

              Image(this.isplaying ? $r('app.media.ic_paused') : $r('app.media.ic_play'))
                .width(45)
                .fillColor(Color.White)
                .onClick(() => {
                  if (this.isplaying == true) {
                    // 暂停歌曲
                    AvPlayerManager.pause()
                    // this.isplaying = false
                  } else {
                    // 播放歌曲
                    AvPlayerManager.reWork()
                    // this.isplaying = true
                  }
                })


              Image($r('app.media.ic_next'))
                .width(30)
                .fillColor(Color.White)
                .onClick(() => {
                  AvPlayerManager.next()
                })

              Image($r('app.media.ic_song_list'))
                .fillColor(Color.White)
                .width(30)
                .onClick(() => {
                  this.h = 400
                })
            }
            .width('100%')
            .justifyContent(FlexAlign.SpaceAround)
            .margin({ bottom: -30 })
          }
          .width('100%')
          .justifyContent(FlexAlign.SpaceAround)
        }
        .justifyContent(FlexAlign.SpaceAround)
        .height('100%')
        .width('100%')
        // 线性渐变
        .linearGradient({
          angle: 180, // 颜色从上往下变化
          colors: [ // 渐变颜色数组
            ['#7f797a', 0.1], //灰色
            ['#b43038', 0.5], // 红色
            ['#b43038', 0.8], // 红色
            ['#b43038', 1], // 红色
          ]
        })

        // 播放歌曲列表容器
        Column() {
          // 上方
          Row() {
            Row({ space: 10 }) {
              Image($r('app.media.ic_play'))
                .height(25)
                .fillColor('#d2577c')

              Text('播放列表(16)')
                .fontColor(Color.White)
                .fontSize(18)
            }

            Image($r('app.media.ic_close'))
              .height(25)
              .onClick(() => {
                // 关闭列表容器
                this.h = 0
              })
          }
          .padding(20)
          .width('100%')
          .justifyContent(FlexAlign.SpaceBetween)

          // 下方
          List() {
            ForEach(AvPlayerManager.songs, (item: songItemType, index: number) => {
              ListItem() {
                Row() {
                  Text((index+1).toString())
                    .fontColor(Color.White)
                    .width(40)
                    .textAlign(TextAlign.Center)
                  Row({ space: 10 }) {
                    Column({ space: 5 }) {
                      Text(item.name)
                        .fontColor(item.url == this.currentMusic.url
                          ?'#d2577c':'#ff9fa0a1')
                        .fontSize(16)
                        .width('100%')

                      Text(item.author)
                        .fontColor(item.url == this.currentMusic.url
                          ?'#d2577c':'#ff9fa0a1')
                        .fontSize(13)
                        .width('100%')
                    }
                  }
                  .layoutWeight(1)

                  Image($r('app.media.ic_more'))
                    .fillColor(Color.White)
                    .width(20)
                }
                .width('100%')
                .padding({ top: 8, bottom: 8, right: 10 })
              }
              .onClick(() => {
                AvPlayerManager.playMusic(item) // 播放歌曲
                AvPlayerManager.sendMusicData(item) // 通知index.est更换歌曲信息
                AvPlayerManager.index = index // 更新播放歌曲的索引
                this.currentMusic = item  // 改变当前歌曲数据
              })
            })
          }
        }
        .width('100%')
        .height(this.h)
        .backgroundColor('#ff2c2c2c')
        .animation({
          duration: 500
        })

      }
    }
  }
}

@State angle1: number = -55 // 控制唱针

定义变量angle1,赋值-55,

if (AvPlayerManager.avPlayer.state == 'playing') {
        this.isplaying = true  //表示当前正在播放歌曲
        this.angle1 = -40
      } else {
        this.isplaying = false
        this.angle1 = -55
      }

之后判断歌曲状态,如果正在播放,angle1 = -40,唱针转动到唱片上,如果未播放,angle1 = -55,唱针离开唱片。

6.结语

至此,鸿蒙开发主要页面和功能介绍完毕,发现页面和推荐页面实现和前面几个页面类似,不再讲述,如果想要学习新的功能,可以自行搜索学习。

后续还有新的功能的话,我会持续在该栏更新。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2100820.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

本地大语言模型部署及应用

01 模型 2024-07-24,Meta 正式发布新一代开源大模型 Llama 3.1 系列,该模型共有三个版本: 8B70B405B meta评估了超过 150 个语言覆盖范围广的基准数据集。比较了 Llama 3.1 与竞争性模型在真实世界场景下的表现。实验评估表明,L…

动态住宅IP代理的搭建指南:实现高效网络访问

在互联网的复杂环境中,动态住宅IP代理逐渐成为提升网络隐私和绕过IP限制的热门选择。相比于数据中心代理IP,动态住宅IP代理具有更高的隐蔽性和真实性,能够有效模拟真实用户的网络行为。本文将详细介绍动态住宅IP代理的搭建方法,帮…

iPhone、微信二选一?苹果、腾讯官方回应

KlipC报道:9月2日,有消息称,腾讯和苹果正在就APPStore的抽成问题在不断交涉,如若谈崩有可能导致微信下架,iPhone一旦升级到 iOS 18.2 系统可能将无法使用微信。 对此,苹果中国区技术顾问表示,“…

Vue 2 与 Vue 3 中 keep-alive 组件的深入解析

更多内容前往个人网站:孔乙己大叔 在 Vue.js 的开发中,keep-alive 是一个非常重要的内置组件,它允许我们将组件状态或实例保持在内存中,避免重新渲染和重新创建组件,这对于需要频繁切换但内容较为复杂的组件场景非常有…

如何在银河麒麟中快速查询网卡固件及驱动信息

如何在银河麒麟中快速查询网卡固件及驱动信息 1、第一步:找到网卡名称2、第二步:查询固件和驱动信息3、总结 💖The Begin💖点点关注,收藏不迷路💖 在银河麒麟服务器操作系统中,如何查询网卡固件…

统一NLP和目标检测的DETR(一)——self attention、encoder、decoder

主流目标检测算法劣势 YOLO系列,它基于anchor来做,少不了要用MNS,导致速度相对较慢。 但今天介绍一款DETR,基于VIT的目标检测算法。 那么我们需要先深入过一遍VIT。 Vision transform 1、传统RNN网络的问题 单向:只…

vue3集成sql语句编辑器

使用的是codemirror 安装 pnpm add codemirror vue-codemirror --savepnpm add codemirror/lang-sqlpnpm add codemirror/theme-one-dark使用 <template><codemirror v-model"configSql" placeholder"Code goes here..." ref"codemirrorR…

热力图科普:数据可视化的利器

hello大家好&#xff0c;俺是没事爱瞎捣鼓又分享欲爆棚的叶同学&#xff01;&#xff01;&#xff01; 日常闲扯 哎呀&#xff0c;第一天上完课&#xff0c;给俺的感觉是&#xff08;热和惊喜&#xff09;&#xff0c;热是真热&#xff0c;从出租屋走到教学楼给我整的汗流浃背…

十七、网络编程

文章目录 一、网络相关的概念二、InetAddresss类三、Socket3.1 基本介绍 四、TCP网络通信编程4.1 应用案例1(使用字节流)4.2 应用案例2(使用字节流)4.3 应用案例3(使用字符流)4.4 应用案例4&#xff08;TCPFileCopy&#xff09;4.5 netstat 指令4.6 TCP网络通讯不为人知的秘密 …

为啥一定要考HCIE安全?这4个理由你不得不看

大家都知道&#xff0c;网络安全越来越成为企业和个人关注的焦点。 随着网络攻击的日益复杂和频繁&#xff0c;对于具备高级网络安全技能的专业人才的需求也在不断增长。HCIE-Security认证&#xff0c;作为华为推出的顶级信息安全认证&#xff0c;正是满足这一需求的黄金标准。…

inflight 守恒化简

设 x 为 E bw / delay 效能&#xff0c;y 为 flow 在瓶颈处的实际分配带宽&#xff0c;r 为时延&#xff0c;inflt 守恒模型的方程组如下&#xff1a; I ( t ) ∑ i 0 n w i ( t ) I(t)\displaystyle\sum_{i0}^n w_i(t) I(t)i0∑n​wi​(t) d x d t y r − x \dfrac{dx}{dt…

论文速读|自然语言的最优控制合成:机遇与挑战

项目地址&#xff1a;Optimal Control Synthesis from Natural Language: Opportunities and Challenges 介绍了一种从自然语言自动生成最优控制器的框架&#xff0c;该框架主要包括以下几个步骤&#xff1a;首先&#xff0c;通过人类用户提供的初始文本和系统描述&#xff0c;…

苹果系统中如何安装Python和PyCharm

1、Python官网下载安装包 Python官网下载苹果版本的安装包。 2、PyCharm官网下载安装包 3、下载完毕后安装Python 我采用的是在Windows下下载安装包&#xff0c;然后移动硬盘挂接到MacOS的方式进行安装。 双击开始安装Python Python安装完毕 4、Python安装完毕后的测定 注意直…

【开源免费】基于SpringBoot+Vue.JS高校校园招聘服务系统(JAVA毕业设计)

本文项目编号 T 010 &#xff0c;文末自助获取源码 \color{red}{T010&#xff0c;文末自助获取源码} T010&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 查…

Qt QPushButton 按钮添加数字气泡

使用场景 项目中&#xff0c;在某个按钮的右上角添加数字气泡是一个很常见的功能&#xff0c;可以用新建一个组合类来实现。不过这样比较麻烦&#xff0c;如果加气泡是后面的需求&#xff0c;可能改动的地方较多。 下面介绍2种比较简单&#xff0c;不需要改动按钮响应的方式。…

Web3社交新经济,与 SOEX 实现无缝交易的高级安全性

出于充分的理由&#xff0c;安全性是交易中至关重要的考虑因素。每个人都应该确保自己的资金在交易时是安全的。由于 &#xff33;&#xff2f;&#xff25;&#xff38; 充当您与交易所的最佳连接&#xff0c;因此必须强调的是&#xff0c;该系统不会引发任何安全问题。 &a…

央视曝光“硫超标”枸杞,记者直击加工现场:被熏到泪流不止

枸杞&#xff0c;这一自古以来便被誉为“药食同源”的珍贵食材&#xff0c;不仅承载着中华民族深厚的养生智慧&#xff0c;也广泛融入现代人的日常饮食与保健之中。 从食品、饮料到保健品&#xff0c;枸杞的身影无处不在&#xff0c;其独特的营养价值与药用价值深受消费者青睐。…

心电调试笔记

原理图设计 注意事项 引脚连接检查&#xff1a;确保每个元器件与芯片引脚连接正确是基础&#xff0c;错误的连接可能导致系统无法正常工作。未连接引脚标识&#xff1a;对于未使用的引脚&#xff0c;虽然不连接但应标识为非使用状态&#xff0c;以免混淆或引起误操作。测试点设…

【 html+css 绚丽Loading 】 000034 三元聚散盘

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享htmlcss 绚丽Loading&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495…

解锁MySQL数据库基础命令:从入门到精通的实战指南

作者简介&#xff1a;我是团团儿&#xff0c;是一名专注于云计算领域的专业创作者&#xff0c;感谢大家的关注 座右铭&#xff1a; 云端筑梦&#xff0c;数据为翼&#xff0c;探索无限可能&#xff0c;引领云计算新纪元 个人主页&#xff1a;团儿.-CSDN博客 前言&#xff1a;…