preface

Hello everyone, I am “wind do not know the way”, not updated for a long time ~, a lot of friends have been pushing to update (actually not), I am really a little busy in the internship =.=, if you have time, you can summarize the interview & internship experience. Ok, back to the topic, we have completed the transition from the nesting pattern by clicking the jump to switch and the rotation picture, if you are reading this series for the first time please click, now we begin to complete the main content of the home page + music player ▶ The content to be completed is as follows ↓.

Project preview and source code

  • Preview online at 👉 : www.wanguancs.top

  • Project Gihub address 👉: Musci 163 If you think the project is good 👏, give a star ⭐ to encourage ~

Main Content of home page

1. Popular recommended header components

Content Body layout

The preview image to be completed is as follows ↓

Header component encapsulation “Preface”

  • View the Header components that need to be encapsulated


  • Why wrap: Because there are multiple similar header components under the current page

  • Some header components in the current page do not have keywords (that is, the category behind the popular recommendations):

  • Click to view multipleheadercomponentdifferences


“Header component” encapsulation

  • Component location (see) : Encapsulate header in the SRC -> Components ->ThemeHeader folder

  • The component needs to rely on the props passed:

    • The title(title) of the component, required;

    • Keyword (data can be written to death first);

  • Pass the default values using propTypes

// theme-header-rcm.js
import propTypes from 'prop-types'
// ...
// Specify the pass props
ThemeHeaderRmc.propTypes = {
  // Title attribute Mandatory (left title)
  title: propTypes.string.isRequired,
  // Keyword (optional, left keyword)
  keywords: propTypes.array
}
// Specify a default value
ThemeHeaderRmc.defaultProps  = {
  keywords: []}Copy the code

Implementation effect

System-system-system-system-system-system-system-system-system-system-system-system-system.html


2. Hot Recommendation module -> Send network request

  • At this point we can send a network request and save the requested data inStory of the storeIn the
  • Popular recommended API interface ↓:
    • /personalized? limit=8
    • Example: http://123.57.176.198:3000/personalized? limit=8

A component needs to send the basic steps of the network

