日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

利用AudioContext来实现网易云音乐的鲸鱼音效

發(fā)布時間:2024/1/1 编程问答 52 豆豆
生活随笔 收集整理的這篇文章主要介紹了 利用AudioContext来实现网易云音乐的鲸鱼音效 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

一直覺得網(wǎng)易云音樂的用戶體驗是很不錯的,很早就注意到了里面的鯨魚音效,如下圖,就是一個環(huán)形的跟著音樂節(jié)拍跳動的特效。

gif動圖可能效果不太理想,可以直接在手機(jī)上體驗

身為前端憑著本能的好奇心和探索心當(dāng)然會研究一番,如何在頁面上實現(xiàn)該效果?

1.AudioContext

其實這類動效原理并不復(fù)雜,你需要一堆數(shù)據(jù)來表述每一塊的高度,然后通過某種方式,讓前臺渲染可見即可。

如何獲取音樂實時的節(jié)拍數(shù)據(jù)呢,這里用到了AudioContext

AudioContext接口表示由音頻模塊連接而成的音頻處理圖,每個模塊對應(yīng)一個AudioNode。AudioContext可以控制它所包含的節(jié)點(diǎn)的創(chuàng)建,以及音頻處理、解碼操作的執(zhí)行。做任何事情之前都要先創(chuàng)建AudioContext對象,因為一切都發(fā)生在這個環(huán)境之中。

這一段是從developer.mozilla.org/zh-CN/docs/…摘錄下來的,里面有很多方法,詳細(xì)可以看具體文檔,這里只介紹我們下面用到的其中幾個

1.1 AudioContext.createAnalyser()

AudioContext的createAnalyser()方法能創(chuàng)建一個AnalyserNode,可以用來獲取音頻時間和頻率數(shù)據(jù),以及實現(xiàn)數(shù)據(jù)可視化。

var audioCtx = new AudioContext(); var analyser = audioCtx.createAnalyser(); 復(fù)制代碼

這里返回的是一個AnalyserNode對象。

AnalyserNode 賦予了節(jié)點(diǎn)可以提供實時頻率及時間域分析的信息。它使一個 AudioNode 通過音頻流不做修改的從輸入到輸出, 但允許你獲取生成的數(shù)據(jù), 處理它并創(chuàng)建音頻可視化。

AnalyserNode還有很多屬性

AnalyserNode.fftSize

AnalyserNode 接口的 fftSize 屬性的值是一個無符號長整型的值, 表示(信號)樣本的窗口大小。當(dāng)執(zhí)行快速傅里葉變換(Fast Fourier Transfor (FFT))時,這些(信號)樣本被用來獲取頻域數(shù)據(jù)。

fftSize 屬性的值必須是從32到32768范圍內(nèi)的2的非零冪; 其默認(rèn)值為2048。

AnalyserNode.frequencyBinCount 只讀

frequencyBinCount 的值固定為 AnalyserNode 接口中fftSize值的一半. 該屬性通常用于可視化的數(shù)據(jù)值的數(shù)量.

1.2 AudioContext.createMediaElementSource()

AudioContext 的 createMediaElementSource() 方法用于創(chuàng)建一個新的 MediaElementAudioSourceNode 對象,輸入某個存在的 HTML <audio> or <video> 元素, 對應(yīng)的音頻即可被播放或者修改。

var audioCtx = new AudioContext(); var source = audioCtx.createMediaStreamSource(stream); 復(fù)制代碼

2.實現(xiàn)

上面很多api可能剛開始看的時候會犯暈,不過沒事,下面一步一步寫成一個例子就明白了。

這里我們采用canvas來繪制頻譜圖,下面簡單寫一個布局

<canvas id='canvas' width="600" height="600"></canvas> <audio id="audio" controls autoplay loop></audio> 復(fù)制代碼

加點(diǎn)樣式

body{background: black; } canvas,audio{display: block;margin: 0 auto; } 復(fù)制代碼

下面來通過音頻來獲取頻譜數(shù)據(jù)

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) 復(fù)制代碼

打印一下這個array,是一個長度為256的數(shù)組

這就是音頻的頻譜數(shù)據(jù),這個長度跟上面設(shè)置的analyser.fftSize有關(guān),是他的一半,也就是說,設(shè)置的越大,得到的數(shù)據(jù)越多,分析的也越準(zhǔn)確。這里只是繪制一些條形圖,并不需要默認(rèn)的2048那么大,所以這里設(shè)置了512。

普通的頻譜圖

在此之前,我們先來實現(xiàn)一下常見的垂直頻譜圖,只需要用到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);//根據(jù)寬度和間距計算出可以放多少個方塊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);//從頻譜數(shù)據(jù)中每隔step均勻取出meterNum個數(shù)據(jù)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(); 復(fù)制代碼

如果需要漸變色的話,可以

var gradient = ctx.createLinearGradient(0, 0, 0, 300); gradient.addColorStop(1, '#0f00f0'); gradient.addColorStop(0.5, '#ff0ff0'); gradient.addColorStop(0, '#f00f00'); ctx.fillStyle = gradient ;//填充 復(fù)制代碼

完整代碼可以查看demo

環(huán)形的頻譜圖

如果上面的頻譜圖很清楚了的話,下面的環(huán)形也輕而易舉了,主要用到了坐標(biāo)的旋轉(zhuǎn)

這里注意的是在進(jìn)行translate和rotate操作時需要進(jìn)行ctx.save()和ctx.restore(),因為操作的是坐標(biāo)系,而不是元素本身,可以多嘗試一下

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;//環(huán)形半徑 var minHeight = 2; var meterWidth = 5; var meterNum = 180;//設(shè)置方塊的數(shù)量,考慮到閉環(huán)的關(guān)系 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(); 復(fù)制代碼

小tip

在進(jìn)行旋轉(zhuǎn)操作時,如果你每次旋轉(zhuǎn)以后,都把坐標(biāo)系還原,那么在循環(huán)的時候需要旋轉(zhuǎn)30,60,90...這樣

ctx.save(); ctx.rotate( 2*PI/meterNum*i ); ctx.restore(); 復(fù)制代碼

如果你在每次旋轉(zhuǎn)以后,不還原坐標(biāo)系,那么每次就是在上一次的基礎(chǔ)上繼續(xù)旋轉(zhuǎn)

//ctx.save(); ctx.rotate( 2*PI/meterNum*i ); //ctx.restore(); 復(fù)制代碼

很顯然,下面的方式更精簡

完整代碼可以查看demo

小節(jié)

以上就實現(xiàn)了環(huán)形的頻譜圖,是不是越來越靠近網(wǎng)易云音樂的鯨魚音效了呢,中間加一個自動旋轉(zhuǎn)的專輯封面就可以了~

之前寫過幾篇都是關(guān)于css的文章,有人可能覺得是不是不會js啊,天天搗鼓css,其實并不是這樣的,各自有各自的職責(zé)范圍,像界面UI之類的,本來就是樣式上的事情,很多人一看看上去覺得css實現(xiàn)不了,馬上就搬出js,效果是出來了,但體驗差了一大截。


如果喜歡的文章的話,可以點(diǎn)贊并收藏,多多關(guān)注我的博客

總結(jié)

以上是生活随笔為你收集整理的利用AudioContext来实现网易云音乐的鲸鱼音效的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。