做一个网易云音乐可视化工具
2020-10-04 本文已影响0人
MangfuStudio
前言
最近在Github上看到了一个音乐可视化前端库:vudio.js https://github.com/alex2wong/vudio.js
效果有点炫酷:
本人是非常喜欢将音频可视化的效果,联想到之前的网易云地址解析API,决定将两者结合,做个简单的网易云音乐可视化小工具。
今天将制作过程记录一下。
除此之外,我也将它推送到了Gitee Page上,你可以通过以下地址体验(受限于网易云地址解析API,无法播放VIP音乐,有点遗憾):
https://txb582.gitee.io/%E7%BD%91%E6%98%93%E4%BA%91%E9%9F%B3%E4%B9%90%E5%8F%AF%E8%A7%86%E5%8C%96/index.html
思路
大体思路是:
1、可视化工具获取用户输入的网易云分享链接。
2、调用解析API将分享地址解析为音乐真实地址实现播放。
3、调用vudio.js将音乐可视化。
解析API和可视化库都准备好了,主要的开发工作就只有可视化工具页面的开发
在该工具中,用到了以下东西:
- axios网络请求库
- 网易云地址解析API
- 音频可视化库vudio.js
开发
1、建立常用的目录结构
2、页面结构
页面主要分为两大块
1、可视化动画区域
2、悬浮于右侧的可收拉菜单
页面布局代码
<body id="body">
<div id="box-show">
</div>
<div id="box-menu">
</div>
</body>
然后在可视化区域添加
<audio>和<canvas>元素,用于音乐的播放和可视化动画的绘制。
<body id="body">
<div id="box-show">
<audio id="audio" src=""></audio>
<canvas id="canvas">
你的浏览器不支持Canvas
</canvas>
</div>
<div id="box-menu">
</div>
</body>
在菜单中添加
两个<div>用于放置收放按钮和菜单主体
<body id="body">
<div id="box-show">
<audio id="audio" src=""></audio>
<canvas id="canvas">
你的浏览器不支持Canvas
</canvas>
</div>
<div id="box-menu">
<div id="box-menu-pull">
</div>
<div id="box-menu-list">
</div>
</div>
</body>
在按钮区域放置一个<button>作为菜单收放按钮
在菜单主体区域放置
<img>用于显示关照二维码
<span>用于显示提示
两个用div包裹起来的<input>实现搜索栏
还有两个嵌套的<div>用于显示播放的历史记录,用两个<div>嵌套是因为后续为了不让滚动条影响页面美观,使用嵌套的方式通过位移来隐藏滚动条。
<body id="body">
<div id="box-show">
<audio id="audio" src=""></audio>
<canvas id="canvas">
你的浏览器不支持Canvas
</canvas>
</div>
<div id="box-menu">
<div id="box-menu-pull">
<button>></button>
</div>
<div id="box-menu-list">
<img src="./images/微信公众号二维码.jpg">
<span> 扫码关注我们 | MF工作室 </span>
<div>
<input id="search-input" type="text" placeholder="网易云音乐链接">
<input id="search-button" type="button" value="GO">
</div>
<div>
<div id="box-menu-list-historical"></div>
</div>
</div>
</div>
</body>
然后引入必要的文件
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>网易云音乐可视化工具 | MF工作室</title>
<link rel="shortcut icon" href="./images/logo.ico" type="image/x-icon">
<meta http-equiv="Access-Control-Allow-Origin" content="viapi.cn/wyy">
<link rel="stylesheet" href="./css/index.css">
<script src="./js/vudio.js"></script>
<script src="./js/axios.min.js"></script>
<script src="./js/index.js"></script>
</head>
3、页面样式
/* 清空所有边距并禁止用户复制选择页面内容 */
* {
margin: 0px;
border: 0px;
padding: 0px;
user-select: none;
box-sizing: border-box;
}
/* 将html,body 设置与窗口一致, 方便高度使用百分比 */
html, body {
width: 100%;
height: 100%;
}
/* 设置页面背景渐变, 如果你想, 也可以用图片替代 */
body {
background-image: linear-gradient(#f28fb2, #6cbffd);
background-repeat: no-repeat;
background-size: cover;
}
/* 可视化区域设置为充满body元素 */
#box-show {
width: 100%;
height: 100%;
display: flex;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
#box-show>canvas {
max-width: 100%;
}
#box-show>audio {
display: none;
}
/* 将菜单设置为绝对定位, 固定在页面右侧 */
#box-menu {
width: 350px;
height: 100%;
position: fixed;
top: 0px;
right: 0px;
opacity: 0.6;
transition: right 0.5s;
display: flex;
}
/* 收拉按钮区域使用flex布局, 建按钮居中显示 */
#box-menu-pull {
width: 50px;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
/* 设置收拉按钮样式 */
#box-menu-pull>button {
width: 50px;
height: 50px;
font-size: 30px;
border-radius: 50% 0% 0% 50%;
outline: none;
background-color: rgba(142, 194, 243, 0.8);
color: #FFFFFF;
}
/* 使用flex布局, 将菜单主体中的内容垂直居中显示 */
#box-menu-list {
width: 300px;
height: 100%;
padding-top: 20px;
padding-bottom: 20px;
margin-bottom: 20px;
background-color: rgba(142, 194, 243, 0.8);
display: flex;
flex-direction: column;
align-items: center;
overflow-y: auto;
}
/* 公众号二维码样式 */
#box-menu-list>img {
width: 85%;
border: 5px solid rgba(151, 199, 231, 0.8);
border-radius: 10px;
}
/* 提示语样式 */
#box-menu-list>span:nth-of-type(1) {
color: #ffffff;
}
/* 搜索栏样式 */
#box-menu-list>div:nth-of-type(1) {
width: 90%;
height: 40px;
display: flex;
justify-content: center;
align-items: center;
margin-top: 10px;
border-radius: 5px;
}
/* 搜索栏输入框样式 */
#search-input {
height: 100%;
width: calc(100% - 40px);
background-color: rgba(151, 199, 231, 0.8);
outline: none;
padding-left: 5px;
padding-right: 5px;
color: #FFFFFF;
}
#search-input::-webkit-input-placeholder { color: #FFFFFF; }
#search-input::-moz-placeholder { color: #FFFFFF; }
#search-input:-moz-placeholder { color: #FFFFFF; }
#search-input:-ms-input-placeholder { color: #FFFFFF; }
/* 搜索栏提交按钮样式 */
#search-button {
width: 40px;
height: 100%;
background-color: rgba(151, 199, 231, 0.8);
outline: none;
border-left: 1px solid #c8d5dd;
color: #FFFF;
}
/* 历史播放记录显示区域样式 */
#box-menu-list>div:nth-of-type(2) {
width: 100%;
height: 55%;
overflow: hidden;
}
#box-menu-list-historical{
width: 100%;
height: 100%;
overflow: auto;
/* 相对于正常位置右移15像素, 这样就可以使用上层元素遮盖掉滚动条了 */
position: relative;
right: -15px;
}
#box-menu-list-historical>button {
display: block;
padding: 10px;
width: 95%;
border-radius: 5px;
margin-top: 10px;
color: #FFFFFF;
background-color: rgba(151, 199, 231, 0.8);
position: relative;
right: 15px;
outline: none;
}
4、页面逻辑
window.onload = function () {
var menu = document.getElementById("box-menu");
var menu_pull_button = document.querySelector("#box-menu-pull > button");
var box_menu_list_historical = document.getElementById("box-menu-list-historical");
var audio_object = document.getElementById("audio");
// 允许跨域读取音频, 如果不设置, 在调用API时浏览器会因为同源策略阻止音乐拉取
audio_object.crossOrigin = "anonymous";
var canvas_object = document.getElementById("canvas");
var search_button = document.getElementById("search-button");
var search_input = document.getElementById("search-input");
// 侧边菜单点击 拉出/隐藏
var is_pull = true;
menu_pull_button.onclick = function () {
if (is_pull) {
menu.style.right = "-300px";
menu_pull_button.innerText = "<";
is_pull = false;
} else {
menu.style.right = "0px";
menu_pull_button.innerText = ">";
is_pull = true;
}
}
// 提交按钮点击处理
search_button.onclick = function () {
// 从网易云分享链接中提取音乐ID
source_music_url = search_input.value;
source_musid_id = ((source_music_url.split("?")[1]).split("&")[0]).split("=")[1];
// 调用API解析音乐真实地址, API具体使用方法参考: https://api.565.ink/docs#/Lan%E5%B7%A5%E5%85%B7%E7%AE%B1/wangyiyunmc_163mc_get
axios.get(`https://api.565.ink/163mc?id=${source_musid_id}`).then(function (response) {
console.log(response.data);
music_url = response.data["resulturl"];
audio_object.setAttribute("src", music_url);
audio_object.play();
// 向菜单列表插入历史播放记录按钮
let button = document.createElement("button");
button.setAttribute("data-music-url", response.data["resulturl"]);
button.innerText = source_musid_id;
button.onclick = function () {
audio_object.setAttribute("src", this.getAttribute("data-music-url"));
audio_object.play();
}
box_menu_list_historical.appendChild(button)
})
}
// 音频可视化, 具体使用方法参考: https://github.com/alex2wong/vudio
var vudio = new Vudio(audio_object, canvas_object, {
effect: 'circlebar',
accuracy: 128,
width: 400,
height: 400,
waveform: {
maxHeight: 80,
minHeight: 1,
spacing: 1,
shadowBlur: 0,
fadeSide: true,
horizontalAlign: 'center',
verticalAlign: 'middle'
}
});
vudio.dance();
}