摘要:一直覺得網易云音樂的用戶體驗是很不錯的,很早就注意到了里面的鯨魚音效,如下圖,就是一個環形的跟著音樂節拍跳動的特效。
一直覺得網易云音樂的用戶體驗是很不錯的,很早就注意到了里面的鯨魚音效,如下圖,就是一個環形的跟著音樂節拍跳動的特效。
gif動圖可能效果不太理想,可以直接在手機上體驗
身為前端憑著本能的好奇心和探索心當然會研究一番,如何在頁面上實現該效果?
1.AudioContext其實這類動效原理并不復雜,你需要一堆數據來表述每一塊的高度,然后通過某種方式,讓前臺渲染可見即可。
如何獲取音樂實時的節拍數據呢,這里用到了AudioContext
AudioContext接口表示由音頻模塊連接而成的音頻處理圖,每個模塊對應一個AudioNode。AudioContext可以控制它所包含的節點的創建,以及音頻處理、解碼操作的執行。做任何事情之前都要先創建AudioContext對象,因為一切都發生在這個環境之中。
這一段是從https://developer.mozilla.org/zh-CN/docs/Web/API/AudioContext摘錄下來的,里面有很多方法,詳細可以看具體文檔,這里只介紹我們下面用到的其中幾個
1.1 AudioContext.createAnalyser()AudioContext的createAnalyser()方法能創建一個AnalyserNode,可以用來獲取音頻時間和頻率數據,以及實現數據可視化。
var audioCtx = new AudioContext(); var analyser = audioCtx.createAnalyser();
這里返回的是一個AnalyserNode對象。
AnalyserNode 賦予了節點可以提供實時頻率及時間域分析的信息。它使一個 AudioNode 通過音頻流不做修改的從輸入到輸出, 但允許你獲取生成的數據, 處理它并創建音頻可視化。
AnalyserNode還有很多屬性
AnalyserNode.fftSizeAnalyserNode 接口的 fftSize 屬性的值是一個無符號長整型的值, 表示(信號)樣本的窗口大小。當執行快速傅里葉變換(Fast Fourier Transfor (FFT))時,這些(信號)樣本被用來獲取頻域數據。
fftSize 屬性的值必須是從32到32768范圍內的2的非零冪; 其默認值為2048。
AnalyserNode.frequencyBinCount 只讀frequencyBinCount 的值固定為 AnalyserNode 接口中fftSize值的一半. 該屬性通常用于可視化的數據值的數量.
1.2 AudioContext.createMediaElementSource()AudioContext 的 createMediaElementSource() 方法用于創建一個新的 MediaElementAudioSourceNode 對象,輸入某個存在的 HTML or 元素, 對應的音頻即可被播放或者修改。
var audioCtx = new AudioContext(); var source = audioCtx.createMediaStreamSource(stream);2.實現
上面很多api可能剛開始看的時候會犯暈,不過沒事,下面一步一步寫成一個例子就明白了。
這里我們采用canvas來繪制頻譜圖,下面簡單寫一個布局
加點樣式
body{ background: black; } canvas,audio{ display: block; margin: 0 auto; }
下面來通過音頻來獲取頻譜數據
var audio = document.getElementById("audio"); audio.crossOrigin = "anonymous"; audio.src="./406238.mp3"; var ctx = new AudioContext(); var analyser = ctx.createAnalyser(); var audioSrc = ctx.createMediaElementSource(audio); audioSrc.connect(analyser); analyser.connect(ctx.destination); analyser.fftSize = 512; var array = new Uint8Array(analyser.frequencyBinCount); console.log(array)
打印一下這個array,是一個長度為256的數組
這就是音頻的頻譜數據,這個長度跟上面設置的analyser.fftSize有關,是他的一半,也就是說,設置的越大,得到的數據越多,分析的也越準確。這里只是繪制一些條形圖,并不需要默認的2048那么大,所以這里設置了512。
普通的頻譜圖在此之前,我們先來實現一下常見的垂直頻譜圖,只需要用到ctx.fillRect來繪制一個個的方塊就行了
var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d"); var cwidth = canvas.width; var cheight = canvas.height - 2; var meterWidth = 5; //方塊的寬度 var gap = 2; //方塊的間距 var minHeight = 2; var meterNum = cwidth / (meterWidth + gap);//根據寬度和間距計算出可以放多少個方塊 ctx.fillStyle = "rgba(255,255,255,.5)";//填充 function render() { var array = new Uint8Array(analyser.frequencyBinCount); analyser.getByteFrequencyData(array); var step = Math.round(array.length / meterNum);//從頻譜數據中每隔step均勻取出meterNum個數據 ctx.clearRect(0, 0, cwidth, cheight); for (var i = 0; i < meterNum; i++) { var value = array[i * step]; ctx.fillRect(i * (meterWidth+gap) , cheight - value + capHeight, meterWidth, cheight||minHeight); //繪制 } requestAnimationFrame(render); } render();
如果需要漸變色的話,可以
var gradient = ctx.createLinearGradient(0, 0, 0, 300); gradient.addColorStop(1, "#0f00f0"); gradient.addColorStop(0.5, "#ff0ff0"); gradient.addColorStop(0, "#f00f00"); ctx.fillStyle = gradient ;//填充
完整代碼可以查看demo
環形的頻譜圖如果上面的頻譜圖很清楚了的話,下面的環形也輕而易舉了,主要用到了坐標的旋轉
這里注意的是在進行translate和rotate操作時需要進行ctx.save()和ctx.restore(),因為操作的是坐標系,而不是元素本身,可以多嘗試一下
var PI = Math.PI; var canvas = document.getElementById("canvas"); var ctx = canvas.getContext("2d"); var cwidth = canvas.width; var cheight = canvas.height; var cr = 230;//環形半徑 var minHeight = 2; var meterWidth = 5; var meterNum = 180;//設置方塊的數量,考慮到閉環的關系 var gradient = ctx.createLinearGradient(0, -cr, 0, -cwidth/2); gradient.addColorStop(0, "#0f0"); gradient.addColorStop(0.5, "#ff0"); gradient.addColorStop(1, "#f00"); ctx.fillStyle = gradient; function render() { var array = new Uint8Array(analyser.frequencyBinCount); analyser.getByteFrequencyData(array); var step = Math.round(array.length / meterNum); ctx.clearRect(0, 0, cwidth, cheight); ctx.save(); ctx.translate(cwidth/2,cheight/2); for (var i = 0; i < meterNum; i++) { //ctx.save(); var value = array[i * step]; var meterHeight = value*(cheight/2 - cr)/256||minHeight; ctx.rotate( 2*PI/meterNum ); ctx.fillRect( -meterWidth/2 , -cr- meterHeight , meterWidth, meterHeight); //ctx.restore(); } ctx.restore(); requestAnimationFrame(render); } render();
小tip
在進行旋轉操作時,如果你每次旋轉以后,都把坐標系還原,那么在循環的時候需要旋轉30,60,90...這樣
ctx.save(); ctx.rotate( 2*PI/meterNum*i ); ctx.restore();
如果你在每次旋轉以后,不還原坐標系,那么每次就是在上一次的基礎上繼續旋轉
//ctx.save(); ctx.rotate( 2*PI/meterNum );//不需要乘i //ctx.restore();
很顯然,下面的方式更精簡
完整代碼可以查看demo
小節以上就實現了環形的頻譜圖,是不是越來越靠近網易云音樂的鯨魚音效了呢,中間加一個自動旋轉的專輯封面就可以了~
之前寫過幾篇都是關于css的文章,有人可能覺得是不是不會js啊,天天搗鼓css,其實并不是這樣的,各自有各自的職責范圍,像界面UI之類的,本來就是樣式上的事情,很多人一看看上去覺得css實現不了,馬上就搬出js,效果是出來了,但體驗差了一大截。
如果喜歡的文章的話,可以點贊并收藏,多多關注我的博客
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/108799.html
摘要:輸入結點主要負責加載解碼音頻源,比如獲取二進制音頻源的獲取音頻源的等處理結點主要對音頻數據進行計算處理,比如處理音頻振幅的等輸出結點則將音頻輸出至揚聲器或耳機,便是默認的輸出節點。 showImg(https://segmentfault.com/img/remote/1460000012753856); 在VR開發中,除了圖形視覺渲染,音頻處理是重要的一環,好的音頻處理可以欺騙用戶的...
摘要:前言本文翻譯自上的利用,這是中的的一個入門教程。原文是英文,但有日本同志翻譯的日文版。這是為了提供一個基本的低音増幅效果在這個例子中可以設定過濾器的種類,周波數,甚至的值。如果是過濾器的話,可以提供一個比指定周波數低的低音増幅。 前言 本文翻譯自MDN上的《Web Audio APIの利用》,這是HTML5中的Web Audio API的一個入門教程。原文是英文,但有日本同志翻譯的日文...
摘要:高動態范圍,采用進行內部處理。這最大限度地減少體積驟降音頻區域之間,從而導致更均勻的交叉衰減,可能是在電平略有不同區域之間。低通濾波器保持較低的頻率范圍,但丟棄高頻。 引用 Getting Started with Web Audio APIhttp://www.html5rocks.com/en/tutorials/webaudio/intro/ Introduction Audio...
閱讀 1496·2023-04-26 01:28
閱讀 3315·2021-11-22 13:53
閱讀 1420·2021-09-04 16:40
閱讀 3189·2019-08-30 15:55
閱讀 2677·2019-08-30 15:54
閱讀 2489·2019-08-30 13:47
閱讀 3366·2019-08-30 11:27
閱讀 1146·2019-08-29 13:21