推荐:AppleCard 动画彩色模糊背景的 SwiftUI & H5 实现

这原本是GitHub上的一个不错的库,名为Colorful,我觉得不错,顺便用JS复刻了一个,现用于我的个人网站上,效果还算可以

主要是性能方面没法弄什么优化,在某些设备上的性能表现可能比较糟糕

贴个代码:

const colorfulBackground = document.getElementById("colorful-background");


const baseColors = [
    "rgb(248, 167, 236)",
    "rgb(227, 187, 255)",
    "rgb(199, 220, 255)",
]; // 基色
const num = 8; // 生成元素数量
const colorMutationAmount = 80; // 颜色变异水平,提高此值可以获得基色之外的更多颜色
const scaleMean = 0.8;
const scaleStddev = 0.2;
const scaleMin = 0;
const scaleMax = 1.3;
const opacityMean = 0.1;
const opacityStddev = 0.02;
const opacityMin = 0.05;
const opacityMax = 0.15;
const positionMean = 50;
const positionStddev = 30;
const positionMin = 0;
const positionMax = 100;
const blurRadius = 128;


function mutatePrpbability(x) {
    return 0.5 * Math.tanh(0.03 * x - 1.5) + 0.5;
}


function mutateColor(color) {
    // 根据与变异水平正相关的变异概率计算是否变异
    ifMutate = Math.random() > mutatePrpbability();
    // 使用HSL而非RGB,会有更好的效果
    let [r, g, b] = color.slice(4, -1).split(", ").map(Number);
    if (ifMutate) {
        let [h, s, l] = rgbToHsl(r, g, b);
        h = clamp(h + getRandomMutation() / 360, 0, 1);
        s = clamp(s + getRandomMutation(0.1) / 100, 0.6, 1);
        l = clamp(l + getRandomMutation(0.1) / 100, 0.7, 1);
        [r, g, b] = hslToRgb(h, s, l);
    }
    return `radial-gradient(rgb(${r}, ${g}, ${b}) 90%, rgba(0,0,0,0)) `;
}

function getRandomMutation(rate=1) {
    return (Math.random() - 0.5) * colorMutationAmount * rate;
}

function createColorfulCircle() {
    const circle = document.createElement("div");
    circle.classList.add("colorful-circle");
    circle.style.backgroundImage = mutateColor( baseColors[Math.floor(Math.random() * baseColors.length)] );
    circle.style.transform = `translate3d(
                calc(-50% + ${getNumberInNormalDistribution(positionMean, positionStddev, positionMin, positionMax)}vw), 
                calc(-50% + ${getNumberInNormalDistribution(positionMean, positionStddev, positionMin, positionMax)}vh),
                0
            )`;
    circle.style.scale = `${getNumberInNormalDistribution(scaleMean, scaleStddev, scaleMin, scaleMax)}`;
    circle.style.opacity = `${getNumberInNormalDistribution(opacityMean, opacityStddev, opacityMin, opacityMax)}`;
    circle.style.filter = `blur(${blurRadius}px)`;
    return circle;
}

function updateColorfulBackground() {
    circle_list = colorfulBackground.children;
    for (let i = 0; i < num; i++) {
        let circle = circle_list[i];
        circle.style.transform = `translate3d(
                    calc(-50% + ${getNumberInNormalDistribution(positionMean, positionStddev, positionMin, positionMax)}vw), 
                    calc(-50% + ${getNumberInNormalDistribution(positionMean, positionStddev, positionMin, positionMax)}vh),
                    0
                )`;
        circle.style.scale = `${getNumberInNormalDistribution(scaleMean, scaleStddev, scaleMin, scaleMax)}`;
        circle.style.opacity = `${getNumberInNormalDistribution(opacityMean, opacityStddev, opacityMin, opacityMax)}`;
    }
}

function initColorfulBackground() {
    for (let i = 0; i < num; i++) {
        const circle = createColorfulCircle();
        colorfulBackground.appendChild(circle);
    }
}

function clamp(number, min, max) {
    return Math.min(Math.max(number, min), max);
}

function getNumberInNormalDistribution(mean, std_dev, min, max) {
    return clamp(
        mean + randomNormalDistribution() * std_dev,
        min,
        max
    );
}

// 网上抄的,别问我,看不懂
function randomNormalDistribution() {
    var u = 0.0,
        v = 0.0,
        w = 0.0,
        c = 0.0;
    do {
        //获得两个(-1,1)的独立随机变量
        u = Math.random() * 2 - 1.0;
        v = Math.random() * 2 - 1.0;
        w = u * u + v * v;
    } while (w == 0.0 || w >= 1.0);
    //这里就是 Box-Muller转换
    c = Math.sqrt((-2 * Math.log(w)) / w);
    //返回2个标准正态分布的随机数,封装进一个数组返回
    //当然,因为这个函数运行较快,也可以扔掉一个
    //return [u*c,v*c];
    return u * c;
}

function rgbToHsl(r, g, b) {
    r /= 255;
    g /= 255;
    b /= 255;

    const max = Math.max(r, g, b);
    const min = Math.min(r, g, b);
    let h, s, l = (max + min) / 2;

    if (max === min) {
        h = s = 0; // achromatic
    } else {
        const d = max - min;
        s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
        switch (max) {
            case r:
                h = (g - b) / d + (g < b ? 6 : 0);
                break;
            case g:
                h = (b - r) / d + 2;
                break;
            case b:
                h = (r - g) / d + 4;
                break;
        }
        h /= 6;
    }

    return [h, s, l];
}

function hslToRgb(h, s, l) {
    function hue2rgb(p, q, t) {
        if (t < 0) t += 1;
        if (t > 1) t -= 1;
        if (t < 1/6) return p + (q - p) * 6 * t;
        if (t < 1/2) return q;
        if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
        return p;
    }

    let r, g, b;

    if (s === 0) {
        r = g = b = l; // achromatic
    } else {
        const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
        const p = 2 * l - q;
        r = hue2rgb(p, q, h + 1/3);
        g = hue2rgb(p, q, h);
        b = hue2rgb(p, q, h - 1/3);
    }

    return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
}


initColorfulBackground();
setInterval(updateColorfulBackground, 4000);

CSS:

#colorful-background {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: -5;
}

.colorful-circle {
    position: absolute;
    border-radius: 50%;
    transform-origin: center;
    width: 50vw;
    height: 50vw;
    opacity: 0.1;
    transition: 4000ms;
    transition-timing-function: linear;
}

HTML:

<div id="colorful-background"></div>
35 views
Comments
登录后评论
Sign In