一个简洁美观的导航页面

2024 年 11 月 19 日 星期二(已编辑)
/
25
这篇文章上次修改于 2024 年 11 月 19 日 星期二,可能部分内容已经不适用,如有疑问可询问作者。

一个简洁美观的导航页面

亮色模式

亮色模式
暗色模式

暗色模式
没有做切换按钮,因为我认为自动切换更优雅一些
纯html,图标用api获取

DEMO

https://nav.dsuk.top/nav.html

<!DOCTYPE html>

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="preconnect" href="https://cdn.jsdelivr.net" />
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/lxgw-wenkai-screen-web/style.css" />
    <link rel="icon" type="image/x-icon" href="https://toolb.cn/favicon/https://pan.dsuk.top">
    <title>神奇导航页</title>
    <style>
        :root {
            /* 亮色模式 */
            --bg-color-light: #ffffff;
            --text-color-light: #2196f3;
            --card-bg-light: #ffffff;
            --card-shadow-light: 0 4px 6px rgba(0, 0, 0, 0.1);
            --link-color-light: rgb(33 150 243 / 86%);
            ;

            /* 暗色模式 */
            --bg-color-dark: #121212;
            --text-color-light: #2196f3;
            --card-bg-dark: #1e1e1e;
            --card-shadow-dark: 0 4px 6px rgba(0, 0, 0, 0.2);
            --link-color-dark: rgb(33 150 243 / 86%);
            ;

            color-scheme: light dark;
        }

        * {
            margin: 0;
            padding: 0;
            transition: background-color 0.3s, color 0.3s;
        }

        body {
            font-family: "LXGW WenKai Screen";
            display: flex;
            flex-direction: column;
            align-items: center;
            padding: 2rem;
            color: var(--text-color-light);
        }

        @media (prefers-color-scheme: dark) {
            body {
                background-color: var(--bg-color-dark);
                color: var(--text-color-dark);
            }

            .page-title {
                color: var(--text-color-dark);
                background: linear-gradient(45deg, #4da6ff, #87CEFA);
                -webkit-background-clip: text;
                -webkit-text-fill-color: transparent;
                background-clip: text;
                color: transparent;
            }

            .card {
                /* background-color: var(--card-bg-dark) !important; */
                box-shadow: var(--card-shadow-dark) !important;
                border-color: rgba(255, 255, 255, 0.1) !important;
            }

            .container {
                background-color: rgba(0, 0, 0, 0) !important;
                backdrop-filter: blur(2px);
            }

            .card-link {
                color: var(--text-color-dark) !important;
            }
        }

        .page-title {
            font-size: 2.5rem;
            margin-bottom: 2rem;
            font-weight: 700;
            text-align: center;
            background: linear-gradient(45deg, #2196f3, #4da6ff);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            background-clip: text;
            color: transparent;
            letter-spacing: 2px;
        }

        .container {
            background-color: rgba(0, 0, 0, 0) !important;
            backdrop-filter: blur(2px);
            width: 100%;
            max-width: 1200px;
        }

        .card-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
            grid-gap: 1.5rem;
            opacity: 0;
            animation: fadeIn 0.5s ease-in-out forwards;
        }

        .card {
            /* background-color: var(--card-bg-light); */
            border-radius: 12px;
            box-shadow: var(--card-shadow-light);
            transition: all 0.3s ease;
            overflow: hidden;
            position: relative;
            border: 1px solid rgba(0, 0, 0, 0.05);
            transform-style: preserve-3d;
        }

        .card .status-badge {
            font-size: 0.8rem;
            margin-left: 10px;
            padding: 2px 5px;
            border-radius: 4px;
        }

        .card .status-badge.status-normal {
            background-color: #4caf50;
            color: white;
        }

        .card .status-badge.status-error {
            background-color: #f44336;
            color: white;
        }

        .card .status-badge.status-checking {
            background-color: #ff9800;
            color: white;
        }

        .card-link {
            display: block;
            text-decoration: none;
            color: var(--text-color-light);
            padding: 1rem;
            display: flex;
            flex-direction: column;
            align-items: center;
            text-align: center;
            height: 100%;
            transition: all 0.3s ease;
        }

        .card:hover {
            transform: translateY(-10px) scale(1.02);
            box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);
        }

        .search-wrapper:focus-within {
            transform: translateY(-10px) scale(1);
        }

        .card img {
            width: 48px;
            height: 48px;
            margin-bottom: 1rem;
            object-fit: contain;
            filter: grayscale(20%) brightness(1.1);
            transition: transform 0.3s ease;
        }

        .card:hover img {
            transform: scale(1.1);
        }

        .card h3 {
            font-size: 1.1rem;
            margin-bottom: 0.5rem;
            color: var(--text-color-light);
            font-weight: 500;
            letter-spacing: 0.5px;
        }

        .card-link span {
            color: var(--link-color-light);
            font-size: 0.9rem;
            opacity: 0.8;
            transition: opacity 0.3s;
            font-family: "LXGW WenKai Screen";
            letter-spacing: 0.5px;
        }

        @media (max-width: 767px) {
            .card-grid {
                grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
            }
        }

        @keyframes fadeIn {
            from {
                opacity: 0;
                transform: translateY(20px);
            }

            to {
                opacity: 1;
                transform: translateY(0);
            }
        }

        ::-webkit-scrollbar {
            display: none !important;
        }

        /* 搜索框样式 */
        .search-container {
            display: flex;
            justify-content: center;
            align-items: center;
            margin-bottom: 2rem;
            width: 100%;
            max-width: 600px;
            padding: 0 20px;
        }

        .search-wrapper {
            display: flex;
            width: 100%;
            background-color: rgba(0, 0, 0, 0) !important;
            backdrop-filter: blur(2px);
            border-radius: 14px;
            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
            overflow: hidden;
            transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
            border: 2px solid transparent;
        }

        .search-wrapper:hover {
            box-shadow: 0 6px 24px rgba(0, 0, 0, 0.12);
        }

        .search-wrapper:focus-within {
            border-color: #2196f3;
            box-shadow: 0 6px 24px rgba(33, 150, 243, 0.15);
        }

        .search-input {
            background-color: rgba(0, 0, 0, 0) !important;
            backdrop-filter: blur(2px);
            flex-grow: 1;
            border: none;
            padding: 15px 20px;
            font-size: 16px;
            outline: none;
            transition: background-color 0.3s ease;
            font-family: "LXGW WenKai Screen";
            min-width: 0;
            /* 防止输入框在flex布局中溢出 */
        }

        .search-input::placeholder {
            color: #9e9e9e;
            opacity: 0.8;
        }

        .search-engine-toggle {
            display: flex;
            align-items: center;
            justify-content: center;
            padding: 0 15px;
            cursor: pointer;
            background-color: transparent;
            transition: all 0.3s ease;
            border: none;
            width: 48px;
            flex-shrink: 0;
            /* 防止按钮被压缩 */
        }

        .search-engine-toggle:hover {
            background-color: rgba(33, 150, 243, 0.08);
        }

        .search-engine-toggle svg {
            width: 24px;
            height: 24px;
            transition: transform 0.3s ease;
        }

        .search-engine-toggle:hover svg {
            transform: scale(1.1);
        }

        .search-button {
            background-color: #2196f3;
            color: white;
            border: none;
            padding: 15px 28px;
            cursor: pointer;
            transition: all 0.3s ease;
            font-weight: 500;
            font-size: 15px;
            letter-spacing: 0.3px;
            font-family: "LXGW WenKai Screen";
            flex-shrink: 0;
            /* 防止按钮被压缩 */
        }

        .search-button:hover {
            background-color: #1976d2;
            transform: translateY(-1px);
        }

        /* 深色模式 */
        @media (prefers-color-scheme: dark) {
            .search-wrapper {
                box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
            }

            .search-wrapper:hover {
                box-shadow: 0 6px 24px rgba(0, 0, 0, 0.25);
            }

            .search-wrapper:focus-within {
                border-color: #2196f3;
                box-shadow: 0 6px 24px rgba(33, 150, 243, 0.2);
            }

            .search-input {
                background-color: rgba(0, 0, 0, 0) !important;
                backdrop-filter: blur(2px);
                background-color: #2c2c2c;
                color: #ffffff;
            }

            .search-input::placeholder {
                color: #888;
            }

            .search-engine-toggle {
                background-color: transparent;
            }

            .search-engine-toggle:hover {
                background-color: rgba(255, 255, 255, 0.05);
            }

            .search-button {
                background-color: #2196f3;
            }

            .search-button:hover {
                background-color: #1976d2;
            }
        }

        /* 移动端适配 */
        @media screen and (max-width: 768px) {
            .search-container {
                padding: 0 12px;
                margin-bottom: 1.5rem;
            }

            .search-wrapper {
                border-radius: 10px;
            }

            .search-input {
                padding: 12px 15px;
                font-size: 14px;
            }

            .search-engine-toggle {
                padding: 0 12px;
                width: 40px;
            }

            .search-engine-toggle svg {
                width: 20px;
                height: 20px;
            }

            .search-button {
                padding: 12px 20px;
                font-size: 14px;
            }
        }

        /* 超小屏幕适配 */
        @media screen and (max-width: 480px) {
            .search-container {
                padding: 0 8px;
                margin-bottom: 1rem;
            }

            .search-input {
                padding: 10px 12px;
                font-size: 14px;
            }

            .search-engine-toggle {
                padding: 0 10px;
                width: 36px;
            }

            .search-button {
                padding: 10px 16px;
                min-width: 70px;
                /* 确保按钮有最小宽度 */
            }
        }
    </style>
