HTML5 Canvas 实现简易 绘制音乐环形频谱图

简介: 参考资料:1.Web 技术研究所2.Web_Audio_API0.启发在B站我们有很多的小伙伴们应该都看到过用AE做的可视化音乐播放器播放音乐的视频,看着特别酷炫带感有木有。

参考资料:
1.Web 技术研究所
2.Web_Audio_API

0.启发

在B站我们有很多的小伙伴们应该都看到过用AE做的可视化音乐播放器播放音乐的视频,看着特别酷炫带感有木有。

img_88baeb52e5aaeb5e468081841bb8dda3.jpe
B站截图
img_4b69c704effb2ba07164d0ecb0113c9a.jpe
B站截图
img_d8ae2623dcea46d6f195edb329335aa5.jpe
B站截图

所以今天我就用 Canvas 做个简单 环形频谱图。

那么~ ヾ(o・ω・)ノ 开始吧!

1.首先绘制静态的效果

img_ce6b4b8a707b7ac2bfa2b98a13051dc0.jpe
静态效果

绘制静态效果很简单,我们只要从一点出发根据一定角度绘制线条,然后画个圆从中点开始覆盖线条就行了

<canvas id="wrap" height="800" width="800"></canvas>
<script>
    var wrap = document.getElementById("wrap");
    var cxt = wrap.getContext("2d");
    (function drawSpectrum() {
        cxt.clearRect(0, 0, wrap.width, wrap.height);
        //画线条
        for (var i = 0; i < 360; i++) {
            var value = 8;
            cxt.beginPath();
            cxt.lineWidth = 2;
            cxt.moveTo(300, 300);
             //R * cos (PI/180*一次旋转的角度数) ,-R * sin (PI/180*一次旋转的角度数)
            cxt.lineTo(Math.cos((i * 1) / 180 * Math.PI) * (200 + value) + 300, (- Math.sin((i * 1) / 180 * Math.PI) * (200 + value) + 300));
            cxt.stroke();
        }
        //画一个小圆,将线条覆盖
        cxt.beginPath();
        cxt.lineWidth = 1;
        cxt.arc(300, 300, 200, 0, 2 * Math.PI, false); 
        cxt.fillStyle = "#fff";
        cxt.stroke();
        cxt.fill(); 
    })();
</script>

2.调用AudioAPI,绘制音乐的频谱图

img_6462f17a8f32ec448dfd349b8c9d445e.gif
绘制音乐的频谱图

第一步完成后,第二步就很简单了,通过调用AudioAPI获取音频变化来改变线条长度。

注意!!!最新chrome浏览器可能需要在http服务器上运行~

你可以参考文章绘制音乐的频谱图(使用Analyser节点)

<input type="button" onclick="audio.play()" value="播放" />
<input type="button" onclick="audio.pause()" value="暂停" />
<canvas id="wrap" height="800" width="800"></canvas>
<script> 
    var wrap = document.getElementById("wrap");
    var cxt = wrap.getContext("2d");
    //获取API
    var AudioContext = AudioContext || webkitAudioContext;
    var context = new AudioContext;
    //加载媒体
    var audio = new Audio("demo.mp3");
    //创建节点
    var source = context.createMediaElementSource(audio);
    var analyser = context.createAnalyser();
    //连接:source → analyser → destination
    source.connect(analyser);
    analyser.connect(context.destination);
    //创建数据
    var output = new Uint8Array(360);
    (function drawSpectrum() {
        analyser.getByteFrequencyData(output);//获取频域数据
        cxt.clearRect(0, 0, wrap.width, wrap.height);
        //画线条
        for (var i = 0; i < 360; i++) {
            var value = output[i] / 8;//<===获取数据
            cxt.beginPath();
            cxt.lineWidth = 2;
            cxt.moveTo(300, 300);
            //R * cos (PI/180*一次旋转的角度数) ,-R * sin (PI/180*一次旋转的角度数)
            cxt.lineTo(Math.cos((i * 1) / 180 * Math.PI) * (200 + value) + 300, (- Math.sin((i * 1) / 180 * Math.PI) * (200 + value) + 300));
            cxt.stroke();
        }
        //画一个小圆,将线条覆盖
        cxt.beginPath();
        cxt.lineWidth = 1;
        cxt.arc(300, 300, 200, 0, 2 * Math.PI, false);
        cxt.fillStyle = "#fff";
        cxt.stroke();
        cxt.fill();
        //请求下一帧
        requestAnimationFrame(drawSpectrum);
    })();
