早期写过一个web壁纸,其中令我最不满意的就是可视化频谱的实现,因为看起来特别僵硬 😶‍🌫️

https://steamcommunity.com/sharedfiles/filedetails/?id=2762672832

About

Wallpaper Engine中音频的返回格式其实就是两组数组,一组左声道,另一组是右声道,没记错的话我这边应该只取了其中之一,并且这边的实现是用的Canvas,只记得写了一大把的js代码,光圆角就搜了老半天

然而就目前来说,我对Canvas依然不是很熟悉,令人感叹

另外,导致僵硬的原因是因为高度是直接变化的,缺少了过渡

于是我用 Js + CSS 实现了一波 🤔

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="container">
    <div class="line-container">
        <div class="line" id="line_1"></div>
        <div class="line" id="line_2"></div>
        <div class="line" id="line_3"></div>
        <div class="line" id="line_4"></div>
        <div class="line" id="line_5"></div>
        <div class="line" id="line_6"></div>
        <div class="line" id="line_7"></div>
        <div class="line" id="line_8"></div>
        <div class="line" id="line_9"></div>
        <div class="line" id="line_10"></div>
        <div class="line" id="line_11"></div>
        <div class="line" id="line_12"></div>
        <div class="line" id="line_13"></div>
        <div class="line" id="line_14"></div>
        <div class="line" id="line_15"></div>
    </div>
</div>
</body>
<script>
    window.onload = () => {
        setInterval(() => {
            generate()
        }, 100)
    }

    const generate = () => {
        for (let i = 0; i < 15; i++) {
            let h = random(500, 0)
            let line = document.querySelector('#line_' + (i + 1))
            line.style.height = h + 'px'
        }
    }

    const random = (Max, Min) => {
        let Range = Max - Min;
        let Rand = Math.random();
        return Min + Math.round(Rand * Range);
    }
</script>
<style>
    body {
        margin: unset;
    }

    #container {
        height: 550px;
        display: flex;
        justify-content: center;
        position: relative;
    }

    .line-container {
        display: flex;
        gap: 15px;
        align-items: flex-end;
    }

    .line {
        width: 15px;
        border-radius: 10px;
        background-color: rgba(141, 114, 225, .5);
        box-shadow: 0 0 8px rgba(141, 114, 225, .4);
        transition: all .4s;
    }
</style>
</html>

虽然用这种方式实现起来要简单很多,但是论实现效果的上限那必然还是Canvas,有时间再买本书看看捏 😴

...

于是我买了

to demo ~

这边是一个使用canvas实现的demo

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Canvas Demo</title>
</head>
<body>
<canvas id="AudioCanvas"></canvas>
</body>
<script>
    let audioCanvas
    let audioCanvasCtx
    let lineMaxH = 200
    // 音柱宽度, 右外边距, 音柱数量, 缓动系数
    let lineW = 5, margiRight = 5, num = 20, easing = 0.03
    // 记录上一个高度
    let lastH = []

    window.onload = () => {
        audioCanvas = document.querySelector('#AudioCanvas')
        audioCanvas.width = window.innerWidth
        audioCanvas.height = window.innerHeight
        audioCanvasCtx = audioCanvas.getContext('2d')
        audioCanvasCtx.save();

        // (function handle() {
        //     window.requestAnimationFrame(handle)
        //
        // })();

        setInterval(() => {
            let audioArray = genArray()
            // 总宽
            let allW = (num * (lineW + margiRight)) - margiRight
            // 起始点
            let beginPointY = window.innerHeight - 100
            let x = (window.innerWidth / 2) - (allW / 2)
            audioCanvasCtx.clearRect(0, 0, audioCanvas.width, audioCanvas.height)
            for (let i = 0; i < audioArray.length; ++i) {
                let newH = audioArray[i];
                if (!lastH[i]) lastH[i] = 0
                let v = (newH - lastH[i]) * easing
                lastH[i] += v
                fillLine(audioCanvasCtx, x, beginPointY, lineW, lastH[i], 'white')
                x += (lineW + margiRight)
            }
        }, 10)
    }

    const genArray = () => {
        let arr = []
        for (let i = 0; i < 10; i++) {
            arr.push(random(lineMaxH, 0))
        }
        return arr;
    }

    const random = (Max, Min) => {
        let Range = Max - Min;
        let Rand = Math.random();
        return Min + Math.round(Rand * Range);
    }

    const fillLine = (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();
    }
</script>
<style>
    body {
        background: black;
        margin: unset;
    }

    canvas {
        height: 100vh;
        width: 100vw;
        position: absolute;
        top: 0;
    }
</style>
</html>

并且我重新上传了一版到创意工坊

🎨 to wallpaper


Ex - ploooosion!