</head>


<body>
    <h1 class="page-title">神奇导航页</h1>
    <div class="search-container">
        <div class="search-wrapper">
            <input type="text" class="search-input" placeholder="搜索...">
            <div class="search-engine-toggle" id="engineToggle">
                <span>必应</span>
            </div>
            <button class="search-button" id="searchButton">搜索</button>
        </div>
    </div>

    <div class="container">
        <div class="card-grid" id="card-grid"></div>
    </div>

    <script>
        // SVG 图标定义
        const googleIcon = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">    <path fill="#4285F4" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"/>    <path fill="#34A853" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"/>    <path fill="#FBBC05" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"/>    <path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"/></svg>`;
        const bingIcon = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">    <path fill="#008373" d="M4.76 1L9.4 3.45v12.39l6.62-3.81-3.07-1.44-1.88-3.93L16.9 8.07l1.88 3.93 1.88 3.93L9.4 22 4.76 19.55V1z"/></svg>`;

        // 获取页面元素
        const searchInput = document.querySelector('.search-input');
        const engineToggle = document.getElementById('engineToggle');
        const searchButton = document.getElementById('searchButton');

        // 设置初始搜索引擎为 Bing
        let currentEngine = 'bing';

        // 设置按钮样式使图标居中
        engineToggle.style.display = 'flex';
        engineToggle.style.alignItems = 'center';
        engineToggle.style.justifyContent = 'center';

        // 设置初始图标
        engineToggle.querySelector('span').innerHTML = bingIcon;

        // 执行搜索功能
        const performSearch = () => {
            const query = searchInput.value.trim();
            if (!query) return;
            // 搜索链接
            const searchUrls = {
                google: `https://www.google.com/search?q=${encodeURIComponent(query)}`,
                bing: `https://www.bing.com/search?q=${encodeURIComponent(query)}`
            };

            // 使用 window.open 在新标签页打开
            window.open(searchUrls[currentEngine], '_blank');
        };

        // 事件监听器
        window.addEventListener('load', () => {
            searchInput.focus();
        });
        // 切换搜索引擎
        engineToggle.addEventListener('click', () => {
            if (currentEngine === 'google') {
                currentEngine = 'bing';
                engineToggle.querySelector('span').innerHTML = bingIcon;
            } else {
                currentEngine = 'google';
                engineToggle.querySelector('span').innerHTML = googleIcon;
            }
        });

        searchButton.addEventListener('click', performSearch);

        searchInput.addEventListener('keyup', (e) => {
            if (e.key === 'Enter') {
                performSearch();
            }
        });

        // 导航链接数据
        const navigateLinks = [{
            title: '哪吒面板',
            url: 'https://x.dsuk.top',
            icon: `https://toolb.cn/favicon/https://x.dsuk.top`
        },
        {
            title: 'HomeLab',
            url: 'https://dsuk.me',
            icon: `https://toolb.cn/favicon/https://dsuk.me`
        },
        {
            title: '博客',
            url: 'https://dsuk.top',
            icon: `https://toolb.cn/favicon/https://dsuk.top`
        },
        {
            title: '网盘',
            url: 'https://pan.dsuk.top',
            icon: `https://toolb.cn/favicon/https://pan.dsuk.top`
        },
        {
            title: 'Alist',
            url: 'https://salen.eu.org',
            icon: `https://toolb.cn/favicon/https://salen.eu.org`
        },
        {
            title: '2FA',
            url: 'https://auth.dsuk.top',
            icon: `https://docs.2fauth.app/static/2fauth_dark.png`
        },
        {
            title: '音乐',
            url: 'https://music.dsuk.top',
            icon: `https://toolb.cn/favicon/https://music.dsuk.top`
        },
        {
            title: '导航',
            url: 'https://nav.dsuk.top',
            icon: `https://toolb.cn/favicon/https://nav.dsuk.top`
        },
        {
            title: 'linux.do',
            url: 'https://linux.do',
            icon: `https://toolb.cn/favicon/https://linux.do`
        },
        {
            title: '青龙面板',
            url: 'https://qlbj.dsuk.top',
            icon: `https://toolb.cn/favicon/https://qlbj.dsuk.top`
        }
        ];
        // 初始化卡片
        function createCard(link) {
            const card = document.createElement('div');
            card.className = 'card';
            card.innerHTML = `
                                <a href="${link.url}" target="_blank" class="card-link">
                                    <img src="${link.icon}" alt="${link.title}">
                                    <h3>${link.title}<span class="status-badge status-checking">检测中</span></h3>
                                    <span>${link.url}</span>
                                </a>
                            `;
            return card;
        }
        // 检查URL状态
        function checkUrl(url) {
            const startTime = performance.now();
            return new Promise((resolve) => {
                fetch(url, {
                    mode: 'no-cors',
                    method: 'GET',
                    timeout: 5000
                })
                    .then(() => {
                        const endTime = performance.now();
                        const duration = Math.round(endTime - startTime);
                        resolve({
                            isAccessible: true,
                            duration
                        });
                    })
                    .catch(() => resolve({
                        isAccessible: false,
                        duration: null
                    }));
            });
        }
        // 更新URL状态
        function updateUrlStatus(card, isAccessible, duration) {
            const statusBadge = card.querySelector('.status-badge');
            if (isAccessible) {
                statusBadge.textContent = `${duration} ms`;
                statusBadge.className = 'status-badge status-normal';
            } else {
                statusBadge.textContent = '失联';
                statusBadge.className = 'status-badge status-error';
            }
        }

        async function initializeCards() {
            const cardGrid = document.getElementById('card-grid');
            cardGrid.innerHTML = ''; // 清空现有卡片

            // 创建并添加所有卡片
            const cardPromises = navigateLinks.map(async (link) => {
                const card = createCard(link);
                cardGrid.appendChild(card);

                // 检查URL状态
                const result = await checkUrl(link.url);
                updateUrlStatus(card, result.isAccessible, result.duration);
            });

            // 等待所有卡片和状态检查完成
            await Promise.all(cardPromises);

            // 显示网格
            cardGrid.style.opacity = '1';
        }

        // 初始化和定期检查
        initializeCards();
        setInterval(initializeCards, 5 * 60 * 1000);

        // 全局配置对象
        const CONFIG = {
            loadDelay: 500,
            particles: {
                count: 230,
                opacity: 0.55,
                zIndex: -1,
                size: 1,
                speed: 0.5,
                connectionDistance: 10000,
                attractionForce: 0.07,
                attractionDistance: 100,
                collisionDistance: 10,
                minMouseDistance: 20,
                randomness: 0.3,
            },
            mouse: {
                attractionDelay: 1500,
                maxDistance: 40000,
            },
            colors: {
                r: null,
                g: null,
                b: null,
            }
        };

        // 粒子系统类
        class ParticleSystem {
            constructor(options = {}) {
                this.canvas = document.createElement("canvas");
                this.ctx = this.canvas.getContext("2d");
                this.particles = [];

                this.mouse = {
                    x: null,
                    y: null,
                    max: CONFIG.mouse.maxDistance,
                };

                this.options = {
                    ...CONFIG.particles,
                    ...options,
                };
                this.mouseStationaryTime = 0;

                this.lastMousePosition = {
                    x: null,
                    y: null,
                };
                this.isAttracting = false;
                this.initCanvas();
                this.calculateParticleCount();
                this.initParticles();
                this.bindEvents();
                this.animate();
            }

            // 获取随机颜色
            getRandomColor() {
                const getColorComponent = (range) => {
                    if (Array.isArray(range)) {
                        return (
                            Math.floor(Math.random() * (range[1] - range[0] + 1)) + range[0]
                        );
                    }

                    return Math.floor(Math.random() * 256);
                };

                return {
                    r: getColorComponent(CONFIG.colors.r),
                    g: getColorComponent(CONFIG.colors.g),
                    b: getColorComponent(CONFIG.colors.b),
                };
            }

            // 初始化画布
            initCanvas() {
                this.canvas.id = "particleCanvas";
                this.canvas.style.cssText = `
                            position: fixed;
                            top: 0;
                            left: 0;
                            z-index:${this.options.zIndex};
                            opacity:${this.options.opacity};
                            `;
                document.body.appendChild(this.canvas);
                this.resize();
            }

            // 计算粒子数量
            calculateParticleCount() {
                const screenArea = window.innerWidth * window.innerHeight;
                const baseDensity = this.options.count / (1920 * 1080);
                this.options.count = Math.floor(screenArea * baseDensity);
                this.options.count = Math.max(20, Math.min(this.options.count, 200));
            }

            // 调整画布大小
            resize() {
                this.canvas.width = window.innerWidth;
                this.canvas.height = window.innerHeight;
                this.calculateParticleCount();
                this.initParticles();
            }

            // 初始化粒子
            initParticles() {
                this.particles = [];

                for (let i = 0; i < this.options.count; i++) {
                    this.particles.push({
                        x: Math.random() * this.canvas.width,
                        y: Math.random() * this.canvas.height,
                        xa: (2 * Math.random() - 1) * this.options.speed,
                        ya: (2 * Math.random() - 1) * this.options.speed,
                        max: this.options.connectionDistance,
                        color: this.getRandomColor(),
                    });
                }
            }

            // 绑定事件
            bindEvents() {
                window.addEventListener("resize", () => this.resize());

                window.addEventListener("mousemove", (e) => {
                    if (this.mouse.x !== e.clientX || this.mouse.y !== e.clientY) {
                        this.mouse.x = e.clientX;
                        this.mouse.y = e.clientY;

                        this.lastMousePosition = {
                            x: e.clientX,
                            y: e.clientY,
                        };
                        this.mouseStationaryTime = 0;
                        this.isAttracting = true;
                    }
                });

                window.addEventListener("mouseout", () => {
                    this.mouse.x = null;
                    this.mouse.y = null;
                    this.isAttracting = false;
                });
            }

            // 混合颜色
            mixColors(color1, color2, weight) {
                return {
                    r: Math.floor(color1.r * weight + color2.r * (1 - weight)),
                    g: Math.floor(color1.g * weight + color2.g * (1 - weight)),
                    b: Math.floor(color1.b * weight + color2.b * (1 - weight)),
                };
            }

            // 绘制粒子
            drawParticles() {
                this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
                const points = [...this.particles, this.mouse];

                // 检查鼠标是否静止
                if (this.mouse.x !== null && this.mouse.y !== null) {
                    if (
                        this.mouse.x === this.lastMousePosition.x &&
                        this.mouse.y === this.lastMousePosition.y
                    ) {
                        this.mouseStationaryTime += 16;

                        if (this.mouseStationaryTime >= CONFIG.mouse.attractionDelay) {
                            this.isAttracting = false;
                        }
                    } else {
                        this.mouseStationaryTime = 0;

                        this.lastMousePosition = {
                            x: this.mouse.x,
                            y: this.mouse.y,
                        };
                        this.isAttracting = true;
                    }
                }

                // 更新和绘制每个粒子
                points.forEach((point, i) => {
                    if (point === this.mouse) return;
                    let newX = point.x + point.xa;
                    let newY = point.y + point.ya;
                    // 碰撞检测
                    let collision = false;

                    for (let j = 0; j < points.length; j++) {
                        if (i !== j) {
                            const otherPoint = points[j];
                            const dx = newX - otherPoint.x;
                            const dy = newY - otherPoint.y;
                            const distance = Math.sqrt(dx * dx + dy * dy);

                            if (distance < this.options.collisionDistance) {
                                collision = true;
                                break;
                            }
                        }
                    }

                    // 处理碰撞或移动
                    if (collision) {
                        point.xa = -point.xa;
                        point.ya = -point.ya;
                    } else {
                        point.x = newX;
                        point.y = newY;
                    }

                    // 边界检查
                    point.xa *= point.x > this.canvas.width || point.x < 0 ? -1 : 1;
                    point.ya *= point.y > this.canvas.height || point.y < 0 ? -1 : 1;

                    // 鼠标吸引效果
                    if (
                        this.isAttracting &&
                        this.mouse.x !== null &&
                        this.mouse.y !== null
                    ) {
                        const dx = this.mouse.x - point.x;
                        const dy = this.mouse.y - point.y;
                        const dist = Math.sqrt(dx * dx + dy * dy);

                        if (
                            dist < this.options.attractionDistance &&
                            dist > this.options.minMouseDistance
                        ) {
                            const targetDist = Math.max(
                                this.options.minMouseDistance,
                                dist * (1 - this.options.attractionForce)
                            );
                            const targetX = this.mouse.x - (dx / dist) * targetDist;
                            const targetY = this.mouse.y - (dy / dist) * targetDist;
                            const randomAngle = Math.random() * Math.PI * 2;
                            const randomDist =
                                Math.random() *
                                this.options.randomness *
                                this.options.minMouseDistance;
                            const randomX = Math.cos(randomAngle) * randomDist;
                            const randomY = Math.sin(randomAngle) * randomDist;
                            point.x +=
                                (targetX - point.x + randomX) * this.options.attractionForce;
                            point.y +=
                                (targetY - point.y + randomY) * this.options.attractionForce;
                        }
                    }

                    // 绘制粒子
                    this.ctx.fillStyle = `rgb(${point.color.r}, ${point.color.g}, ${point.color.b})`;
                    this.ctx.fillRect(
                        point.x - this.options.size / 2,
                        point.y - this.options.size / 2,
                        this.options.size,
                        this.options.size
                    );

                    // 绘制连接线
                    points.forEach((p2, j) => {
                        if (i === j) return;
                        const dx = point.x - p2.x;
                        const dy = point.y - p2.y;
                        const dist = dx * dx + dy * dy;

                        if (dist < point.max) {
                            const ratio = (point.max - dist) / point.max;
                            const mixedColor = this.mixColors(
                                point.color,
                                p2.color || point.color,
                                ratio
                            );
                            this.ctx.beginPath();
                            this.ctx.lineWidth = ratio / 2;
                            this.ctx.strokeStyle = `rgba(${mixedColor.r}, ${mixedColor.g}, ${mixedColor.b}, ${ratio + 0.2})`;
                            this.ctx.moveTo(point.x, point.y);
                            this.ctx.lineTo(p2.x, p2.y);
                            this.ctx.stroke();
                        }
                    });
                });
            }

            // 动画循环
            animate() {
                this.drawParticles();
                requestAnimationFrame(() => this.animate());
            }
        }

        // 页面加载完成后初始化
        window.addEventListener("load", () => {
            setTimeout(() => {
                const particleSystem = new ParticleSystem();
                particleSystem.canvas.classList.add("loaded");
            }, CONFIG.loadDelay);
        });
    </script>
</body>

</html>

使用社交账号登录

  • Loading...
  • Loading...
  • Loading...
  • Loading...
  • Loading...