</script>

3.环形左右同步显示

实习步骤2后其实已经完成一大半了,不过细心的小伙伴们会发现环形最右端点上的线条间差了好多。


img_52746a60b401d02c240a4fd3f532216e.jpe
demo

处理办法很多,我们用其中一个简单的办法处理,那就是让其左右对称的显示。

<input type="button" onclick="audio.play()" value="播放" />
<input type="button" onclick="audio.pause()" value="暂停" />
<canvas id="wrap" height="550" width="800"></canvas>
<script> 
    var wrap = document.getElementById("wrap");
    var cxt = wrap.getContext("2d");
    //获取API
    var AudioContext = AudioContext || webkitAudioContext;
    var context = new AudioContext;
    //加载媒体
    var audio = new Audio("demo.mp3");
    //创建节点
    var source = context.createMediaElementSource(audio);
    var analyser = context.createAnalyser();
    //连接:source → analyser → destination
    source.connect(analyser);
    analyser.connect(context.destination);
    //创建数据
    var output = new Uint8Array(361);
    (function drawSpectrum() {
        analyser.getByteFrequencyData(output);//获取频域数据
        cxt.clearRect(0, 0, wrap.width, wrap.height);
        //画线条
        for (var i = 0; i < output.length; i++) {
            var value = output[i] / 10;
            //绘制左半边
            cxt.beginPath();
            cxt.lineWidth = 1;
            cxt.moveTo(300, 300);
            cxt.lineTo(Math.cos((i *0.5 + 90) / 180 * Math.PI) * (200 + value) + 300, (- Math.sin((i *0.5 + 90) / 180 * Math.PI) * (200 + value) + 300));
            cxt.stroke();
            //绘制右半边
            cxt.beginPath();
            cxt.lineWidth = 1;
            cxt.moveTo(300, 300);
            cxt.lineTo( (Math.sin((i *0.5) / 180 * Math.PI) * (200 + value) + 300),-Math.cos((i *0.5) / 180 * Math.PI) * (200 + value) + 300);
            cxt.stroke();
        }
        //画一个小圆,将线条覆盖
        cxt.beginPath();
        cxt.lineWidth = 1;
        cxt.arc(300, 300, 200, 0, 2 * Math.PI, false);
        cxt.fillStyle = "#fff";
        cxt.stroke();
        cxt.fill();
        //请求下一帧
        requestAnimationFrame(drawSpectrum);
    })();
</script>
img_c102eb5a49741f0c1a79a747e9f2e21b.gif
效果

4.添加波形图

最后我们来尝试在圈内,添加一个波形图

你可以参考文章绘制音乐的波形图(使用Analyser节点)

img_f1901d535eccd8a1e9dd179a9fbf27fa.gif
示例

