在网易云上听Hello - OMFG,无意间点开了MV,看上去很动感,但是简单到就像一个前端动画。于是自己想照着做一个WEB。最终效果和源码已传至github。
首先中间的OMFG四个字母是不动的。
两侧跳跃的图形,应该跟音频相关,这个可以和音频文件的波形、频率对应。
跳跃图形下方还有一小部分倒影,这个可以在作出上方图形后,再以其为参照来制作。
忽明忽暗的背景黑圈也是和音频相关的。
可视化第一步:获取音频数据
先看了些文章,学会了把音频文件的数据读取出来,也就是使用audio api获取音频内容。
var AudioContext = AudioContext || webkitAudioContext;
var context = new AudioContext;
//加载媒体
var audio = new Audio("hello.mp4");
//创建节点
var source = context.createMediaElementSource(audio);
var analyser = context.createAnalyser();
//连接:source → analyser → destination
source.connect(analyser);
analyser.connect(context.destination);
这里先创建一个audio容器,载入音频文件hello.mp4。可以这样理解,source
获取hello.mp4的内容节点,analyser
是为可视化做准备的频谱分析器,destination
是电脑上的声卡。连接后,音频节点先通过分析器,然后传到声卡。
analyser.fftSize = 4096;
var length = analyser.fftSize;
//创建数据
var dataArray = new Uint8Array(length);
fftSize (Fast Fourier Transform) 是快速傅里叶变换,一般情况下是固定值2048。具体作用是什么我也不太清除,但是经过研究,这个值可以决定音频频谱的密集程度。值大了,频谱就松散,值小就密集。
dataArray数组将用来放音频高低音不同区域的数据信息,当音频播放时,每一个时间节点,都有不同的音频数据,使用analyser.getByteFrequencyData(dataArray)
将数据放入数组,用来进行频谱的可视化绘制。
可视化:音频数据的canvas绘制
大家应该都见过,频谱就是类似下面这个图片的样子(波形和频谱是不同的概念)。
我们已经在dataArray
数组中获取到了音频数据,数据的内容是大小不同的数值,分布在数组小标从0
到dataArray.length
的各数组项内。
MDN上面给出的例子,就是直接把数据以方块的形式绘制出来,比如dataArray[0]
的值为128,那么就在画布横坐标0的地方画上128个方块。
明白了这个对应关系,画图就变得简单了。
我先使用线条绘制图形,然后填充,得到了频谱的主体。
在主体的基础上,再用类似的方法绘制一层线条,不填充,并加入随机变量、高度向上平移一些,得到高出主体的线条。
倒影刚开始我想用主体反转变形的方法,发现对图片的反转模糊很简单,图形太复杂。于是我又仿照主体图形的代码,写了一个反转的,高度收缩的倒影。
最后将所有的图形加上渐变填充,就变的色彩鲜明了。
背景变化的黑白光圈是一个中心辐射型的黑白填充,使用随机变量来确定辐射的半径。
到此,完成了所有canvas的动画部分,这些内容全部放到一个需要requestAnimationFrame()
重复执行的块内。
文字部分:CSS动画很方便
绘制完动画后,同样使用canvas绘制开场的文字动画,发现太复杂:文字的滚动,擦除已经很麻烦了,还需要设计颜色样式。决定放弃canvas动画,转用CSS3动画,使用绝对定位,写几个相对坐标改变的动画就搞定了。
文字的倒影是一个绿色填充圆角DIV,加上阴影。
优化
所有工作完成后,发现不同分辨率下显示有问题,我的分辨率是1366*768,到更高的分辨率下就无法全屏显示了。
刚开始我想重新计算图形,使用自适应屏幕大小的方法。发现太复杂,要计算的东西很多。想了想,决定使用对body
的transform:scale(x,y)
来拉伸整个页面:先计算当前分辨率与1366 768的比例,然后拉伸成不同比例。这样挺简单的。
之前做过html5小游戏,加上这个音频可视化,两次使用canvas的感觉都是再跟数学打交道,全是坐标计算方面的工作。计算准确数值,画图什么的还是很简单的,都是基本绘制语句。看来以后还需要多学习数学呵~
效果、源码
DEMO
源码 on github
插曲
我一直都是在一个js副件内写代码的,最终定稿后,一个利索的操作把副件删除了,干净的html和js,等待上传github。传完打开,index.html提示找不到副件.js
,瞬间意识到删错了东西,打开js一看,里面就几行代码。当时心里是崩溃的。学了一下午文件恢复,winhex始终找不到文件的位置。最终在chrome的缓存中取出了js的16进制原始文档。
写代码时遇到的一些问题
波形图 getByteTimeDomainData
和频谱图 getByteFrequencyData
是两种形式的数据。我刚开始使用的是波形图,想做成频谱图的样子真是费了巨大的功夫,想了很多算法把波形图的数据转化成频谱图,无疾而终。
波形图,dataArray
中最大值为256,最小值为0,初始值为128。频谱图初始值为0,只有比0大的值。
不同长度的数组dataArray
console.log
出来,内容有时相同,有时不同?
数组很大时,将analyser的实时数据装入数组,log操作很费时,导致log之后下一个短数组装入数据时,实时数据发生了变化。
canvas有多层画布时,是有先后顺序的,如果想要透明前面的层。除了要设定透明度外,必须给canvas加上一个z-index。