通过网易云api实现一个简单的音乐播放器
這次文章我來實現(xiàn)一個簡易的播放器,這里調用了網(wǎng)易云的api來獲取他的音樂,可以自行下載其文件,本次案例需要?nodejs?來獲取接口,需要通過ajax來請求數(shù)據(jù),實現(xiàn)功能有搜索音樂并顯示列表,展示歌詞等,本次實現(xiàn)get請求的封裝,以及有富含js封裝函數(shù)的思想,模塊化開發(fā)?狀態(tài)化處理,說這3個我有點不配啊🤢 只是普通的案例涉及一點這些思想
還是那句話先來效果看下,有點丑,希望大家不要介意?🌹🌹🌹 這里吹以下漠河舞廳真的好聽
首先運行網(wǎng)易云api,由于是我之前的一個案例,這里忘了在哪里找的api了可以自行查找去下載。安裝后打開cmd輸入 node app.js運行3000端口
?首先準備容器直接放代碼 css就不放了有點丑,這里的搜索列表是先通過position隱藏起來的之后在js里再改變其position將其展示(調整top值,讓他在頁面上面)
<div><input type="text" id="keyword"><button id="search-button">搜索</button></div><!-- 搜索列表 --><div class="search-list"><ul id="result-list"></ul></div><!-- 歌詞容器 --><div class="lrc-all-warp"><ul id="lrc-warp"></ul></div><!-- 音頻 --><audio src="" id="audio" controls></audio><script src="./music.js"></script>然后就是最主要的js了
先來封裝以下ajax的get請求,因為搜索音樂要頻繁的調用ajax請求,每次都調用太過浪費,只在搜素/獲取音樂鏈接的時候發(fā)一次,
先放一波常規(guī)的ajax請求 發(fā)起請求得到數(shù)據(jù),將數(shù)據(jù)修改字符串性質,這里的http://localhost:3000/search?keywords=啦啦啦,簡易參考下3000端口的內容,會告訴該怎么獲取內容
var xhr =new XMLHttpRequest() // xhr.open('get','http://localhost:3000/search?keywords=啦啦啦',true) // xhr.send() // xhr.onreadystatechange=function(){ // if(xhr.status==200&&xhr.readyState==4){ // // 獲取成功的結果 // console.log(JSON.parse(xhr.response)) // // 返回的結果是一個JSON的字符串 // // 怎么處理JSON格式的字符串 // // 變成JS對象數(shù)據(jù)類型方便我們使用 // // 獲取音樂名字 // var result=JSON.parse(xhr.response) // var songs= result.result.songs // console.log(songs) // // 放歌 // var firstSongId=songs[0].Id // xhr.open('get','http://localhost:3000/song/url?id='+firstSongId,true) // xhr.send() // }接下來就擼起袖子加油干!實現(xiàn)get的ajax請求封裝 ,拼接字符串實現(xiàn) ,url是路徑,data是要請求的參數(shù),也就是query參數(shù),callback比較重要,是回調函數(shù),如果調用get方法有回調函數(shù),就會把JSON.parse(xhr.response)當成參數(shù)來給該函數(shù)。之后的請求會基于這個回調再獲取數(shù)據(jù),再通過他們的回調獲取數(shù)據(jù),其實這里有點回調地獄的意思了,我promise沒學好,沒給他改好,先對付看吧
// get請求包裝 把ajax包裝成get方法 var get = function (url, data, callback) {var xhr = new XMLHttpRequest()var param = '?'for (var key in data) {if (data.hasOwnProperty(key)) {param += key + '=' + data[key] + '&'}}param = param.slice(0, param.length - 1)// http://localhost:3000/song/url?id=123456 要這樣的形式 ?id=123456xhr.open('GET', url + param, true)xhr.send()xhr.onreadystatechange = function () {if (xhr.status == 200 && xhr.readyState == 4) {if (callback) {callback(JSON.parse(xhr.response))}}} }第二層封裝,封裝一些常用的方法
首先是一個搜索音樂的函數(shù) ,這里就通過給get傳遞回調函數(shù)獲取通過請求得到的數(shù)據(jù)也就是JSON.parse(xhr.response),還有一個回調🐱?👤,與上面同理,如果存在就把數(shù)據(jù)通過回調函數(shù)傳過去
// 建立一個搜索函數(shù) var search = function (keywords, callback) {get('http://localhost:3000/search', {keywords: keywords}, function (res) {if (callback) {// 歌在res的result的songs里 此時的res為get()里的callback()里的JSON.parse(xhr.response)callback(res.result.songs)}}) }播放封裝
// 播放封裝 獲取url封裝 var getSongUrl = function (id, callback) {get('http://localhost:3000/song/url', {id: id}, function (res) {if (callback) {// 音樂播放鏈接callback(res.data[0].url)}}) }獲取歌詞封裝
var getLrc = function (id, callback) {get('http://localhost:3000/lyric', {id: id}, function (res) {// console.log(res)var lrcString = res.lrc.lyricif (callback) {callback(lrcString)}}) }這些url以及數(shù)據(jù)都在3000端口有說的
先來倆個簡單的函數(shù),打開列表和關閉列表
// 構造打開列表關閉列表方法 用的時候調用就好了 var closeSearchList = function () {searchList.className = 'search-list' } var openSearchList = function () {searchList.className = 'search-list active' }渲染搜索列表方法 這里resultList獲取的是渲染列表的ul元素 參數(shù)為key也就是到時候輸入框獲取的value值,并且傳遞回調函數(shù)為參數(shù)
使用一個模板來更換ul列表的innerHTML?
var resultList = document.getElementById('result-list') var renderSearchList = function (key) {search(key, function (res) {console.log(res)// 模板 class方便點擊事件 data-id為了存儲歌曲idvar tpl = '<li class="songs" data-id="{--id--}">' +'<h3>{--name--}</h3>' +'<h5><span>{--geshou--}</span> - 專輯:<span>{--zhuanji--}</span></h5>' +'<hr>' +'</li>'var html = ''for (var i = 0; i < res.length; i++) {html += tpl.replace('{--name--}', res[i].name).replace('{--geshou--}', res[i].artists[0].name).replace('{--zhuanji--}', res[i].album.name).replace("{--id--}", res[i].id)}resultList.innerHTML = htmlopenSearchList()// 不要在一個方法構造實現(xiàn)另一個方法// searchList.className='search-list active'addEventListener()}) }這里不直接改變列表的類名來更改樣式,而是用了個函數(shù)來進行封裝,也體現(xiàn)了函數(shù)封裝的思想,一個代碼應該這樣他的功能邏輯和函數(shù)多少成反比的是。而且這樣多的模塊可以更好的實現(xiàn)復用以及維護。這才是我這篇文章想向你們表達的東西,更多的實現(xiàn)功能函數(shù),每個方法都是一個函數(shù),達到模塊化代碼
?接下來獲取輸入框想聽的音樂,并且調用獲取列表方法展示列表
// 點擊按鈕 獲取Input里的輸入值 var searchButton = document.getElementById('search-button') var keywordInput = document.getElementById('keyword')searchButton.addEventListener('click', function () {var value = keywordInput.value// 將input的輸入值代入renderSearchList函數(shù)里renderSearchList(value)})接下來就是點擊列表里的音樂來進行播放了,這里我已經(jīng)詳細把注釋寫在上面了,這里的songs是之前渲染模板增加的類名,并且把音樂id存儲在data-id屬性中了,下面的展示歌詞和清空歌詞下main會說
var audio = document.getElementById('audio') // 做一個li點擊事件方法 var addEventListener = function () {var songs = document.getElementsByClassName('songs')for (var i = 0; i < songs.length; i++) {songs[i].addEventListener('click', function () {// getAttributevar id = this.getAttribute('data-id')// 通過getSongUrl方法來播放音樂getSongUrl(id, function (url) {// 通過獲取audio來播放音樂 通過src來播放audio.src = url// 與視頻一樣play()開始播放audio.play()closeSearchList()// searchList.className='search-list'})// 展示歌詞getLrc(id, function (res) {var lrc = parseLrc(res)console.log(lrc)fillLrc(lrc)})// 清空歌詞var lrcWarp = document.getElementById('lrc-warp')if(lrcWarp){lrcWarp.innerHTML=''}})} }接下來就是解析歌詞以及展示歌詞了,通過api獲取的歌詞格式和要使用的歌詞有點不太一樣,我們得把他解析我們要用的格式,概念寫在下面,時間戳是用來到時候來展現(xiàn)歌詞高亮用的
[03:06.466]晚星就像你的眼睛殺人又放火\n把字符串轉化為數(shù)組需要的格式 把字符串準換為對象格式{time:186.466,content:'晚星就像你的眼睛殺人又放火'}解析歌詞方法,方法詳細寫在了注釋上,先把歌詞與時間分離開,在格式化時間
// 歌詞 // [03:06.466]晚星就像你的眼睛殺人又放火\n var parseLrc = function (lrcString) {// 將時間字符串類型轉化為number類型 做函數(shù)var parseTime = function (timeString) {// 切割分鐘和秒 會分為倆項var timeStringArr = timeString.split(':') //["01","51.73"]var min = parseInt(timeStringArr[0]) //01var s = parseFloat(timeStringArr[1]) //51.73var totalTime = (min * 60 + s) * 1000return parseInt(totalTime)}var lrcArr = []// 通過split()方法來用'\n'分割歌詞lrcArr = lrcString.split('\n')//把每一行存儲時間和內容var lrcObjArr = []// 遍歷 通過split()方法來用']'分割歌詞內容與事件for (var i = 0; i < lrcArr.length; i++) {var lines = lrcArr[i].split(']')//提取時間,通過把 [03:06.466 的 [ 用slice()給切割 得到03:06.466 slice(1,lines[0].length從1開始切(保留)到length最后 仍掉數(shù)組[0]var time = parseTime(lines[0].slice(1, lines[0].length))// 內容var content = lines[1]// console.log(time, content)// console.log(lines,time)//錯誤處理 如果某一行解析不對直接跳過這一行 //continue在foreach()里用不了if (content == undefined || isNaN(time)) {continue}lrcObjArr.push({// 因為要把字符串時間轉化為數(shù)組類型,所以在這里就處理了time: time,content: content})};// console.log(lrcObjArr)return lrcObjArr }接下來就是封裝歌詞填充的方法,同樣定義模板字符串來拼接通過replace方法來替換
// 模板 var tpl = '<li class="lrc-item">{--content--}</li>' // 歌詞填充 var lrcWarp = document.getElementById('lrc-warp') var fillLrc = function (lrcObjArr) {var html = ''for (var i = 0; i < lrcObjArr.length; i++) {html += tpl.replace('{--content--}', lrcObjArr[i].content)}lrcWarp.innerHTML = htmlnowLrcObject = lrcObjArr }接下來就是令歌詞滾動并且展示高亮了? 🐱?👤都碼累了
// 歌詞滾動 var index = 0 // 初始距離 var marginTop=240 var nowLrcObject = [] // 對比歌詞 var compareLrc = function () {// 在html中獲取全部的歌詞var lrcItem = document.getElementsByClassName('lrc-item')// 對比時間,確定那一句歌詞播放if (nowLrcObject[index].time - audio.currentTime * 1000 < 300) {lrcItem[index].style.color = 'red'// 歌詞向上移動20pxmarginTop-=20lrcWarp.style.marginTop=marginTop+'px'lrcItem[index].style.fontSize = '1.2em'// 將上一句的高亮去掉if(index-1>-1){lrcItem[index-1].style.color = ''lrcItem[index-1].style.fontSize = ''}index++}}最后通過timeupdata事件函數(shù)來調用對比歌詞方法
// 在這里timeupdate對比 audio.addEventListener('timeupdate', function () {compareLrc() })現(xiàn)在就可以聽音樂了!
模塊化開發(fā) ,屁大點事也要寫個函數(shù)? 一個函數(shù)應該只關心另外一個函數(shù)給我們提供的功能,而不參與他的實現(xiàn)。這就是我本篇文章的內容,希望可以得到大家的見解和指摘,感謝大家觀看
總結
以上是生活随笔為你收集整理的通过网易云api实现一个简单的音乐播放器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何提高生产力(二)、软件的开发与采购
- 下一篇: Plain text, flat fil