Introduction to the
@diyGod /APlayer is a simple and beautiful HTML5 music player (” blue ω Blue “). When I first saw the appearance level of this player, I was very impressed by those designers who can design a beautiful interface (* >ω<).
But after using, I found that there are still insufficient places. This is the Issues I have mentioned
I have been using APlayer for a while and like the simplicity of its UI. Here are some other suggestions for improvement:
1. I think it’s necessary to provide an API for dynamically managing playlists (if not, you can only re-initialize when you need to dynamically add songs to the list) 2. 3. Lyrics are allowed to be added asynchronously, usually the interface to get lyrics is separate (now you have to wait for the lyrics interface to return and then initialize the player, if the lyrics fail to get or the time is too long, the music playing function will be affected simultaneously).
Regarding the third item, APlayer actually supports asynchronous lyrics, but only supports the address of the incoming.lrc file. If it is returned in JSON format like netease Cloud/QQ Music, it does not meet the requirement
Why not mention that PR has to be rewritten? I have thought about this for a while, but I still think component-based development is better (the original APlayer uses native JS and does not rely on other libraries). Besides, I am quite experienced in writing music player (imitated weibo player, at that time I will not use Git source has been lost) when I was working on the back end. Rewrite a difficulty is not easy, and more arbitrary, but also feel free to add something you want
screenshots
Note: This player is based on the layout and style of @DiyGod /APlayer using TS + Vue componentized reconstruction
Demo: aplayer.quq.cat Documentation: aplayer.quq.cat/docs source: github.com/MoeFE/vue-…. NPM:www.npmjs.com/package… Playlist from netease Cloud Playlist: music.163.com/#/playli…
If you like don’t forget to point a star yo (*ゝω ·) welcome to mention Issues and PR (´ _ · ‘)
Selection of the framework
For the convenience of use, I choose Vue, which can control the attributes of the player in a responsive manner and release them in the form of plug-ins (see demo for details). In order to facilitate better debugging, SourceMap and DevTools enabled in production If you have vue-devTools installed you can open the debugger to view component partitioning and information about individual components
I don’t want to go into too much detail about why we use TypeScript and you can look up the difference between TypeScript and JavaScript on the web. Okay
I can only tell you: it’s not too cool for a developer who used to use C#.
Finally, we recommend a TS + Vue scaffold template: github.com/Toilal/vue…. Later or will be added to the official template: github.com/vuejs-temp….
TS + React scaffolding can be used with this: github.com/wmonk/crea….
Break up the component
The first thing you do when you get the layout is split up the components copy the layout and style of @diyGod /APlayer to make sure that the style is ok and then copy the layout and style of each component individually if you don’t know the design you have to copy it please allow me to make a sad face (ಗ ‸ ಗ)
I split the player into the following components:
Component name | Component description |
---|---|
APlayer.ts | Player container Components |
Button.ts | Button component |
Picture.ts | Song Picture component |
Container.ts | Right side container Component |
Info.ts | Song information component |
Lyric.ts | Lyrics Panel Component |
Progress.ts | Progress bar component |
Time.ts | Play time component |
Volume.ts | Volume control unit |
List.ts | Playlist component |
Item.ts | Playlist item component |
Here’s a clearer picture:
Click to see the original hd image
The function development
It’s really not that difficult to develop functionality, HTML5 has already wrapped the HTMLAudioElement element and we’re just going to use its API and view for data binding and interaction and just look at the documentation
But there was a slight problem, and that was that Vue couldn’t listen for changes in the properties of the Audio object because the Audio object was an HTMLAudioElement, and Vue couldn’t listen for changes in the properties of the element, so I came up with a trick
We define a Media interface that defines the same properties as the Audio object. Synchronize the Media properties in the Audio event so that we can use the Media object to retrieve the value of the Audio property. APlayer.ts#L326-L334
Let me briefly introduce some of the more common properties and methods
The name of the | instructions |
---|---|
autoplay |
Whether auto play (inSafari Call the play method manually after initializing the audio. |
bufferd |
Get buffered progress (must be inreadeyState >= 3 Fetch later, otherwise an exception will be thrown) |
loop |
Whether to play the audio loop (it is recommended to implement this function according to the current playing mode) |
preload |
Preloading option, recommendedmetadata Get only the length of the audio when it is not playing instead of loading the entire audio |
src |
Gets or sets the playback address of the audio |
volume |
Gets or sets the audio volume (0 ~ 1) |
paused |
Gets whether the current audio has been paused |
currentTime |
Gets or sets the current audio playback progress (in seconds) |
duration |
Gets the length of the current audio in seconds |
playbackRate |
Gets or sets the playback speed of the current audio |
play () |
Playback of audio |
pause () |
Pause the audio |
Click to see all Media events
In fact Audio and Video objects are pretty much Media objects so if you can develop music players you can also develop Video players
I’m going to focus on the Playing event, which is the event that is constantly triggered when the audio is playing, and this is probably the most useful event because you have to constantly redraw the progress of the player and the time that it has been playing, and then synchronize the lyrics to the current time of the song
If you don’t have or don’t know about this event, you might use setInterval instead of setInterval, and there are two problems: 1. How much redraw time is appropriate? 2. If the user pauses the playback, the timer needs to be cleared, and the timer needs to be initialized when the playback starts, which is too troublesome (or lazy, you can judge the return when paused, so you need to continuously run an empty timer).
LRC lyrics analysis and synchronization
Probably the most fun to do this function QWQ, because long ago I used to do LRC lyrics when I was bored, so I am sensitive to this function try to do the best (´ _ ‘)
The main function here is lyrics analysis. For lyrics synchronization, you only need to calculate the item element that best matches the current playing time and then set the scroll bar position of lyrics panel to the current element position
Common time labels are as follows
[mm:ss:ms] have minute, second, millisecond time tag [mm:ss:ms] have minute, second, millisecond time tag another format [mm:ss:ms] [mm:ss.msMultiple time tags share the same lyricCopy the code
Here’s my idea: First, divide the lyric text into an array according to the line, and then parse it by line. Use regular expression to match the minutes, seconds, and milliseconds of the line and the displayed lyric text. Convert the minutes, seconds, and milliseconds into millisecond units, and then add them up and associate them with the lyric text and save them in the array. Finally, we need to arrange the lyrics in chronological order so that the current lyrics to display = the last item in the filter array after the current playing time
private async parseLRC (): Promise<void> {
if (!this.lrc || this.lrc === 'loading') return
if (this.isURL(this.lrc)) { // If the lyric is a URL, that address is requested to get the text of the lyric
const { data } = await Axios.get(this.lrc.toString())
this.currentLRC = data
} else this.currentLRC = this.lrc
const reg = /\[(\d+):(\d+)[.|:](\d+)\](.+)/
const regTime = /\[(\d+):(\d+)[.|:](\d+)\]/g
const regCompatible = /\[(\d+):(\d+)]()(.+)/
const regTimeCompatible = /\[(\d+):(\d+)]/g
const regOffset = / \ [offset: \ s * (- {0, 1} \ d +) \] /
const offsetMatch = this.lrc.match(regOffset)
const offset = offsetMatch ? Number.parseInt(offsetMatch[1) :0
this.LRC = []
const matchAll = (line: string) = > {
let match = line.match(reg) || line.match(regCompatible)
if(! match)return
if(match.length ! = =5) return
const minutes = Number.parseInt(match[1) | |0
const seconds = Number.parseInt(match[2) | |0
const milliseconds = Number.parseInt(match[3) | |0
const time = (minutes * 60 * 1000 + seconds * 1000 + milliseconds) + offset
const text = (match[4] as string).replace(regTime, ' ').replace(regTimeCompatible, ' ')
if(! text)return // optimization: do not display blank lines
this.LRC.push({ time, text })
matchAll(match[4]) // Recursively matches multiple time tags
}
this.currentLRC.replace(/\\n/g.'\n').split('\n').forEach(line= > matchAll(line))
// Lyrics format not supported
if (this.LRC.length <= 0) this.LRC = [{ time: - 1, text: '(· ∀ · *) Sorry, the lyric format is not supported ' }]
else this.LRC.sort((a, b) = > a.time - b.time)
}Copy the code
Click here to see the full code
conclusion
Improved the shortcomings of the original APlayer: 1. Responsive control of player attributes 2. Support multiple time tag format (FIX #39) 3. Sync lyrics compatible with [offset:0] tag 4. Asynchronous lyrics support 5. Allow to control the playback speed (the same song with different speed listening will feel different) 6. Volume allow drag control 7. Support registration of all Media events
And experienced the pleasure of writing Vue with TS
(๑•̀ㅂ•́)و✧.. There is no requirement for a cute profile picture!!
I wish I had a big boy who could take me with me