用脚本实现简易屏幕保护

wen 实用脚本 2
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">简易屏幕保护</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
            background-color: black;
            font-family: Arial, Helvetica, sans-serif;
        }
        #canvas-container {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            cursor: none; /* 隐藏鼠标指针,更像屏保 */
        }
        #info {
            position: fixed;
            bottom: 20px;
            left: 0;
            width: 100%;
            text-align: center;
            color: rgba(255, 255, 255, 0.2);
            font-size: 14px;
            pointer-events: none;
            user-select: none;
            z-index: 10;
        }
        @media (max-width: 600px) {
            #info {
                font-size: 10px;
                bottom: 10px;
            }
        }
    </style>
</head>
<body>
    <div id="canvas-container">
        <canvas id="screenSaverCanvas"></canvas>
    </div>
    <div id="info">点击或移动鼠标退出 · 简易屏保</div>
    <script>
        (function() {
            // ----- 获取 Canvas 和上下文 -----
            const canvas = document.getElementById('screenSaverCanvas');
            const ctx = canvas.getContext('2d');
            // ----- 尺寸变量 -----
            let width = window.innerWidth;
            let height = window.innerHeight;
            // ----- 粒子系统参数 -----
            const PARTICLE_COUNT = 80;          // 粒子数量
            const CONNECTION_DISTANCE = 150;    // 连线最大距离 (像素)
            const PARTICLE_RADIUS = 2.5;        // 粒子半径
            const SPEED = 1.2;                  // 移动速度因子
            // ----- 颜色配置 (柔和的蓝色/紫色调) -----
            const COLORS = [
                { r: 100, g: 180, b: 255 },   // 淡蓝
                { r: 150, g: 120, b: 255 },   // 淡紫
                { r: 80, g: 220, b: 200 },    // 青绿
                { r: 255, g: 150, b: 200 }    // 淡粉
            ];
            // ----- 粒子数组 -----
            let particles = [];
            // ----- 鼠标 / 触摸交互 (用于退出屏保) -----
            let interactionTimeout = null;
            const EXIT_DELAY = 1000; // 无操作后重新启动 (实际上通过点击/移动立即退出,这里用于重启逻辑)
            // ----- 控制变量 -----
            let isRunning = true;      // 控制动画循环
            let animationFrameId = null;
            // ----- 退出屏保:通过重置状态或跳转? 这里我们设计为点击或移动鼠标时,停止动画并显示一个简单的退出效果,但作为屏保脚本,我们更倾向于“退出屏保模式”回到正常页面。
            // 但因为我们没有实际的后台,我们实现为:当检测到交互,清空画布并显示一条消息,然后可以重新开始。
            // 为了让演示更友好,我们设计为:第一次交互停止动画并显示“已退出”,再次点击重新激活。
            // 但更符合屏保习惯:任何交互都停止屏保,显示空白或提示,直到用户再次点击“激活”。
            // 我们实现一个切换功能:点击/移动鼠标 切换 运行/停止。
            // 但是为了模仿真实屏保,一般移动鼠标或点击会立即退出到桌面,这里我们简化为停止动画并显示文字。
            // 为了不干扰展示,如果停止后3秒无操作,自动重新启动。
            // 我们采用更直接的方式:默认运行,一旦检测到鼠标移动或点击,立即停止动画并显示“屏保已退出”,
            // 然后过2秒后自动重新启动,如果再次交互,再次停止,如此循环。
            // 这样做可以完整展示屏保效果,同时演示退出机制。
            // ----- 初始化粒子 -----
            function initParticles() {
                particles = [];
                for (let i = 0; i < PARTICLE_COUNT; i++) {
                    particles.push({
                        x: Math.random() * width,
                        y: Math.random() * height,
                        vx: (Math.random() - 0.5) * SPEED * 2,
                        vy: (Math.random() - 0.5) * SPEED * 2,
                        color: COLORS[Math.floor(Math.random() * COLORS.length)],
                        radius: PARTICLE_RADIUS * (0.8 + Math.random() * 0.6) // 随机大小
                    });
                }
            }
            // ----- 更新粒子位置 (边界反弹) -----
            function updateParticles() {
                for (let p of particles) {
                    p.x += p.vx;
                    p.y += p.vy;
                    // 边界反弹 (并稍微随机化速度防止周期性)
                    if (p.x < 0) {
                        p.x = 0;
                        p.vx = -p.vx * (0.9 + Math.random() * 0.2);
                    } else if (p.x > width) {
                        p.x = width;
                        p.vx = -p.vx * (0.9 + Math.random() * 0.2);
                    }
                    if (p.y < 0) {
                        p.y = 0;
                        p.vy = -p.vy * (0.9 + Math.random() * 0.2);
                    } else if (p.y > height) {
                        p.y = height;
                        p.vy = -p.vy * (0.9 + Math.random() * 0.2);
                    }
                    // 限制速度范围,避免过快
                    const maxSpeed = SPEED * 2.5;
                    const speedVal = Math.hypot(p.vx, p.vy);
                    if (speedVal > maxSpeed) {
                        p.vx = (p.vx / speedVal) * maxSpeed;
                        p.vy = (p.vy / speedVal) * maxSpeed;
                    }
                }
            }
            // ----- 绘制粒子及连线 -----
            function draw() {
                ctx.clearRect(0, 0, width, height);
                // 1. 绘制连线 (先绘制连线,让粒子在上一层)
                for (let i = 0; i < particles.length; i++) {
                    for (let j = i + 1; j < particles.length; j++) {
                        const p1 = particles[i];
                        const p2 = particles[j];
                        const dx = p1.x - p2.x;
                        const dy = p1.y - p2.y;
                        const dist = Math.sqrt(dx * dx + dy * dy);
                        if (dist < CONNECTION_DISTANCE) {
                            // 根据距离调整透明度
                            const opacity = 1 - (dist / CONNECTION_DISTANCE);
                            // 混合颜色 (取两个粒子的颜色平均)
                            const avgR = (p1.color.r + p2.color.r) / 2;
                            const avgG = (p1.color.g + p2.color.g) / 2;
                            const avgB = (p1.color.b + p2.color.b) / 2;
                            ctx.strokeStyle = `rgba(${avgR | 0}, ${avgG | 0}, ${avgB | 0}, ${opacity * 0.6})`;
                            ctx.lineWidth = 1.2;
                            ctx.beginPath();
                            ctx.moveTo(p1.x, p1.y);
                            ctx.lineTo(p2.x, p2.y);
                            ctx.stroke();
                        }
                    }
                }
                // 2. 绘制粒子
                for (let p of particles) {
                    ctx.beginPath();
                    ctx.arc(p.x, p.y, p.radius, 0, Math.PI * 2);
                    // 粒子颜色 (稍微带点发光效果)
                    ctx.fillStyle = `rgba(${p.color.r}, ${p.color.g}, ${p.color.b}, 0.9)`;
                    ctx.fill();
                    // 增加一点光晕
                    ctx.shadowColor = `rgba(${p.color.r}, ${p.color.g}, ${p.color.b}, 0.5)`;
                    ctx.shadowBlur = 8;
                    ctx.fill();
                    ctx.shadowBlur = 0; // 重置避免影响其他绘制
                }
            }
            // ----- 动画循环 -----
            function animate() {
                if (!isRunning) {
                    // 如果停止,则不更新画面,但可以绘制一个退出提示
                    // 但为了干净,我们会在停止时清空画布并显示文字,所以这里直接返回
                    return;
                }
                updateParticles();
                draw();
                animationFrameId = requestAnimationFrame(animate);
            }
            // ----- 启动动画 -----
            function startAnimation() {
                if (animationFrameId) {
                    cancelAnimationFrame(animationFrameId);
                    animationFrameId = null;
                }
                isRunning = true;
                // 重新初始化粒子以适应新尺寸 (如果尺寸变了)
                initParticles();
                animate();
                // 隐藏任何覆盖提示 (如果有)
                const infoEl = document.getElementById('info');
                infoEl.style.color = 'rgba(255, 255, 255, 0.2)';
                infoEl.textContent = '点击或移动鼠标退出 · 简易屏保';
            }
            // ----- 停止动画 (退出屏保) -----
            function stopAnimation() {
                if (!isRunning) return;
                isRunning = false;
                if (animationFrameId) {
                    cancelAnimationFrame(animationFrameId);
                    animationFrameId = null;
                }
                // 清空画布并显示退出文字
                ctx.clearRect(0, 0, width, height);
                // 绘制一些淡出的粒子痕迹 (可选) 或者直接显示文字
                ctx.fillStyle = 'rgba(0, 0, 0, 1)';
                ctx.fillRect(0, 0, width, height);
                // 显示退出文字
                ctx.textAlign = 'center';
                ctx.textBaseline = 'middle';
                ctx.fillStyle = 'rgba(200, 200, 255, 0.3)';
                ctx.font = '24px Arial, sans-serif';
                ctx.fillText('屏幕保护已退出', width/2, height/2 - 20);
                ctx.fillStyle = 'rgba(200, 200, 255, 0.15)';
                ctx.font = '16px Arial, sans-serif';
                ctx.fillText('点击或移动鼠标重新启动', width/2, height/2 + 30);
                // 更新底部信息
                const infoEl = document.getElementById('info');
                infoEl.style.color = 'rgba(255, 255, 255, 0.4)';
                infoEl.textContent = '点击或移动鼠标重新启动屏保';
            }
            // ----- 切换状态 (交互触发) -----
            function toggleScreenSaver() {
                if (isRunning) {
                    stopAnimation();
                    // 设置一个定时器,如果一段时间无操作自动重启 (但为了演示,我们让用户通过交互重启)
                    // 清除之前的定时器
                    if (interactionTimeout) {
                        clearTimeout(interactionTimeout);
                        interactionTimeout = null;
                    }
                    // 设置5秒后自动重启 (如果仍处于停止状态)
                    interactionTimeout = setTimeout(() => {
                        if (!isRunning) {
                            startAnimation();
                            // 更新底部信息
                            const infoEl = document.getElementById('info');
                            infoEl.style.color = 'rgba(255, 255, 255, 0.2)';
                            infoEl.textContent = '点击或移动鼠标退出 · 简易屏保';
                        }
                    }, 5000);
                } else {
                    // 如果当前停止,则重新启动
                    if (interactionTimeout) {
                        clearTimeout(interactionTimeout);
                        interactionTimeout = null;
                    }
                    startAnimation();
                }
            }
            // ----- 事件监听:鼠标移动、点击、触摸 -----
            function handleInteraction(event) {
                // 阻止默认行为,避免页面滚动等
                event.preventDefault();
                toggleScreenSaver();
            }
            // 鼠标移动 (使用节流,避免过于频繁)
            let moveThrottle = null;
            function handleMouseMove(event) {
                if (moveThrottle) return;
                moveThrottle = setTimeout(() => {
                    moveThrottle = null;
                }, 300); // 300ms 内只触发一次
                handleInteraction(event);
            }
            // 鼠标点击
            function handleMouseClick(event) {
                handleInteraction(event);
            }
            // 触摸事件 (移动端)
            function handleTouch(event) {
                handleInteraction(event);
            }
            // ----- 窗口大小自适应 -----
            function handleResize() {
                width = window.innerWidth;
                height = window.innerHeight;
                canvas.width = width;
                canvas.height = height;
                // 如果正在运行,重新初始化粒子以适应新尺寸
                if (isRunning) {
                    initParticles();
                } else {
                    // 如果停止,重绘停止画面
                    ctx.fillStyle = 'rgba(0, 0, 0, 1)';
                    ctx.fillRect(0, 0, width, height);
                    ctx.textAlign = 'center';
                    ctx.textBaseline = 'middle';
                    ctx.fillStyle = 'rgba(200, 200, 255, 0.3)';
                    ctx.font = '24px Arial, sans-serif';
                    ctx.fillText('屏幕保护已退出', width/2, height/2 - 20);
                    ctx.fillStyle = 'rgba(200, 200, 255, 0.15)';
                    ctx.font = '16px Arial, sans-serif';
                    ctx.fillText('点击或移动鼠标重新启动', width/2, height/2 + 30);
                }
            }
            // ----- 初始化 Canvas 尺寸和粒子 -----
            function setup() {
                canvas.width = width;
                canvas.height = height;
                initParticles();
                // 默认启动
                startAnimation();
                // 注册事件
                document.addEventListener('mousemove', handleMouseMove);
                document.addEventListener('click', handleMouseClick);
                document.addEventListener('touchstart', handleTouch, { passive: false });
                window.addEventListener('resize', handleResize);
            }
            // ----- 启动一切 -----
            setup();
            // ----- 清理 (理论上脚本执行期间不会卸载,但提供良好习惯) -----
            // 如果需要清理,可以导出清理函数,但这里不涉及。
        })();
    </script>
</body>
</html>

屏保机制与交互

您可以通过鼠标或触屏与这个动态粒子屏保进行互动,它的核心逻辑围绕粒子运动与视觉连接展开。

用脚本实现简易屏幕保护

  • 动态粒子网络:屏幕上有数十个带有柔和色彩的粒子随机运动,当粒子之间的距离小于设定值时,它们之间会绘制出半透明的连线,形成不断变化的网状结构。
  • 交互与状态切换:移动鼠标、点击或触摸屏幕会触发屏保的“退出”与“重启”,首次交互会停止动画并显示退出提示;再次交互或等待约5秒,屏保会自动恢复运行。
  • 自适应与视觉:页面会自适应窗口大小,粒子在碰到屏幕边界时会反弹,并随机微调速度,使其运动更自然,整体采用深色背景与低饱和度的彩色粒子,视觉上较为舒适。

优化建议: 您可以根据喜好调整 PARTICLE_COUNT(粒子数量)、SPEED(运动速度)和 CONNECTION_DISTANCE(连线距离)等参数,以改变屏保的视觉效果和密集度。

抱歉,评论功能暂时关闭!