// 1. Encapsulate the corresponding function in the corresponding network request file
Reducer reducer reducer reducer reducer reducer reducer reducer reducer reducer reducer reducer reducer reducer reducer reducer reducer reducer reducer
// 3. Add action to actionCreator to send network requests
// 4. Test the network request data using the Dispatch Action in the component
// 5(Optional). Define the constant constans to control the number of limits (for later maintenance)
// 6. Use useSelector to display data in the componentThe detailed steps are as follows: 👇Copy the code
  1. Network request interface encapsulation: SRC /service/recommend.js

    export function getHotRecommends(limit) {
      return request({
        url: "/personalized".params: {
          limit
        }
      })
    Copy the code
  2. Modify the story


  3. Add actionCreator


  4. Define constants to control the number of limits. The advantage is that if you want to change the number one day, you can change it directly in the constants file

  5. Use useSelector to display data in components


3. Song Cover package

  • Common component location: SRC /components/song-cover/index.js
Click to view song Cover package:


Interface field: Cover image: picUrl Number of plays: playCount Cover name: Name Text at the bottom of the cover: copywriter Song Cover Component layout 👇👇👇Copy the code

  • Will be stored in thereduxFor traversal (wrap div, and use Flex layout, flex-wrap)

4. Completion effect of popular recommended components

5. A new CD hits the shelves

Network request

  • New disc mounting interface:

    • /top/album? limit=10Has been abolished
    • /album/newest
    • Example: http://123.57.176.198:3000/album/newest
  • Put the requested data into reUDX

    1. Encapsulate network interface requests
    2. Modify thereduxTo addstateTo addcasestatements
    3. addactionCreator:getNewAlbumsAction
      • Used to send network requests
    4. The component is distributed toactiontest
    5. addactionCreator:changeNewAlbumAction
      • Used to dispatch this in a network requestaction
    View savedstate


New disc mount component layout

  • Carousel: use Carousel control

Data interception logic code

//1. Data: In order to ensure that there are 5 pieces of data per page of the rotation chart:
// Select * from page 1 to page 5-10
// Note :slice(method does not include intercepting target number)
// 2. Layout:
// Flex layout with class name npage
<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>
Copy the code
  • Effect to be improved 🤔

AlbumCover Component encapsulation

// The AlbumCover component requires (data is not fixed): width, height and BGP (background image horizontal and vertical) are passed by the caller
// Because the size of the component used in other pages is different from that of BGP
<Carousel dots={false} ref={albumRef}>
...
<AlbumCover
  key={cItem.id}
  info={cItem}
  size={100}
  bgp="-570px"
>
  {cItem.name}
</AlbumCover>
...
</Carousel>
Copy the code
  • Completed effect 😏

6. List

The list shows

Request list data

  • List data API: /top/list? Idx =0 has been deprecated, the project interface has been changed again

    • 0: Cloud music soaring chart
    • 2: Cloud music New songs list
    • 3: Cloud original songs chart
  • List data API:

    • Drop down to view: three lists on the home pageid


    • Rising cloud music list API: http://localhost:3000/playlist/detail? id=19723756

    • Cloud music new song list API: http://localhost:3000/playlist/detail? id=3779629

    • Netease original European QuBang API: http://localhost:3000/playlist/detail? id=2884035

    • In the requested data, only the first 10 data are retained by intercepting the tracks field

  • Precautions ↓

Send network requests put the requested data into state in redux. Note: This can be used when sending actions based on different ids and listsswitchDifferent actions are issued based on different idsCopy the code

Top-ranking encapsulation

  • The component packaging to be completed is as follows 👇

  • To achieve the effect
    • At first:iconsFixed to the parent element ofwidthIs zero,hoverAfter give fixed width
    • The mouseacross This line itemLet text overflow hide display… And to displayicons
    • The mouseleaveShow original effect, hideicons, the width of the song name can be fixed

To complete the effect

7. The right side of the main body

Do not do login specific functions for the time being, just do data rendering first. Login module layout is relatively simple to skip;

Settle singer

  • In singerAPI:
    • /artist/list? limit=5&cat=5001
    • Example:http://123.57.176.198:3000/artist/list?limit=5&cat=5001
  • The returnedJSONThe following
{
    picUrl(pin):"http://p4.music.126.net/LCWqYYKoCEZKuAC3S3lIeg==/109951165034938865.jpg"
    followed(pin):false
    briefDesc(pin):""
    name(pin):"Xue Zhiqian"
    id(pin):5781
    alias(pin):
    musicSize(pin):275
    accountId(pin):97137413
    picId_str(pin):"109951165034938865"
    img1v1Id_str(pin):"109951165034950656"
}
Copy the code

Popular anchor

  • hot-artist
    • SRC /common/local-data.js: SRC /common/local-data.js: SRC /common/local-data.js: SRC /common/local-data.js: SRC /common/local-data.js: SRC /common/local-data.js
    • The returnedJSONThe following
{
  picUrl: 'http://p1.music.126.net/H3QxWdf0eUiwmhJvA4vrMQ==/1407374893913311.jpg'.name: 'David li Chen'.position: 'Professor Chen Li, psychologist and gourmet'.url: '/user/home? id=278438485',},Copy the code

Music playback

Music Playback Component (app-Play-Bar)

Player component description: When we switch the page on the official website of netease Cloud Music, we will find that the music playing is always fixed at the bottom, which has nothing to do with route switching

  • Component location: so we willapp-play-barComponent encapsulation tosrc/psgesIn the folder 📂

Layout of the reference

The PlayBar component layout uses fixed positioning: PlayerWrapper↓ The content is divided into three parts :↓ Control(left) three buttons. Add background image, use flex layout PlayIInfo(middle) two parts (top and bottom) for outer layer, andT component library Slider component for bottom Slider, find Opertaor(right) two parts, use Flex layout for outer layerCopy the code

Slider component style overwrite

Slider component style change (overlay)1.Cover background image and wide height style..2.Mairign margin for0
  3.Set the background color to transparent4.Set the background image for mouse sliding5.Sets the dot style overlayCopy the code

To complete the effect

  • Images and some dynamically acquired data are written down for now

Song playback data request

  • Start by playing a regular song

  • Song API: /song/detail? ids=167876

    • Example: http://123.57.176.198:3000/song/detail? ids=167876
  • Where does the requested data go?

    • The request is due toSong informationSo we put itplayerfolderstoreUnder the folder 📂storeTo save
    • rducerThe use ofimmutableManagement of the state
    • In the project root, importplayerUnder folderreducer,combine(merger)
Step1 add currentSong (default state) in player to reducer: {} send network request: player.js save data in reducer step2 request data from plaer using redux: Currentsong-al. PicUrl image CurrentSong-name Song name currentSong-ar [0[.name author currentSong-dt (duration, formatting) formatDate(duration, formatting)"mm:ss")
Copy the code

Play music function

  • The songsAPIInterface:Music.163.com/song/media/…
    • idIs dynamic: you can come down from the requestCurrentSong (currentSong)Get current songidinformation

Music playback logic

Step2 play the music. Use useRef to obtain the dom element encapsulation of audio: play the song`API`Interface, after id is taken as the parameter, click play ▶ button to dynamically set the SCR property, and call play method to start playing Step3 Song time display the local state of the component creation: CurrentTime is used to change the current playback time. The Audio element has an OnTimeUpdate event that is called back when the song event changes: E.currenttime property (used to get current playback time) for seconds ->format the time ->formatDate(currentTime,'mm:ss'Step4 progress bar Slider control value of andt Slider: current playing progress = current time/total duration *100
Copy the code

Drag slider logic

Now we start to drag the Slider and play the corresponding progress song demand: Slider Slider: the current time will change when the Slider is lifted: the current song progress will change step11.The Slider component is provided2API: (1OnChange: triggered when the slider is pulled, the function takes the dragged value as an argument (2OnAfterChange: When triggered by a lift, the function takes the value of the lift as an argument2.The progress state is used to save the current Slider progress. When the song is triggered to change the progress (1The onChange event parameter value is the current sliding progress value:2There is a bug when dragging the slider while playing music (progress bar is pulled forward) (2.1) Cause: This is because we are changing in both the onChange event and the timeUpdate event"progress"The progress value is also changed when the TimeUpdate event is triggered while the song is playing"progress"Progress (2.2Solution: Create a state in the component to indicate whether it is changing, change progress in the song play event if it is not changing, and change the change variable in the lift event to indicate whether it is changingfalse
  step2 
    1.Set the SRC property of the song to uesEffect depending on currentSong2.Play pause function, background image switchCopy the code

FAQ

The progress schedule:1-100
currentTime: to milliseconds audioRef. Current. CurrentTime: is the total number of secondsCopy the code

The specific function of song playback is perfect

Click on a song on the page to play the music

  • Music playback logic

  • 1. Add required fields in the Reducer

    • currentSongIndexThe index that records the music currently played
    • playListThe playlist
  • 2. Request song details logic

    • The drop-down to view


  • 3. When actionCreator inside one component is used by other components (see)

    // Will add the song action export
    export {
      reducer,
      actionCreator
    }
    Copy the code
  1. To complete the effect

    • The drop-down to view


Single loop or sequence play or list loop

  • After the current song is played, decide whether the next song should be played sequentially or in a single loop, etc
First idea: Create an array of playlists and decide what music to play next. If it's sequential, just copy the source array. If it's random, disarrange the sequenceCopy the code
Second idea: Decide what music to play next and make the current currentSongIndex +1, design sequential data structures (sequence)0Order of play1Random broadcast2Single loop background toggle song list display numberCopy the code

Click the button to play the previous or next song

  • Click the button to play the previous or next song
  • Both buttons listen for click events: both use the same function, pass different tabs, and handle different logic
    • Because action needs to be distributed, it is written in actionCreator
    • The single track loop also switches to the next track, so the logic is the same
  • Switch song implementation ideas (reference) :
    • According to theplaySequenceDecide whether to play sequentially or randomly
    • Select the next piece of music according to the order in which it is played
      • Random play…
      • Play in sequence…
    • Gets the music to play
    • Changes the current playing index
    • Changes the current playing music

Decide the order in which the next piece of music will be played

  • Listen for audio elements:onEndedEvent (triggered after the song finishes playing)
  • There are only two situations when the song is finished playing:
    • The first case: single cycle
      • Set the current playback time to 0: audioRef.current.currentTime = 0
    • The second case: switch to the next piece of music (according toplaySequenceDecide whether to play randomly or sequentially)

Additional details

  1. After clicking the switch song order icon button: Switch the corresponding icon0 Sequence Playback 1 Random Play 2 Single track cycle
  2. setIsPlayingWhy add random values when changing state?
    • If currently in Play state: is still in Play state when adding the next piece of musictrue
    • If this value is the same as the previous value, the isPlaying dependent useEffect callback is not executed
    • So every updateisPlayingNeed to display the updateisPlaying
  3. After clicking the switch order button, the text prompt of the current playing order will be suspended, single loop or random play, etc
    • Tooltip text Tooltip widgets, mouse over bubbles, whether content is looped or played randomly, and more

The lyrics show

Analysis of lyrics requested down

Lyrics data API

  • http://123.57.176.198:3000/lyric?id=167876
[00:00000.< p style = "max-width: 100%; clear: both;time: 毫秒, content: "Lyric content"}
[00:01000.] 作词 : 许嵩
[00:22.240The sky wants rain [00:24.380I wish I lived next door to you.00:26.810Standing downstairs in your house [00:29.500Lift your head and count the clouds [00:31.160If there is a piano in the scene [00:33.640I'll sing you a song \n[00:35.900] Even if a lot of water down \n[00:41.060Summer is almost over \n[00:43.340Please buy less ice cream \n[00:45.680Don't wear short skirts on cold days \n[00:47.830Don't be so naughty \n[00:50.060] If sometimes not so happy \n[00:52.470]...
Copy the code
  • We will find that the requested lyrics follow a pattern: \n is a newline

The timing of requesting lyric data

  • When is lyric data requested:
    • inThe component is renderedWhen you switch songs
    • requestCurrent Playing MusicSong lyrics orClick on the song on the page

Encapsulate lyric parsing tool functions

1.Use slice to slice the string2.Create a regular resolution rule: will[00:26.810] Standing downstairs at your house...Parsed into - >00:26.810 
3.Note: the last line also has \n in the traversal to add a judgment, if not empty perform the following operation4.Of the regex3Time converted to milliseconds, minutes: seconds:00*10 000Milliseconds (plus a judgment *)1Convert to number type)5.To obtain the3Add milliseconds: The total length of the current song played in milliseconds6.Get the lyrics currently playing: replace method (complete as 👇) [0: {totalTime: 0.content: "Composition: Xu Song"}
    1: {totalTime: 1000.content: "Lyrics: Xu Song"}
    2: {totalTime: 22240.content: "The sky wants rain."}
    3: {totalTime: 24380.content: "I want to live next door to you."}]7.Save the data to redux8.Note: Cannot read Property may be declared when switching songs'1' of nullThe attribute was not found when it was read from resultifExecute the keyword if result has no valuecontinueJump to the judgment condition and retryCopy the code

Lyric parsing code

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
      // Total duration of current songs played in milliseconds
      const totalTime = time1 + time2 + time3
      const content = line.replace(parseExp, ' ').trim()
      const lineObj = {totalTime, content};
      lyricList.push(lineObj)
    }
  }
  return lyricList
}
Copy the code

The finished effect is shown below

Get the lyrics currently playing

When the song is playing, there will be a currentTime variable. Compare this variable with the time in the current song lyrics, which is less than the time in the song lyrics, and then get the index value -1In the timeUpdate event: Gets the currently playing lyrics1.Gets an index of lyrics2.Iterate over the lyric array.length3.To judge that the current playing time is less than the lyrics' playing time, obtain the index of the current loop4.Retrieves the index from the lyric array to get the current playing lyric optimization: yesforCycle optimization5.Manage lyrics: Because lyrics are used in multiple places, redux is optimized for management: Dispatch actions are too frequent. [fixed] Index does not need dispatch if it has not changed (index vs. currentLyricIndex)Copy the code

Implementation effect

Display the lyrics

  • Use the Antd Message component

  • Modify the built-in styles

  • Implementation effect

  • By now, we have completed the basic functions of the first page of “netease Cloud Music PC”. I believe you are familiar with React family. Next, you can supplement and perfect functions by yourself (though I believe there are few friends who can see here)😂;
  • If there is any part of the article do not understand or write bad or have any suggestions are welcome to put forward 🤗, I hope we make progress together;

The last

  • Thank you very much to Teacher Wang HongyuanReact Core technologiesLet me learn a lotReactKnowledge.
  • Thank you very much to the backend providersBinaryifyThe interface is stable and the documentation is perfect