博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
利用AudioContext来实现网易云音乐的鲸鱼音效
阅读量:6263 次
发布时间:2019-06-22

本文共 4656 字,大约阅读时间需要 15 分钟。

一直觉得网易云音乐的用户体验是很不错的,很早就注意到了里面的鲸鱼音效,如下图,就是一个环形的跟着音乐节拍跳动的特效。

鲸鱼音效

gif动图可能效果不太理想,可以直接在手机上体验

身为前端凭着本能的好奇心和探索心当然会研究一番,如何在页面上实现该效果?

1.AudioContext

其实这类动效原理并不复杂,你需要一堆数据来表述每一块的高度,然后通过某种方式,让前台渲染可见即可。

如何获取音乐实时的节拍数据呢,这里用到了

AudioContext接口表示由音频模块连接而成的音频处理图,每个模块对应一个
AudioNode
AudioContext可以控制它所包含的节点的创建,以及音频处理、解码操作的执行。做任何事情之前都要先创建
AudioContext对象,因为一切都发生在这个环境之中。

这一段是从摘录下来的,里面有很多方法,详细可以看具体文档,这里只介绍我们下面用到的其中几个

1.1 AudioContext.createAnalyser()

AudioContextcreateAnalyser()方法能创建一个AnalyserNode,可以用来获取音频时间和频率数据,以及实现数据可视化。

var audioCtx = new AudioContext();var analyser = audioCtx.createAnalyser();

这里返回的是一个对象。

AnalyserNode 赋予了节点可以提供实时频率及时间域分析的信息。它使一个 AudioNode 通过音频流不做修改的从输入到输出, 但允许你获取生成的数据, 处理它并创建音频可视化。

WebAudioFFT

AnalyserNode还有很多属性

AnalyserNode.fftSize

AnalyserNode 接口的 fftSize 属性的值是一个无符号长整型的值, 表示(信号)样本的窗口大小。当执行快速傅里叶变换(Fast Fourier Transfor (FFT))时,这些(信号)样本被用来获取频域数据。

fftSize 属性的值必须是从32到32768范围内的2的非零幂; 其默认值为2048。

AnalyserNode.frequencyBinCount 只读

frequencyBinCount 的值固定为 AnalyserNode 接口中fftSize值的一半. 该属性通常用于可视化的数据值的数量.

1.2 AudioContext.createMediaElementSource()

AudioContextcreateMediaElementSource() 方法用于创建一个新的 MediaElementAudioSourceNode 对象,输入某个存在的 HTML <audio> or <video> 元素, 对应的音频即可被播放或者修改。

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的数组

20181121145740

这就是音频的频谱数据,这个长度跟上面设置的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 ;//填充

music01.gif

完整代码可以查看

环形的频谱图

如果上面的频谱图很清楚了的话,下面的环形也轻而易举了,主要用到了坐标的旋转

这里注意的是在进行translaterotate操作时需要进行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();

很显然,下面的方式更精简

music02.gif

完整代码可以查看

小节

以上就实现了环形的频谱图,是不是越来越靠近网易云音乐的鲸鱼音效了呢,中间加一个自动旋转的专辑封面就可以了~

之前写过几篇都是关于css的文章,有人可能觉得是不是不会js啊,天天捣鼓css,其实并不是这样的,各自有各自的职责范围,像界面UI之类的,本来就是样式上的事情,很多人一看看上去觉得css实现不了,马上就搬出js,效果是出来了,但体验差了一大截。


如果喜欢的文章的话,可以点赞并收藏,多多关注

转载地址:http://ikcpa.baihongyu.com/

你可能感兴趣的文章
RDS SQL Server - 最佳实践 - 高CPU使用率系列之数据类型转换
查看>>
C语言OJ项目参考(1056)输出已交换后的两个值
查看>>
公网数据采集比较(LogHub vs 自建前端机)
查看>>
【云栖大会】Hello,云栖小镇,我们又来了
查看>>
sqoop2:从mysql导出数据到hdfs
查看>>
有些事,该提前想好了的
查看>>
Spring的声明式事务管理
查看>>
RDS SQL Server - 最佳实践 - 高CPU使用率系列之索引缺失
查看>>
socket属性设置与初始化操作
查看>>
[LeetCode] Maximum Product Subarray
查看>>
JavaScript权威设计--JavaScript变量,作用域,声明提前(简要学习笔记四)
查看>>
DIV+CSS导航学习笔记
查看>>
设计素材管理软件 PinCap:测评与优惠
查看>>
sqoop详解
查看>>
【SQL】SQL Server登录常见问题
查看>>
8天玩转并行开发——第七天 简要分析任务与线程池
查看>>
OEA 中 WPF 树型表格虚拟化设计方案
查看>>
简洁的序列预测算法
查看>>
元素的click与dblclick
查看>>
用FMDB 还是 CoreData
查看>>