利用AudioContext来实现网易云音乐的鲸鱼音效
一直觉得网易云音乐的用户体验是很不错的,很早就注意到了里面的鲸鱼音效,如下图,就是一个环形的跟着音乐节拍跳动的特效。
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.fftSize
AnalyserNode 接口的 fftSize 属性的值是一个无符号长整型的值, 表示(信号)样本的窗口大小。当执行快速傅里叶变换(Fast Fourier Transfor (FFT))时,这些(信号)样本被用来获取频域数据。
fftSize 属性的值必须是从32到32768范围内的2的非零幂; 其默认值为2048。
AnalyserNode.frequencyBinCount 只读
frequencyBinCount 的值固定为 AnalyserNode 接口中fftSize值的一半. 该属性通常用于可视化的数据值的数量.
1.2 AudioContext.createMediaElementSource()
AudioContext 的 createMediaElementSource() 方法用于创建一个新的 MediaElementAudioSourceNode 对象,输入某个存在的 HTML <audio> or <video> 元素, 对应的音频即可被播放或者修改。
var audioCtx = new AudioContext();
var source = audioCtx.createMediaStreamSource(stream);
2.实现
上面很多api可能刚开始看的时候会犯晕,不过没事,下面一步一步写成一个例子就明白了。
这里我们采用canvas来绘制频谱图,下面简单写一个布局
<canvas id='canvas' width="600" height="600"></canvas>
<audio id="audio" controls autoplay loop></audio>
加点样式
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,效果是出来了,但体验差了一大截。
如果喜欢的文章的话,可以点赞并收藏,多多关注我的博客
利用AudioContext来实现网易云音乐的鲸鱼音效的更多相关文章
- 如何用Python网络爬虫爬取网易云音乐歌曲
今天小编带大家一起来利用Python爬取网易云音乐,分分钟将网站上的音乐down到本地. 跟着小编运行过代码的筒子们将网易云歌词抓取下来已经不再话下了,在抓取歌词的时候在函数中传入了歌手ID和歌曲名两 ...
- 手把手教你用Python网络爬虫获取网易云音乐歌曲
前天给大家分享了用Python网络爬虫爬取了网易云歌词,在文尾说要爬取网易云歌曲,今天小编带大家一起来利用Python爬取网易云音乐,分分钟将网站上的音乐down到本地. 跟着小编运行过代码的筒子们将 ...
- 模拟制作网易云音乐(AudioContext)
记得好早前在慕课网上看到一款可视化音乐播放器,当前是觉得很是神奇,还能这么玩.由于当时刚刚转行不久,好多东西看得稀里糊涂不明白,于是趁着现在有时间又重新梳理了一遍,然后参照官网的API模拟做了一款网易 ...
- UWP 动画系列之模仿网易云音乐动画
一.前言 最近在弄毕业设计(那时坑爹选了制作个UWP商店的APP),一个人弄得烦躁,在这里记录一些在做毕业设计时的学习过程.由于我的毕业设计是做一个音乐播放器,那么Windows商店上优秀的软件当然是 ...
- 使用网易云音乐,丢掉QQ音乐吧
我是一个听音乐的重度用户,基本上每天大约有三分之一的时间里我在使用网易云音乐去听音乐.包括工作写代码的时候,跑步的时候,去上班的途中我都去听.首先需要声明的是,在这里我不是故意的去抹黑其他的音乐产品, ...
- 《云阅》一个仿网易云音乐UI,使用Gank.Io及豆瓣Api开发的开源项目
CloudReader 一款基于网易云音乐UI,使用GankIo及豆瓣api开发的符合Google Material Desgin阅读类的开源项目.项目采取的是Retrofit + RxJava + ...
- 使用webcollector爬虫技术获取网易云音乐全部歌曲
最近在知乎上看到一个话题,说使用爬虫技术获取网易云音乐上的歌曲,甚至还包括付费的歌曲,哥瞬间心动了,这年头,好听的流行音乐或者经典老歌都开始收费了,只能听不能下载,着实很郁闷,现在机会来了,于是开始研 ...
- Python 获取 网易云音乐热门评论
最近在研究文本挖掘相关的内容,所谓巧妇难为无米之炊,要想进行文本分析,首先得到有文本吧.获取文本的方式有很多,比如从网上下载现成的文本文档,或者通过第三方提供的API进行获取数据.但是有的时候我们想要 ...
- NetCloud——一个网易云音乐评论抓取和分析的Python库
在17的四月份,我曾经写了一篇关于网易云音乐爬虫的文章,还写了一篇关于评论数据可视化的文章.在这大半年的时间里,有时会有一些朋友给我发私信询问一些关于代码方面的问题.所以我最近抽空干脆将原来的代码整理 ...
随机推荐
- OA办公软件篇(一)—组织架构
OA办公软件篇(一)-组织架构 背景 作用 迭代历程 具体实现 写在最后 背景 在说组织架构之前,我们先来说说OA本身. 百度百科解释OA为:办公自动化(Office Automation,简称O ...
- laravel 访问器 和修改器的使用
对于访问器我是这样定义的,就是将数据库中的数据被访问时可以变成我们想要的数据类型(例如:数据库中的时间字段是int类型,要将她变成data(Y-m-d H:i:s),格式类型) 参看博客 https: ...
- think php 未登录,禁止访问页面 + 退出登录
1.首先在Local创建一个基类控制器 D:\PHP\phpstudy_pro\WWW\1906A\pyg>php think make:controller goods/Base --plai ...
- 理解并手写 apply() 函数
apply()函数,在功能上类似于call(),只是传递参数的格式有所不同. dog.eat.call(cat, '鱼', '肉'); dog.eat.apply(cat, ['鱼', '肉']); ...
- 5. 堪比JMeter的.Net压测工具 - Crank 实战篇 - 接口以及场景压测
目录 堪比JMeter的.Net压测工具 - Crank 入门篇 堪比JMeter的.Net压测工具 - Crank 进阶篇 - 认识yml 堪比JMeter的.Net压测工具 - Crank 进阶篇 ...
- 还在写SQL做SAP二开?通过RFC调用NetWeaver,让HANA数据库操作更可靠
相比于从零开始构建全套信息化系统,基于成熟的ERP等行业软件做二次开发是更多中大型企业应对个性化软件需求的首选方案.如何在二开模块中,可靠地对成品软件的数据库进行读写操作,以满足单据自动创建.元数据自 ...
- 前端—我的第一篇博客 梦开始的地方(面向对象版tab栏)
这是我的第一篇博客 博客生涯才开始 但是人生已经过去了二十个年头了 才开始弄这个 也没搞得太懂 我原本的想法是想搞个源代码上来 但是看了半天好像就只能传html源代码 那我还有css js的部分呢 我 ...
- 《前端运维》四、Jenkins--持续构建
首先,我们先来了解下什么叫做持续构建.持续构建简称CI,负责拉取代码库中的代码后,执行用户预定义的脚本,通过一系列编译操作构建出一个制品,并将制品推送到制品库里.常用的持续构建工具有 Gitlab C ...
- Git初始化常用方法
准备工作 安装git sudo apt install git 创建一个ssh密钥 如果已经有ssh密钥了,则这一步不要执行 ssh-keygen -t rsa -C '账号' 复制公钥内容 gedi ...
- LCS&&LRC&&LIS问题
注:最近笔试题经常碰到DP动态规划的问题,但是由于本人没有接触过DP,笔试后看到别人家的答案简洁又漂亮,真的羡慕:难的DP自己可能不会,那再见到常见的LCS和LRS以及LIS为问题总该会吧: 资料参考 ...