<input type="button" onclick="audio.play()" value="播放" />
<input type="button" onclick="audio.pause()" value="暂停" />
<canvas id="wrap" height="550" width="800"></canvas>
<script> 
    var wrap = document.getElementById("wrap");
    var cxt = wrap.getContext("2d");
    //获取API
    var AudioContext = AudioContext || webkitAudioContext;
    var context = new AudioContext;
    //加载媒体
    var audio = new Audio("demo.mp3");
    //创建节点
    var source = context.createMediaElementSource(audio);
    var analyser = context.createAnalyser();
    //连接:source → analyser → destination
    source.connect(analyser);
    analyser.connect(context.destination);
    //创建数据
    var output = new Uint8Array(361);
    //计算出采样频率44100所需的缓冲区长度
    var length = analyser.frequencyBinCount * 44100 / context.sampleRate | 0;
    //创建数据 
    var output2 = new Uint8Array(length);
    (function drawSpectrum() {
        analyser.getByteFrequencyData(output);//获取频域数据
        cxt.clearRect(0, 0, wrap.width, wrap.height);
        //画线条
        for (var i = 0; i < output.length; i++) {
            var value = output[i] / 10;
            //绘制左半边
            cxt.beginPath();
            cxt.lineWidth = 1;
            cxt.moveTo(300, 300);
            cxt.lineTo(Math.cos((i * 0.5 + 90) / 180 * Math.PI) * (200 + value) + 300, (- Math.sin((i * 0.5 + 90) / 180 * Math.PI) * (200 + value) + 300));
            cxt.stroke();
            //绘制右半边
            cxt.beginPath();
            cxt.lineWidth = 1;
            cxt.moveTo(300, 300);
            cxt.lineTo((Math.sin((i * 0.5) / 180 * Math.PI) * (200 + value) + 300), -Math.cos((i * 0.5) / 180 * Math.PI) * (200 + value) + 300);
            cxt.stroke();
        }
        //画一个小圆,将线条覆盖
        cxt.beginPath();
        cxt.lineWidth = 1;
        cxt.arc(300, 300, 200, 0, 2 * Math.PI, false);
        cxt.fillStyle = "#fff";
        cxt.stroke();
        cxt.fill();
        //将缓冲区的数据绘制到Canvas上 
        analyser.getByteTimeDomainData(output2);
        var height = 100, width = 400;
        cxt.beginPath();
        for (var i = 0; i < width; i++) {
            cxt.lineTo(i + 100, 300 - (height / 2 * (output2[output2.length * i / width | 0] / 256 - 0.5)));
        }
        cxt.stroke();
        //请求下一帧
        requestAnimationFrame(drawSpectrum);
    })();
</script>

5.尝试波形反向显示

如果是波形向圆形内显示 又改如何实现呢?
起始也很简单稍微改下上面的代码就可以了

首先我们要去掉圆形覆盖,用空心圆代替

<input type="button" onclick="audio.play()" value="播放" />
<input type="button" onclick="audio.pause()" value="暂停" />
<canvas id="wrap" height="800" width="800"></canvas>
<script> 
    var wrap = document.getElementById("wrap");
    var cxt = wrap.getContext("2d");
    //获取API
    var AudioContext = AudioContext || webkitAudioContext;
    var context = new AudioContext;
    //加载媒体
    var audio = new Audio('demo.mp3');
    //创建节点
    var source = context.createMediaElementSource(audio);
    var analyser = context.createAnalyser();
    //连接:source → analyser → destination
    source.connect(analyser);
    analyser.connect(context.destination);
    //创建数据
    var output = new Uint8Array(180); 
    var du = 2;//角度
    var potInt = { x: 300, y: 300 };//起始坐标
    var R = 200;//半径
    var W = 2;//宽
    (function drawSpectrum() {
        analyser.getByteFrequencyData(output);//获取频域数据
        cxt.clearRect(0, 0, wrap.width, wrap.height);
        //画线条
        for (var i = 0; i < 180; i++) {
            var value = output[i] / 10;//<===获取数据 
            cxt.beginPath();
            cxt.lineWidth = W;
            cxt.moveTo(( Math.sin((i * du) / 180 * Math.PI) * R + potInt.y),-Math.cos((i * du) / 180 * Math.PI) * R + potInt.x);//从圆边开始
            Rv = (R + value);
            cxt.lineTo( ( Math.sin((i * du) / 180 * Math.PI) * Rv + potInt.y),-Math.cos((i * du) / 180 * Math.PI) * Rv + potInt.x);
            cxt.stroke();
        } 
        cxt.fill();
        //画一个空心小圆,将线条覆盖
        cxt.beginPath();
        cxt.lineWidth = 1;
        cxt.arc(300, 300, 200, 0, 2 * Math.PI, false); 
        cxt.stroke(); 
        cxt.closePath();
        //请求下一帧
        requestAnimationFrame(drawSpectrum);
    })();
