Pix 添加 音频可视化 🌈

    1,518

1️⃣ Eg

2️⃣ Pix 添加音乐配置

API:https://bohecat.com (感谢大佬提供的api 🤗

歌单列表id可以进入网易云web端歌单页获取

3️⃣ 编辑 - pixplayer.js

给 audio 标签添加 crossorigin 属性

/*
* 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代码

通过编辑 header.php 加入
<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 即可

⚠️ 需要开启右栏

Comments | 14 条评论
  • Fuzzz

    赞👏

  • bohe

    大佬,加个歌词放左下角👀

  • bohe

    其实都打包进标签,然后加到头部或者HTML代码里就可以了,不用改源文件

    • bohe

      @bohe script标签

    • bohe

      @bohe 扩展设置里的头部或者底部html代码

      • bohe

        @bohe 绑定播放事件的方法确实需要在原文件改。。。。。

        • xiamo

          @bohe 自定义js其实有试过,不过小于号会被转义成 &lt ; 🥲
          然后刚刚试了试放 头部/底部HTML代码 里是可行的,没被转义 🐶

  • zzzj

    牛逼👍

  • lxhcool

    为啥我加了后音乐没声音了

    • xiamo

      @lxhcool 大概JS有报错吧,得像我这样开三栏才行

      • lxhcool

        @xiamo 跨域了怎么办

        • xiamo

          @lxhcool 看第三点,需要给audio标签添加 crossorigin=”anonymous”

消息盒子
# 您需要首次评论以获取消息 #
# 您需要首次评论以获取消息 #

只显示最新10条未读和已读信息