基于JavaScript實現(xiàn)簡單的音樂音譜圖效果
我們經(jīng)??吹皆诼牁芬舻臅r候,會有音譜圖隨著音樂的節(jié)奏不斷變化給人視覺上的享受,那么我們通過js來實現(xiàn)以下這個效果,下面是簡單的效果圖
首先我們需要有一個繪制音頻的函數(shù)
function draw() { // 請求下一幀動畫 animationId = requestAnimationFrame(draw); // 獲取音頻頻譜數(shù)據(jù) analyser.getByteFrequencyData(dataArray); // 清空畫布 ctx.fillStyle = 'black'; ctx.fillRect(0, 0, canvas.width, canvas.height); // 計算每個頻譜條的寬度 var barWidth = (canvas.width / bufferLength) * 2.5; var barHeight; var x = 0; // 遍歷頻譜數(shù)據(jù)數(shù)組,繪制頻譜條 for (var i = 0; i < bufferLength; i++) { // 計算頻譜條的高度 barHeight = dataArray[i] / 255 * canvas.height; // 根據(jù)頻譜條的索引值計算顏色(彩虹色) var hue = i / bufferLength * 360; ctx.fillStyle = 'hsl(' + hue + ', 100%, 50%)'; // 繪制頻譜條矩形 ctx.fillRect(x, canvas.height - barHeight, barWidth, barHeight); // 更新下一個頻譜條的起始位置 x += barWidth + 1; } }
這個函數(shù)是用于繪制頻譜圖的核心部分。它使用requestAnimationFrame()
方法來請求下一幀動畫,并將自身作為回調(diào)函數(shù)。這樣可以不斷更新頻譜圖。
在函數(shù)內(nèi)部,analyser.getByteFrequencyData(dataArray)
用于獲取當前的音頻頻譜數(shù)據(jù),將數(shù)據(jù)存儲在dataArray
數(shù)組中。
然后,畫布被清空,使用黑色填充整個畫布。
接下來,通過計算每個頻譜條的寬度,以及根據(jù)頻譜數(shù)據(jù)計算每個頻譜條的高度,來確定頻譜條的繪制參數(shù)。
然后,使用彩虹色調(diào)的漸變來設(shè)置頻譜條的顏色,顏色的HSL值根據(jù)頻譜條的索引值計算。
最后,在畫布上繪制每個頻譜條的矩形,每個矩形之間留有間距。
通過不斷調(diào)用requestAnimationFrame()
方法并在每一幀更新頻譜圖,可以實現(xiàn)連續(xù)的動畫效果。
接下來我們需要分析一下音頻
document.getElementById('playButton').addEventListener('click', function() { if (!audioContext) { audioContext = new (window.AudioContext || window.webkitAudioContext)(); analyser = audioContext.createAnalyser(); analyser.fftSize = 2048; audioElement = document.createElement('audio'); audioElement.src = '1.mp3'; audioElement.controls = true; audioElement.style.display = 'none'; document.body.appendChild(audioElement); var source = audioContext.createMediaElementSource(audioElement); source.connect(analyser); analyser.connect(audioContext.destination); bufferLength = analyser.frequencyBinCount; dataArray = new Uint8Array(bufferLength); } audioElement.play(); draw(); }); document.getElementById('pauseButton').addEventListener('click', function() { audioElement.pause(); cancelAnimationFrame(animationId); });
當用戶點擊"播放"按鈕時,創(chuàng)建一個新的AudioContext
對象用于處理音頻,創(chuàng)建一個AnalyserNode
對象用于分析音頻頻譜。然后創(chuàng)建一個audio
元素并將其設(shè)置為要播放的音頻文件。將audio
元素連接到AnalyserNode
,將AnalyserNode
連接到AudioContext
的目標(通常是揚聲器)。設(shè)置頻率分析器的參數(shù),包括FFT大小。
當用戶點擊"播放"按鈕時,音頻開始播放,并且在draw()
函數(shù)中的requestAnimationFrame(draw)
中調(diào)用的循環(huán)中,更新頻譜數(shù)據(jù)并繪制頻譜圖。首先,使用analyser.getByteFrequencyData(dataArray)
獲取音頻頻譜數(shù)據(jù)。然后,通過遍歷數(shù)據(jù)數(shù)組,計算每個頻譜條的高度,并根據(jù)頻譜條的位置在畫布上繪制矩形。顏色根據(jù)頻譜條的索引值計算,使得頻譜圖呈現(xiàn)彩虹色的效果。
當用戶點擊"暫停"按鈕時,音頻暫停播放,并調(diào)用cancelAnimationFrame(animationId)
來停止繪制頻譜圖。
請確保將audioElement.src
中的路徑替換為你要播放的實際音頻文件的路徑。
當然修改draw函數(shù)可以得到其他的音頻圖,比如波形圖
具體的draw代碼如下
這個函數(shù)主要用于繪制音頻的時域波形圖。它也使用了requestAnimationFrame()
方法來請求下一幀動畫,并將自身作為回調(diào)函數(shù)。
在函數(shù)內(nèi)部,analyser.getByteTimeDomainData(dataArray)
用于獲取當前的音頻時域數(shù)據(jù),將數(shù)據(jù)存儲在dataArray
數(shù)組中。
然后,畫布被清空,并將背景顏色設(shè)置為lime。
接下來,設(shè)置線條的寬度和顏色。
然后,開始繪制路徑。
通過計算每個數(shù)據(jù)片段的寬度,以及根據(jù)時域數(shù)據(jù)計算每個點的縱坐標,確定波形圖的繪制參數(shù)。
然后,根據(jù)波形點的位置,使用moveTo()
方法將繪制路徑移動到第一個點的位置,并使用lineTo()
方法連接到下一個點的位置。這樣就形成了一條完整的波形路徑。
在遍歷完所有的數(shù)據(jù)點后,使用lineTo()
方法將最后一個點連接到畫布的右側(cè)中點,以形成閉合路徑。
最后,使用stroke()
方法繪制路徑。
通過不斷調(diào)用requestAnimationFrame()
方法并在每一幀更新波形圖,可以實現(xiàn)連續(xù)的動畫效果。
function draw() { // 請求下一幀動畫 animationId = requestAnimationFrame(draw); // 獲取音頻時域數(shù)據(jù) analyser.getByteTimeDomainData(dataArray); // 清空畫布并設(shè)置背景顏色為lime ctx.fillStyle = 'lime'; ctx.fillRect(0, 0, canvas.width, canvas.height); // 設(shè)置線條寬度和顏色 ctx.lineWidth = 2; ctx.strokeStyle = 'black'; // 開始繪制路徑 ctx.beginPath(); // 計算每個數(shù)據(jù)片段的寬度 var sliceWidth = canvas.width * 1.0 / bufferLength; var x = 0; // 遍歷時域數(shù)據(jù)數(shù)組,繪制波形 for (var i = 0; i < bufferLength; i++) { // 將數(shù)據(jù)歸一化到范圍[-1, 1] var v = dataArray[i] / 128.0; // 計算波形點的縱坐標 var y = v * canvas.height / 2; if (i === 0) { // 移動到第一個點的位置 ctx.moveTo(x, y); } else { // 連接到下一個點的位置 ctx.lineTo(x, y); } // 更新下一個點的橫坐標 x += sliceWidth; } // 連接最后一個點到畫布右側(cè)中點,形成閉合路徑 ctx.lineTo(canvas.width, canvas.height / 2); // 繪制路徑 ctx.stroke(); }
到此這篇關(guān)于基于JavaScript實現(xiàn)簡單的音樂音譜圖效果的文章就介紹到這了,更多相關(guān)JavaScript音譜圖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript 判斷數(shù)據(jù)類型的4種方法
這篇文章主要介紹了JavaScript 判斷數(shù)據(jù)類型的4種方法,幫助大家更好的理解和學(xué)習JavaScript,感興趣的朋友可以了解下2020-09-09javascript 自動標記來自搜索結(jié)果頁的關(guān)鍵字
使用javascript自動標記來自搜索結(jié)果頁的關(guān)鍵字的實現(xiàn)代碼。2010-01-01基于javascript實現(xiàn)彩票隨機數(shù)生成(簡單版)
這篇文章主要介紹了基于javascript實現(xiàn)彩票隨機數(shù)生成的相關(guān)資料,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-01-01使用Javascript監(jiān)控前端相關(guān)數(shù)據(jù)的代碼
本篇文章詳細的介紹了使用Javascript監(jiān)控前端相關(guān)數(shù)據(jù),可以及時的監(jiān)控前端的錯誤,加載時間等,有需要的可以了解一下。2016-10-10前端JavaScript中l(wèi)ocation.reload刷新頁面用法詳解
這篇文章主要介紹了前端JavaScript中l(wèi)ocation.reload刷新頁面用法的相關(guān)資料,location.reload()是JavaScript中用于重新加載當前頁面的方法,它可以接受一個布爾參數(shù),以決定是否忽略緩存,文中通過代碼介紹的非常詳細,需要的朋友可以參考下2025-02-02