Pix 添加 音频可视化 🌈
1️⃣ Eg
![](https://takina.ink/wp-content/uploads/2023/01/8JNOYPI92R4D7XWZ4U5.png)
![](https://takina.ink/wp-content/uploads/2023/01/PHUP858ZCL323KRN7LEY.png)
2️⃣ Pix 添加音乐配置
![](https://takina.ink/wp-content/uploads/2023/01/PIX主题设置-‹-Another-1024x724.png)
API:https://bohecat.com (感谢大佬提供的api 🤗
歌单列表id可以进入网易云web端歌单页获取
![](https://takina.ink/wp-content/uploads/2023/01/QQ截图2023011220423.png)
3️⃣ 编辑 - pixplayer.js
给 audio 标签添加 crossorigin 属性
![](https://takina.ink/wp-content/uploads/2023/01/image-2.png)
/*
* pix主题音乐播放器 - before
*/
var rem=[];
rem.audio = $('<audio id="pix_player"></audio>');
var audiobox = $('<audio id="pix_player"></audio>');
var au = $('#pix_player');
/*
* pix主题音乐播放器 - after
*/
var rem=[];
rem.audio = $('<audio id="pix_player" crossorigin="anonymous"></audio>');
var audiobox = $('<audio id="pix_player" crossorigin="anonymous"></audio>');
var au = $('#pix_player');
或许你想说
document.querySelector('#pix_player').setAttribute('crossorigin', 'anonymous')
但是很遗憾,这样不会生效 💦
4️⃣ JavaScript Source Code ( 包含歌词 )
// for position
const canvasW = 200, canvasH = 150
let $root, beginPointY = canvasH, beginPointX = 0, canvasX, canvasY
// for audio line
let $media, audioCtx, analyser, sourceElement, canvasCtx
let $canvas,
lineNum = 30,
lineFullWidth = canvasW / lineNum,
lineWidth = lineFullWidth - 3.6,
easing = .8,
lineInstances = []
// for lrc
let $lrc, lrcOriginal, lastIndex = -1
// target
const mediaEl = '#pix_player', containerEl = '.sidebar_right'
$(document).ready(function () {
$root = $(containerEl)
canvasX = $root.offset().left + ($root.width() + canvasW) / 2 - canvasW
canvasY = $root.offset().top + $root.height()
musicInit()
})
function musicInit() {
$canvas = $('<canvas>')
$canvas.id = 'audio_canvas'
$canvas.css({
height: `${canvasH}px`,
width: `${canvasW}px`,
position: 'fixed',
zIndex: '999',
left: `${canvasX}px`,
bottom: 0,
})
$canvas[0].width = canvasW
$canvas[0].height = canvasH
canvasCtx = $canvas[0].getContext('2d')
document.body.appendChild($canvas[0])
lrcInit()
mediaInit()
}
function mediaInit() {
$media = $(mediaEl)
if ($media.length === 0) setTimeout(mediaInit, 50)
else {
$media.on('timeupdate', lrcAni)
$media.on('play', function () {
lrcLoad()
if (!analyser) {
audioCtx = new window.AudioContext()
analyser = audioCtx.createAnalyser()
analyser.fftSize = 512
analyser.connect(audioCtx.destination)
sourceElement = audioCtx.createMediaElementSource($media[0])
sourceElement.connect(analyser)
}
const bufferLength = analyser.frequencyBinCount
const dataArray = new Uint8Array(bufferLength);
(function audioTask() {
window.requestAnimationFrame(audioTask)
analyser.getByteFrequencyData(dataArray)
canvasCtx.clearRect(0, 0, canvasW, canvasH)
let addOffset = Math.floor(dataArray.length / lineNum), indexOffset = 0
for (let i = 0; i < lineNum; i++) {
let instance = lineInstances[i]
if (!instance) {
instance = {
h: (dataArray[indexOffset] / 3) * easing,
y: canvasH,
}
lineInstances[i] = instance
}
lineHandle(dataArray[indexOffset], instance.h, instance, i)
indexOffset += Math.floor(addOffset / 2)
}
})()
})
}
}
function lineHandle(barHeight, lastH, instance, i) {
instance.h = ((barHeight / 3 - lastH) * easing + lastH) || 0
instance.color = getColor()
drawAudioLine(canvasCtx, beginPointX + (i * lineFullWidth), beginPointY, lineWidth, instance.h, instance.color)
}
function getColor() {
if (document.body.classList.value.indexOf('dark') > -1) return "#8c92b3"
else return "#5f936e"
}
function drawAudioLine(ctx, x, y, w, h, color) {
// 绘制方形
ctx.save()
ctx.translate(x, y)
ctx.scale(1, -1)
ctx.fillStyle = color;
ctx.beginPath();
ctx.fillRect(0, 0, w, h)
ctx.closePath();
ctx.fill();
ctx.restore();
// 绘制顶部圆
drawRound(ctx, x, y - h, w, color)
// 绘制底部圆
drawRound(ctx, x, y, w, color)
}
function drawRound(ctx, x, y, w, color) {
ctx.save()
ctx.translate(x, y)
ctx.scale(1, -1)
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc((w / 2), 0, (w / 2), 0, 360 * Math.PI / 180, false);
ctx.closePath();
ctx.fill();
ctx.restore();
}
function lrcInit() {
$lrc = $('<div>')
$lrc[0].id = 'music-lyrics'
$lrc.css({
position: 'fixed',
bottom: `${canvasH}px`,
left: `${$canvas.offset().left}px`,
width: `${canvasW}px`,
height: '100px',
zIndex: '999',
})
document.body.appendChild($lrc[0])
}
function lrcLoad() {
$.get('https://test.xiamoqwq.com/musicapi?type=lrc&id=' + $media.attr('src').split('id=')[1]).then(res => lrcOriginal = res)
}
const lrc = {
lrcArr: [],
domArr: [],
lyricsByTime: (time) => {
let arr = [], obj = {}, res = []
for (let j = 0; j < lrc.lrcArr.length; j++) {
if (lrc.lrcArr[j][0] <= time) {
obj.i = j
arr.push(lrc.lrcArr[j])
}
}
let last = arr[arr.length - 1] ? arr[arr.length - 1][0] : '-1'
arr.forEach(v => {
if (v[0] === last) {
res.push(v[1])
}
})
obj.v = res
return obj
},
add: (data) => {
if (lrc.domArr.length > 0) {
const first = lrc.domArr.shift()
first.removeClass('lrc-in').addClass('lrc-out')
setTimeout(() => {
first.remove()
}, 500)
}
const div = $('<div>')
div.addClass('lrc-in')
data.v.forEach((item) => {
div.append($('<div>').text(item || '...'))
})
lrc.domArr.push(div)
$lrc.append(div)
}
}
function lrcAni() {
if (!lrcOriginal) return
// if (lrc.lrcArr.length === 0)
lrc.lrcArr = lyricsOriginalHandle(lrcOriginal)
const data = lrc.lyricsByTime($media[0].currentTime)
if (lastIndex !== data.i) {
lrc.add(data)
lastIndex = data.i
}
}
function lyricsOriginalHandle(original) {
let res = original.split('[')
let lrcArr = []
for (let i = 0; i < res.length; i++) {
if (res[i]) {
let data = res[i].split(']')
data[1] = data[1].replace(/^\s+/, '').replace(/\s+$/, '')
let timeArr = data[0].split(':')
let min = Number(timeArr[0]) * 60, sec = Number(timeArr[1])
data[0] = min + sec
lrcArr.push(data)
}
}
if (lrcArr[0][0] > 0) {
lrcArr.unshift([0, ''])
}
return lrcArr
}
通过 Pix扩展设置 加入
打开 Pix主题设置 - 扩展设置 ,并将代码加入至 底部HTML代码 中
![](https://takina.ink/wp-content/uploads/2023/01/QQ截图20230113174429-1024x364.png)
通过编辑 header.php 加入
![](https://takina.ink/wp-content/uploads/2023/01/image-1.png)
<head>
<meta charset="<?php bloginfo('charset'); ?>">
<meta name="viewport"
content="width=device-width,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0,user-scalable=no">
<meta name="keywords" content="<?php getKeywords(); ?>">
<meta name="description" content="<?php getDescription(); ?>">
<title><?php gettitle(); ?></title>
<link rel="profile" href="https://gmpg.org/xfn/11">
<link rel="shortcut icon" href="<?php echo get_op('favicon'); ?>" title="Favicon">
<?php wp_head(); ?>
<!-- add start -->
...
<!-- add end -->
</head>
5️⃣ 其中碰到的问题
MediaElementAudioSource outputs zeroes due to CORS access restrictions for xxx
跨域限制
即 audio 标签加入 crossorigin 属性所解决的问题
刚开始以为是 API 的原因,因为音频文件之类的都被重定向到了网易云音乐官方那边,猜想可能是网易那边做了跨域限制,于是就自己动手敲了一个代理服务,结果修改跨域配置后依然没有解决这个问题 🥲
然而网易云音乐官方有提供将播放器嵌入到其它页面的方案,所以大概是没做跨域限制的
至于使用 js 操作属性无效则是因为这个属性需要在设置 audio - src 之前就已经存在
6️⃣ 通过链接引入
<!--music begin-->
<script src="https://file.qwq.link/blog/music/music.js"></script>
<link rel="stylesheet" href="https://file.qwq.link/blog/music/music.css" />
<!--music end-->
加入至 PIX主题设置 - 扩展设置 - 底部 HTML 即可
⚠️ 需要开启右栏
Fuzzz
赞👏
xiamo
@Fuzzz 嘿嘿 🥳
bohe
大佬,加个歌词放左下角👀
xiamo
@bohe 可以有 👌
bohe
其实都打包进标签,然后加到头部或者HTML代码里就可以了,不用改源文件
bohe
@bohe script标签
bohe
@bohe 扩展设置里的头部或者底部html代码
bohe
@bohe 绑定播放事件的方法确实需要在原文件改。。。。。
xiamo
@bohe 自定义js其实有试过,不过小于号会被转义成 < ; 🥲
然后刚刚试了试放 头部/底部HTML代码 里是可行的,没被转义 🐶
zzzj
牛逼👍
lxhcool
为啥我加了后音乐没声音了
xiamo
@lxhcool 大概JS有报错吧,得像我这样开三栏才行
lxhcool
@xiamo 跨域了怎么办
xiamo
@lxhcool 看第三点,需要给audio标签添加 crossorigin=”anonymous”