</script>
img_0a9521233ccce5790e7b9d4f82e33e65.gif
示例

接下去就简单了 只要让圆半径减去对应值就行了

<input type="button" onclick="audio.play()" value="播放" />
<input type="button" onclick="audio.pause()" value="暂停" />
<canvas id="wrap" height="800" width="800"></canvas> 
<script> 
    var wrap = document.getElementById("wrap");
    var cxt = wrap.getContext("2d");
    //获取API
    var AudioContext = AudioContext || webkitAudioContext;
    var context = new AudioContext;
    //加载媒体
    var audio = new Audio('demo.mp3');
    //创建节点
    var source = context.createMediaElementSource(audio);
    var analyser = context.createAnalyser();
    //连接:source → analyser → destination
    source.connect(analyser);
    analyser.connect(context.destination);
    //创建数据
    var output = new Uint8Array(180); 
    var du = 2;//角度
    var potInt = { x: 300, y: 300 };//起始坐标
    var R = 200;//半径
    var W = 2;//宽
    (function drawSpectrum() {
        analyser.getByteFrequencyData(output);//获取频域数据
        cxt.clearRect(0, 0, wrap.width, wrap.height);
        //画线条
        for (var i = 0; i < 180; i++) {
            var value = output[i] / 10;//<===获取数据 
            cxt.beginPath();
            cxt.lineWidth = W; 
            Rv = (R -value);
            cxt.moveTo(( Math.sin((i * du) / 180 * Math.PI) * R + potInt.y),-Math.cos((i * du) / 180 * Math.PI) * R + potInt.x);
            cxt.lineTo( ( Math.sin((i * du) / 180 * Math.PI) * Rv + potInt.y),-Math.cos((i * du) / 180 * Math.PI) * Rv + potInt.x);
            cxt.stroke();
        } 
        cxt.fill();
        //画一个空心小圆,将线条覆盖
        cxt.beginPath();
        cxt.lineWidth = 1;
        cxt.arc(300, 300, 200, 0, 2 * Math.PI, false); 
        cxt.stroke(); 
        cxt.closePath();
        //请求下一帧
        requestAnimationFrame(drawSpectrum);
    })();
</script>
img_125564aff96eae5ab0b4423ed040ceef.gif
示例

6.双向显示

既然实现了反向显示了那双向显示也就很简单了

<input type="button" onclick="audio.play()" value="播放" />
<input type="button" onclick="audio.pause()" value="暂停" />
<canvas id="wrap" height="800" width="800"></canvas> 
<script> 
    var wrap = document.getElementById("wrap");
    var cxt = wrap.getContext("2d");
    //获取API
    var AudioContext = AudioContext || webkitAudioContext;
    var context = new AudioContext;
    //加载媒体
    var audio = new Audio('demo.mp3');
    //创建节点
    var source = context.createMediaElementSource(audio);
    var analyser = context.createAnalyser();
    //连接:source → analyser → destination
    source.connect(analyser);
    analyser.connect(context.destination);
    //创建数据
    var output = new Uint8Array(180); 
    var du = 2;//角度
    var potInt = { x: 300, y: 300 };//起始坐标
    var R = 200;//半径
    var W = 4;//宽
    (function drawSpectrum() {
        analyser.getByteFrequencyData(output);//获取频域数据
        cxt.clearRect(0, 0, wrap.width, wrap.height);
        //画线条
        for (var i = 0; i < 180; i++) {
            var value = output[i] / 10;//<===获取数据 
            cxt.beginPath();
            cxt.lineWidth = W; 
            Rv1 = (R -value);
            Rv2 = (R +value);
            cxt.moveTo(( Math.sin((i * du) / 180 * Math.PI) * Rv1 + potInt.y),-Math.cos((i * du) / 180 * Math.PI) * Rv1 + potInt.x);
            cxt.lineTo( ( Math.sin((i * du) / 180 * Math.PI) * Rv2 + potInt.y),-Math.cos((i * du) / 180 * Math.PI) * Rv2 + potInt.x);
            cxt.stroke();
        } 
        cxt.fill();
        //画一个空心小圆,将线条覆盖
        cxt.beginPath();
        cxt.lineWidth = 1;
        cxt.arc(300, 300, 200, 0, 2 * Math.PI, false); 
        cxt.stroke(); 
        cxt.closePath();
        //请求下一帧
        requestAnimationFrame(drawSpectrum);
    })();
