最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 基于React全家桶开发「网易云音乐PC」项目实战(三)

    正文概述 掘金(风不识途)   2021-01-05   810

    前言

    基于React全家桶开发「网易云音乐PC」项目实战(三)

    首页主体内容

    1.热门推荐头部组件

    Content主体布局

    要完成的预览图如下↓ 基于React全家桶开发「网易云音乐PC」项目实战(三)

    header组件封装「前言」

    • 查看需要封装的header组件 基于React全家桶开发「网易云音乐PC」项目实战(三)
    • 为什么封装:由于在当前页面下有多个类似的的头部(header)组件

    • 在当前页面中有的header组件是没有keywords关键字(也就是热门推荐后面的分类):

    • 点击查看多个header组件差异 基于React全家桶开发「网易云音乐PC」项目实战(三)

    "头部(header)组件"封装

    • 组件存放路径(参考):将header封装到src->components->ThemeHeader文件夹下

    • 组件需要依赖传递的props:

      • 组件的title(标题),必传;

      • keyword(关键字分类) (可以先写死数据),非必传;

    • 使用propTypes传递默认值

    // theme-header-rcm.js
    import propTypes from 'prop-types'
    // ...
    // 指定传递props
    ThemeHeaderRmc.propTypes = {
      // title属性必填(左侧标题)
      title: propTypes.string.isRequired,
      // 关键字(非必传,左侧关键字)
      keywords: propTypes.array
    }
    // 指定默认值
    ThemeHeaderRmc.defaultProps  = {
      keywords: []
    }
    

    实现效果

    基于React全家桶开发「网易云音乐PC」项目实战(三)

    RecommendWrapper首页外层结构划分(参考) 基于React全家桶开发「网易云音乐PC」项目实战(三)

    2.热门推荐模块->发送网络请求

    • 这个时候我们就可以来发送网络请求,将请求下来的数据保存在redux的store
    • 热门推荐API接口↓:
      • /personalized?limit=8
      • 示例:http://123.57.176.198:3000/personalized?limit=8

    一个组件需要发送网络的基本步骤

    // 1.在网络请求对应文件中封装对应的函数
    // 2.修改"当前"组件目录下store中的reducer (前提在actionTypes定义常量名)
    // 3.在actionCreator文件中添加action发送网络请求
    // 4.在组件中使用dispatch action 测试网络请求数据
    // 5(可选).定义常量 constans 用于控制limit的数量(方便后期维护修改)
    // 6.在组件中使用useSelector展示数据
    详细步骤如下?
    
    1. 网络请求接口的封装: src/service/recommend.js

      export function getHotRecommends(limit) {
        return request({
          url: "/personalized",
          params: {
            limit
          }
        })
      
    2. 修改redux 基于React全家桶开发「网易云音乐PC」项目实战(三)
    3. 添加actionCreator 基于React全家桶开发「网易云音乐PC」项目实战(三)
    4. 定义常量用于控制limit的数量, 好处是如果有一天想修改数量直接在常量文件中修改即可

    5. 在组件中使用useSelector展示数据 基于React全家桶开发「网易云音乐PC」项目实战(三)

    3.歌曲封面(song cover)组件封装

    • 公共组件位置: src/components/song-cover/index.js
    点击查看 歌曲封面(song cover)组件封装: 基于React全家桶开发「网易云音乐PC」项目实战(三)
    接口字段: 
        封面图片: picUrl
        播放数量: playCount
        封面名字: name
        封面底部文字: copywriter
    song cover组件布局思路???
    

    基于React全家桶开发「网易云音乐PC」项目实战(三)

    • 将保存在redux中热门推荐的八条数据,进行遍历(外层包裹div, 并使用flex布局, flex-wrap换行)

    4.热门推荐组件完成效果

    基于React全家桶开发「网易云音乐PC」项目实战(三)

    5.新碟上架

    网络请求

    • 新碟上架接口:

      • /top/album?limit=10 已废除
      • /album/newest
      • 示例:http://123.57.176.198:3000/album/newest
    • 将请求的数据放到reudx里面

      1. 封装网络接口请求
      2. 修改redux,添加state,添加case语句
      3. 添加actionCreatorgetNewAlbumsAction
        • 用于发送网络请求
      4. 组件派发该action测试
      5. 添加actionCreatorchangeNewAlbumAction
        • 用于在网络请求中派发该action
      查看保存的state 基于React全家桶开发「网易云音乐PC」项目实战(三)

    新碟上架组件布局

    • 轮播图: 使用Carousel(走马灯)控件

      基于React全家桶开发「网易云音乐PC」项目实战(三)

    数据截取逻辑代码

    //1.数据方面:为了保证轮播图的每1个页有5条数据:
    //  对数据进行截取: 在遍历第一页中0-5 第二页5-10数据
    //  注意:slice(方法不包括截取目标number) 
    // 2.布局方面:
    //  对class name为npage及进行flex布局
    <Carousel dots={false} ref={albumRef}>
      {[0, 1].map(item => {
        return (
          <div key={item} className="page">
            {newAlbums.slice(item * 5, (item + 1) * 5).map(cItem => {
              return (
                <div key={cItem.id} className="c-item">
                  {cItem.name}
                </div>
              )
            })}
          </div>
        )
      })}
    </Carousel>
    
    • 待完善效果?
    基于React全家桶开发「网易云音乐PC」项目实战(三)

    AlbumCover组件封装

    基于React全家桶开发「网易云音乐PC」项目实战(三)
    // AlbumCover组件要求(数据是不固定的):  宽高和bgp(背景图片横纵坐标)由调用者传递
    // 因为在其他的页面中使用该组件的尺寸和bgp是不同的
    <Carousel dots={false} ref={albumRef}>
    ...
    <AlbumCover
      key={cItem.id}
      info={cItem}
      size={100}
      bgp="-570px"
    >
      {cItem.name}
    </AlbumCover>
    ...
    </Carousel>
    
    • 完成效果?

    基于React全家桶开发「网易云音乐PC」项目实战(三)

    6.榜单

    榜单说明

    基于React全家桶开发「网易云音乐PC」项目实战(三)

    请求榜单数据

    • 榜单数据API: /top/list?idx=0 已废弃,项目接口又重新改了一遍

      • 0: 云音乐飙升榜
      • 2: 云音乐新歌榜
      • 3: 云原创歌曲榜
    • 榜单数据API:

      • 下拉查看: 首页三个榜单的id 基于React全家桶开发「网易云音乐PC」项目实战(三)
      • 云音乐飙升榜API: http://localhost:3000/playlist/detail?id=19723756

      • 云音乐新歌榜API: http://localhost:3000/playlist/detail?id=3779629

      • 网易原创欧曲榜API: http://localhost:3000/playlist/detail?id=2884035

      • 在请求下来的数据,对其中的tracks字段进行数据截取只保留前10条数据

    • 注意事项↓

    发送网络请求将请求的数据放到redux中state中 (详细步骤不在展开,和上面步骤一样)
    注意: 根据不同 id 请求不同榜单
    在派发action时可以使用switch根据不同的 id 派发不同的action
    

    榜单组件(top-ranking)封装

    • 要完成的组件封装如下?

    基于React全家桶开发「网易云音乐PC」项目实战(三)

    • 要实现效果
      • 刚开始: icons的父元素固定的width为0, hover后给固定的宽度
      • 鼠标划过 这行item 让文字溢出隐藏显示...,并显示icons
      • 鼠标离开显示原本效果,隐藏icons,固定歌曲名字宽度即可

    完成效果

    基于React全家桶开发「网易云音乐PC」项目实战(三)

    7.主体右侧

    入驻歌手(settle-singer)

    基于React全家桶开发「网易云音乐PC」项目实战(三)
    • 入驻歌手API:
      • /artist/list?limit=5&cat=5001
      • 示例:http://123.57.176.198:3000/artist/list?limit=5&cat=5001
    • 返回的JSON如下
    {
        picUrl(pin):"http://p4.music.126.net/LCWqYYKoCEZKuAC3S3lIeg==/109951165034938865.jpg"
        followed(pin):false
        briefDesc(pin):""
        name(pin):"薛之谦"
        id(pin):5781
        alias(pin):
        musicSize(pin):275
        accountId(pin):97137413
        picId_str(pin):"109951165034938865"
        img1v1Id_str(pin):"109951165034950656"
    }
    

    热门主播

    • hot-artist
      • 接口没找到,那就先写死吧,在:src/common/local-data.js 文件已经写好了
      • 返回的JSON如下
    {
      picUrl: 'http://p1.music.126.net/H3QxWdf0eUiwmhJvA4vrMQ==/1407374893913311.jpg',
      name: '陈立',
      position: '心理学家、美食家陈立教授',
      url: '/user/home?id=278438485',
    },
    

    基于React全家桶开发「网易云音乐PC」项目实战(三)

    音乐播放

    音乐播放组件(app-play-bar)

    播放器组件说明:我们在网易云音乐官网切换页面时,会发现音乐播放一直是固定在下面的,和路由切换没有关系

    • 组件存放位置: 所以我们将app-play-bar组件封装到 src/psges 文件夹?中

    基于React全家桶开发「网易云音乐PC」项目实战(三)

    布局参考

    PlayBar组件布局采用固定定位: 
     PlayerWrapper↓
     	内容(content)分了三个部分:↓
     		Control(左侧)
     			三个按钮.添加背景图,外层采用flex布局
     		PlayIInfo(中间)
     			两个部分(上、下),下面滑动条采用andt组件库Slider组件,找到类名覆盖样式即可
     		Opertaor(右侧)
     			两部分,外层采用flex布局
    

    Slider组件(进度条)样式覆盖

    Slider组件样式更改(覆盖)
      1.外层包裹背景图和宽高样式..
      2.mairign边距为0
      3.设置背景颜色为透明
      4.设置鼠标滑动时的背景图
      5.设置圆点的样式覆盖
    

    完成效果

    • 图片和一些动态获取的数据暂时先写死

    基于React全家桶开发「网易云音乐PC」项目实战(三)

    歌曲播放数据请求

    • 先固定播放一首歌曲

    • 歌曲API接口:/song/detail?ids=167876

      • 示例:http://123.57.176.198:3000/song/detail?ids=167876
    • 请求下来的数据放在哪呢?

      • 请求下的由于是歌曲信息所以就放在player文件夹下store文件夹?下的store中进行保存
      • rducer中使用immutable管理state
      • 在项目根目录,导入player文件夹下的reducer,进行combine(合并)
    step1
        添加player中reducer默认state的 currentSong: {}
        发送网络请求: player.js
        将数据保存在reducer中
    
    step2
        在plaer组件使用redux中请求来的数据: 
    	useSelector
        更改player(播放器)组件的固定数据
            currentSong.al.picUrl   图片
            currentSong.name 		歌曲名字
            currentSong.ar[0].name  作者
            currentSong.dt  (歌曲总时长,格式化)
        导入时间格式化工具(转换时间格式)
            formatDate(duration, "mm:ss")
    

    播放音乐功能

    • 歌曲播放API接口: music.163.com/song/media/…
      • id是动态的:可以从请求下来当前歌曲(currentSong)获取当前歌曲id信息

    音乐播放逻辑

    下面我们开始做音乐播放功能
      step1 
      	添加 audio 标签
    	点击 ▶播放按钮 监听click事件,添加src属性↓
      step2 音乐播放
      	使用useRef,获取audio的dom元素
      	封装: 歌曲播放`API`接口,id作为参数
      	之后点击 播放▶按钮 动态设置scr属性,调用play方法开始播放
      step3 歌曲时间显示
      	创建组件局部状态: currentTime 用于更改当前播放时间
      	audio元素有一个OnTimeUpdate事件,当歌曲事件发生变化就会被回调
      		事件参数: e.currentTime属性(用于获取当前播放时间)
      		对秒数->对时间进行格式化->formatDate(currentTime, 'mm:ss')
      step4 进度条滚动
    	控制andt的Slider组件的value值: 当前播放的进度=当前时间/总时长*100
    

    拖动滑块逻辑

    下面我们开始做拖动滑块,播放对应的进度歌曲
      需求:
        Slider组件滑动时: 当前时间会发生改变
        Slider组件抬起时: 当前歌曲进度发生改变
      step1
        1.Slider组件提供了2个api: 
          (1)onChange: 当滑块被拉动时触发,函数的参数是拖动的value
          (2)onAfterChange: 当滑抬起时触发,函数的参数是抬起时的value
        2.progress状态用于保存当前Slider进度,当歌曲播放触发更改进度
          (1)onChange事件参数的value为当前滑动的进度值: 更改setProgress进度
          (2)当我们播放音乐时,拖动滑块时,会有bug(进度条被拉到前面了)
            (2.1)原因: 这是因为我们在onChange事件中,和timeUpdate事件中都在更改"progress"进度值,在歌曲播放时触发TimeUpdate事件中也更改了"progress"进度
            (2.2)解决: 组件中创建一个用于标识是否正在改变的state,如果不是在change那么就在歌曲播放事件中更改progress进度,最后在抬起事件中再将标识change变量更改为false
      step2 
        1.设置歌曲的src属性,放到uesEffect当中依赖于currentSong
        2.播放暂停功能,背景图切换
    

    FAQ

    progress进度: 1-100
    currentTime: 要的是毫秒数
    audioRef.current.currentTime: 要的是总秒数
    

    歌曲播放具体功能完善

    点击页面上的一首歌播放音乐

    • 音乐播放逻辑

    基于React全家桶开发「网易云音乐PC」项目实战(三)

    • 1.在reducer中添加需要的字段

      • currentSongIndex 记录当前播放音乐的索引
      • playList 播放列表
    • 2.请求歌曲详情逻辑

      • 下拉查看 基于React全家桶开发「网易云音乐PC」项目实战(三)
    • 3.当一个组件内部的actionCreator被其他组件使用时(参考)

      // 将添加歌曲action导出
      export {
        reducer,
        actionCreator
      }
      
    1. 完成效果

      • 下拉查看 基于React全家桶开发「网易云音乐PC」项目实战(三)

    单曲循环或顺序播放或列表循环

    • 当前歌曲播放完毕后,决定下一首是顺序播放还是单曲循环等等
    第一种思路: 创建一个播放列表数组,决定下一首播放什么音乐
      如果是顺序播放,直接把源数组拷贝,如果是随机播放,将顺序打乱
    
    第二种思路: 决定下一首播放什么音乐,让当前currentSongIndex + 1,
      设计顺序的数据结构(sequence)
        0 顺序播放 
        1 随机播放
        2 单曲循环 
      背景图切换
      歌曲列表显示个数
    

    点击按钮播放上一首或下一首

    • 点击按钮: 播放上一首或下一首音乐
    • 两个按钮监听点击事件: 都使用同一个函数,传递不同的tab(标记),处理不同的逻辑
      • 因为需要派发action,所以放到actionCreator里编写
      • 单曲循环也是切换到下一首的, 所以它们的逻辑一样
    • 切换歌曲的实现思路(参考):
      • 根据playSequence决定是顺序播放还是随机播放
      • 根据播放顺序选择下一首音乐
        • 随机播放 ...
        • 顺序播放 ...
      • 获取需要播放的音乐
      • 更改当前播放的索引
      • 更改当前播放的音乐

    基于React全家桶开发「网易云音乐PC」项目实战(三)

    决定下一首音乐播放的顺序

    • 给音频元素监听: onEnded事件(歌曲播放完后触发)
    • 当前歌曲播放完后只有两种情况:
      • 第一种情况: 单曲循环
        • 设置当前播放时间为0: audioRef.current.currentTime = 0
      • 第二种情况: 切换下一首音乐(根据playSequence决定是随机播放还是顺序播放)

    其他细节补充

    1. 点击切换歌曲顺序图标按钮后: 切换对应图标 0顺序播放 1随机播放 2单曲循环
    2. setIsPlaying修改状态时为什么要添加随机值?
      • 如果当前是播放状态: 添加下一首音乐时, 还是播放状态, 设置的值还是true
      • 如果这一次的值和上一次的值时相同的, 就不会执行依赖于isPlayinguseEffect回调
      • 所以每次更新isPlaying时, 需要显示的更新isPlaying
    3. 点击切换顺序按钮后, 悬浮当前播放的顺序文本提示, 单曲循环还是随机播放等等
      • Tooltip文字提示组件, 鼠标经过显示气泡, 内容是单曲循环还是随机播放等等

    歌词显示

    对请求下来的歌词分析

    歌词数据API接口

    • http://123.57.176.198:3000/lyric?id=167876
    [00:00.000] 作曲 : 许嵩 -> {time: 毫秒, content: "歌词内容"}
    [00:01.000] 作词 : 许嵩
    [00:22.240]天空好想下雨
    [00:24.380]我好想住你隔壁
    [00:26.810]傻站在你家楼下
    [00:29.500]抬起头数乌云
    [00:31.160]如果场景里出现一架钢琴
    [00:33.640]我会唱歌给你听\n[00:35.900]哪怕好多盆水往下淋\n[00:41.060]夏天快要过去\n[00:43.340]请你少买冰淇淋\n[00:45.680]天凉就别穿短裙\n[00:47.830]别再那么淘气\n[00:50.060]如果有时不那么开心\n[00:52.470]...
    
    • 咱们会发现请求下来的歌词是有规律的: \n 为换行

    请求歌词数据的时机

    • 什么时候请求歌词数据:
      • 组件被渲染完成切换歌曲时
      • 请求当前播放音乐的歌词或者点击页面上的歌曲

    封装歌词解析工具函数(逻辑思路)

    1.使用slice切割字符串
    2.创建正则解析规则: 将"[00:26.810]傻站在你家楼下..."  解析成->  00:26.810 
    3.注意: 最后一行也有\n在遍历时加个判断,如果为不为空执行下面操作
    4.获取正则解析的3个时间转换为毫秒, 分钟:秒数:00*10 000就是毫秒(加个判断*1转换为number类型)
    5.将获取的3个毫秒数相加: 当前歌曲播放的总时长(毫秒)
    6.获取当前播放的歌词: replace方法 (完成效果如下?)
      [
        0: {totalTime: 0, content: "作曲 : 许嵩"}
        1: {totalTime: 1000, content: "作词 : 许嵩"}
        2: {totalTime: 22240, content: "天空好想下雨"}
        3: {totalTime: 24380, content: "我好想住你隔壁"}
      ]
    7.将数据保存到redux当中
    8.注意: 在切换歌曲时有可能会报 Cannot read property '1' of null , 这是因为从result读取属性时没找到, 加一个if判断,如果result没有值的话, 执行关键字continue跳转到判断条件重新执行
    

    歌词解析代码

    const parseExp = /\[([0-9]{2}):([0-9]{2})\.([0-9]{2,3})\]/
    export function parseLyric(lyrics) {
      if(!lyrics) return
      const lineStrings = lyrics.split('\n')
      const lyricList = []
      for (const line of lineStrings) {
        if (line) {
          const result = parseExp.exec(line)
          if(!result) continue
          const time1 = result[1] * 60 * 1000
          const time2 = result[2] * 1000
          const time3 = result[3].length > 2 ? result[3] * 1 : result[3] * 1000
          // 当前歌曲播放的总时长(毫秒)
          const totalTime = time1 + time2 + time3
          const content = line.replace(parseExp, '').trim()
          const lineObj = {totalTime, content};
          lyricList.push(lineObj)
        }
      }
      return lyricList
    }
    

    完成效果如下图

    基于React全家桶开发「网易云音乐PC」项目实战(三)

    拿到当前播放的歌词

    在歌曲播放的时候会有一个 currentTime 变量, 
    拿到这个变量和当前播放的歌词中的 time 进行比对,
    小于歌词中time,之后获取索引值-1(要展示的歌词是前一句)
    
    在timeUpdate事件中: 获取当前播放的歌词
    	1.获取歌词的索引
       	2.遍历歌词数组.Length
       	3.判断当前播放时间小于歌词播放时间,获取当前循环的索引
       	注意: 注意时间问题,转换为毫秒(current)进行对比判断
       	4.从歌词数组取出索引拿到当前播放的歌词
       	优化: 对for循环进行优化
       	5.对歌词进行管理: 由于歌词在多处使用,使用redux进行管理
       	  优化: dispatch action 过于频繁. 
       	  解决: index如果没有变不需要dispatch(index和currentLyricIndex对比)
    

    实现效果

    基于React全家桶开发「网易云音乐PC」项目实战(三)

    展示歌词

    • 使用Antd Message组件

    • 修改内置样式

    • 实现效果

    基于React全家桶开发「网易云音乐PC」项目实战(三)

    最后

    • 非常感谢王红元老师的React核心技术实战让我学习到很多 React 的知识。
    • 非常感谢后台提供者Binaryify,接口很稳定,文档很完善

    起源地下载网 » 基于React全家桶开发「网易云音乐PC」项目实战(三)

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元