Pix 添加 音频可视化 + 歌词 🎵
1. Eg
2. JavaScript Source Code
继上一篇文章,替换 js 代码即可
<script type="text/javascript">
const getColor = () => {
if (document.body.classList.value.indexOf('dark') > -1) return "#8c92b3"
else return "#5f936e"
}
const 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();
// 绘制顶部圆
ctx.save()
ctx.translate(x, y - h)
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();
// 绘制底部圆
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();
}
const musicInit = async () => {
let audioCanvasElement = document.createElement('canvas'), canvasW = 200;
document.body.append(audioCanvasElement)
audioCanvasElement.id = 'audio_canvas';
audioCanvasElement.style.height = '150px';
audioCanvasElement.style.width = canvasW + 'px';
audioCanvasElement.style.position = 'fixed';
audioCanvasElement.style.bottom = '0';
audioCanvasElement.style.zIndex = '999';
let sidebarRight = document.querySelector('.sidebar_right');
let beginX = sidebarRight.getBoundingClientRect().x;
beginX = beginX + ((sidebarRight.clientWidth + canvasW) / 2) - canvasW;
audioCanvasElement.style.left = beginX + 'px';
audioCanvasElement.width = canvasW;
audioCanvasElement.height = '150';
let audioCanvasCtx = audioCanvasElement.getContext('2d');
audioCanvasCtx.save();
let beginPointY = audioCanvasElement.height, beginPointX = 0;
let media = null, audioCtx = null, analyser = null, sourceElement = null;
generateLrcElement()
const loadMediaEle = () => {
media = document.querySelector('#pix_player');
if (media != null) {
media.ontimeupdate = async (e) => {
await lyricsLoad(media)
}
media.onplay = async () => {
// get lyrics
lrcOriginal = await $.get('https://bohecat.com/musicapi/?type=lrc&id=' + media.getAttribute('src').split('id=')[1] + '&source=netease');
if (!analyser) {
audioCtx = new window.AudioContext();
analyser = audioCtx.createAnalyser();
analyser.fftSize = 512;
analyser.connect(audioCtx.destination);
sourceElement = audioCtx.createMediaElementSource(media);
sourceElement.connect(analyser);
}
let bufferLength = analyser.frequencyBinCount;
let dataArray = new Uint8Array(bufferLength);
(function task() {
window.requestAnimationFrame(task);
analyser.getByteFrequencyData(dataArray);
audioCanvasCtx.clearRect(0, 0, audioCanvasElement.width, audioCanvasElement.height);
let n = 30, aw = canvasW / n, w = aw - 2, easing = .8, lastH = 0,
add = Math.floor(dataArray.length / n), index = 0;
for (let i = 0; i < n; i++) {
let barHeight = dataArray[index];
let h = (barHeight / 3 - lastH) * easing
h += lastH
drawAudioLine(audioCanvasCtx, beginPointX + (i * aw) + (aw / 2 - w / 2), beginPointY, 4, h, getColor());
index += Math.floor(add / 2);
}
})()
}
} else {
setTimeout(() => {
loadMediaEle()
}, 50)
}
}
loadMediaEle()
}
/* music lyrics */
let lrcDom, lastIndex = -1, lrcArr, lrcContainerW = 200, lrcOriginal
const generateLrcElement = () => {
let lrcContainer = document.createElement('div')
lrcContainer.id = 'music-lyrics'
lrcContainer.style.position = 'fixed'
let audioCanvas = document.querySelector('#audio_canvas')
// lrcContainer.style.bottom = audioCanvas.clientHeight + 'px'
lrcContainer.style.bottom = '110px'
lrcContainer.style.left = audioCanvas.getBoundingClientRect().x + 'px'
lrcContainer.style.width = lrcContainerW + 'px'
lrcContainer.style.height = 100 + 'px'
lrcContainer.style.zIndex = '999'
document.body.append(lrcContainer)
}
const lyricsLoad = async (media) => {
const handle = {
lyricsDomHandle: (data) => {
if (lrcDom.children.length >= 1) {
lrcDom.removeChild(lrcDom.childNodes[0])
}
handle.lyricsItemAdd(data.v, data.i)
},
lyricsItemAdd: (lrc, index) => {
const genChild = (text) => {
let childEl = document.createElement('div')
childEl.innerHTML = text
return childEl
}
let el = document.createElement('div')
el.id = 'lrc_id_' + index
el.classList.add('lrc-in')
lrc.forEach(v => {
el.appendChild(genChild(v || '...'))
})
lrcDom.appendChild(el)
},
lyricsByTime: (time) => {
let arr = [], obj = {}, res = []
for (let j = 0; j < lrcArr.length; j++) {
if (lrcArr[j][0] <= time) {
obj.i = j
arr.push(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
}
}
lrcDom = document.querySelector('#music-lyrics')
if (lrcOriginal) {
lrcArr = lyricsOriginalHandle(lrcOriginal)
let data = handle.lyricsByTime(media.currentTime)
if (lastIndex != data.i) {
handle.lyricsDomHandle(data)
lastIndex = data.i
}
}
}
const 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)
}
}
// 处理前奏过长且歌词源数据的开始时间不为0
if (lrcArr[0][0] > 0) {
lrcArr.unshift([0, ''])
}
return lrcArr
}
window.addEventListener("DOMContentLoaded", () => {
musicInit()
})
</script>
空空如也!