</script>
img_822404e00f70c8783fd17709e8a79e45.gif
示例
img_91d9b6a7acade2ff0009961273d21a88.gif
添加自己的创意
img_c15d2cc01a95a1b1181a3071edfec9a2.gif
添加自己的创意

(o゚v゚)ノ 大家可以参考着尝试自己DIY一个好玩的播放器出来

相关文章
|
18天前
|
Web App开发 移动开发 前端开发
html5 canvas五彩碎纸屑飘落动画特效
h5 canvas飘落纸片动画是一款实现五彩纸屑飘落的背景动画特效,基于canvas绘制的空中飘落的纸屑片动画特效,适用于网页动态背景效果代码。简单使用,欢迎下载!代码适用浏览器:搜狗、360、FireFox(建议)、Chrome、Safari、Opera、傲游、世界之窗,是一款不错的的特效插件,希望大家喜欢!
34 5
|
29天前
|
前端开发
基于canvas实现的彩色纸屑组成文字3d动画HTML源码
基于canvas实现的彩色纸屑组成文字3d动画HTML源码
24 0
基于canvas实现的彩色纸屑组成文字3d动画HTML源码
|
29天前
|
移动开发 前端开发 HTML5
HTML5 Canvas制作的粒子十秒倒计时源码
一段基于HTML5 Canvas制作的粒子爆炸,十秒数字倒计时,全屏倒计时动画效果,给人一种非常大气的视觉感
34 0
HTML5 Canvas制作的粒子十秒倒计时源码
|
1月前
|
前端开发 JavaScript
Canvas三维变化背景动画HTML源码
Canvas三维变化背景动画HTML源码
34 5
|
1月前
|
JSON 移动开发 数据格式
html5+css3+js移动端带歌词音乐播放器代码
音乐播放器特效是一款html5+css3+js制作的手机移动端音乐播放器代码,带歌词显示。包括支持单曲循环,歌词显示,歌曲搜索,音量控制,列表循环等功能。利用json获取音乐歌单和歌词,基于html5 audio属性手机音乐播放器代码。
117 6
|
3月前
|
XML 移动开发 前端开发
HTML5 SVG和canvas的性能探讨
HTML5 中的 SVG(可缩放矢量图形)和 Canvas(画布)分别用于网页图形绘制。SVG 基于矢量图形,使用 XML 描述,适合静态或少量动态内容(如图标、图表),易于编辑且保持高分辨率;Canvas 则基于位图,通过 JavaScript 绘制,更适合快速更新大量图形的场景(如游戏、动态动画),但在复杂图形计算时可能遇到性能瓶颈。总体而言,SVG 适用于静态和少量动态内容,而 Canvas 更适合高频率更新和性能要求高的场景。
|
3月前
|
移动开发 前端开发 JavaScript
HTML5 Canvas详解及应用
HTML5 Canvas 允许通过 JavaScript 在网页上动态绘制图形、动画等视觉内容。首先在 HTML 中定义 `&lt;canvas&gt;` 元素,并通过 JavaScript 获取画布上下文进行绘制。常见方法包括绘制矩形、路径、圆形和文本,以及处理图像和创建动画效果。适用于游戏开发、数据可视化、图像编辑和动态图形展示等多种应用场景。需要注意性能优化、无状态绘制及自行处理事件等问题。
|
3月前
|
移动开发 前端开发 数据挖掘
用HTML5中的 画布(Canvas)在“圳品”信息系统网页上绘制显示饼图
用HTML5中的 画布(Canvas)在“圳品”信息系统网页上绘制显示饼图
|
3月前
|
前端开发
在 HTML canvas 绘制文本
在 HTML canvas 绘制文本
25 0
|
4月前
|
移动开发 前端开发 JavaScript