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一个好玩的播放器出来

相关文章
|
2月前
|
前端开发 JavaScript API
2024 新年HTML5+Canvas制作3D烟花特效(附源码)
2024 新年HTML5+Canvas制作3D烟花特效(附源码)
50 0
|
7月前
|
移动开发 前端开发 Shell
《HTML5 Canvas核心技术 图形、动画与游戏开发》 读书笔记
《HTML5 Canvas核心技术 图形、动画与游戏开发》 读书笔记
|
2月前
|
存储 移动开发 前端开发
HTML新特性【HTML5内联SVG、SVG_矩形、SVG 与 Canvas两者间的区别 、HTML5_MathML 】(三)-全面详解(学习总结---从入门到深化)
HTML新特性【HTML5内联SVG、SVG_矩形、SVG 与 Canvas两者间的区别 、HTML5_MathML 】(三)-全面详解(学习总结---从入门到深化)
46 0
|
1月前
|
前端开发 JavaScript 容器
编程笔记 html5&css&js 032 HTML Canvas
编程笔记 html5&css&js 032 HTML Canvas
|
3月前
|
移动开发 前端开发 JavaScript
html5 Canvas 绘制基本图形 从直线图形到使用路径 - 直线、矩形、路径、多边形、复杂组合图形
html5 Canvas 绘制基本图形 从直线图形到使用路径 - 直线、矩形、路径、多边形、复杂组合图形
123 0
html5 Canvas 绘制基本图形 从直线图形到使用路径 - 直线、矩形、路径、多边形、复杂组合图形
|
3月前
|
存储 移动开发 前端开发
HTML新特性【HTML5内联SVG、SVG_矩形、SVG 与 Canvas两者间的区别 、HTML5_MathML 】(三)-全面详解(学习总结---从入门到深化)(下)
HTML新特性【HTML5内联SVG、SVG_矩形、SVG 与 Canvas两者间的区别 、HTML5_MathML 】(三)-全面详解(学习总结---从入门到深化)
29 0
|
3月前
|
XML 移动开发 前端开发
HTML新特性【HTML5内联SVG、SVG_矩形、SVG 与 Canvas两者间的区别 、HTML5_MathML 】(三)-全面详解(学习总结---从入门到深化)(上)
HTML新特性【HTML5内联SVG、SVG_矩形、SVG 与 Canvas两者间的区别 、HTML5_MathML 】(三)-全面详解(学习总结---从入门到深化)
29 0
|
4月前
|
前端开发
HTML和CSS拼成的像素图
HTML和CSS拼成的像素图
22 0
|
4月前
|
移动开发 前端开发 JavaScript
HTML5新增元素之Canvas-实现太极八卦图和扇子
HTML5新增元素之Canvas-实现太极八卦图和扇子
|
7月前
|
前端开发 开发者