1370 lines
53 KiB
HTML
1370 lines
53 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||
<title>CYBER-OS | 赛博朋克未来都市操作系统</title>
|
||
|
||
<!-- 字体引入 -->
|
||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;500;700;900&family=Roboto+Mono:wght@300;400;500&family=Share+Tech+Mono&display=swap" rel="stylesheet">
|
||
|
||
<!-- 图标库 -->
|
||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||
|
||
<!-- 动画库 -->
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js"></script>
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.min.js"></script>
|
||
|
||
<style>
|
||
/* =========================================
|
||
CSS 变量与基础重置
|
||
========================================= */
|
||
:root {
|
||
--neon-blue: #00f3ff;
|
||
--neon-pink: #ff00ff;
|
||
--neon-purple: #bc13fe;
|
||
--neon-green: #0aff0a;
|
||
--bg-dark: #050510;
|
||
--glass-bg: rgba(10, 15, 30, 0.65);
|
||
--glass-border: rgba(0, 243, 255, 0.3);
|
||
--text-main: #e0e0e0;
|
||
--font-head: 'Orbitron', sans-serif;
|
||
--font-code: 'Roboto Mono', monospace;
|
||
--font-ui: 'Share Tech Mono', monospace;
|
||
--shadow-glow: 0 0 10px rgba(0, 243, 255, 0.5), 0 0 20px rgba(0, 243, 255, 0.3);
|
||
--scanline-color: rgba(0, 0, 0, 0.5);
|
||
}
|
||
|
||
* {
|
||
box-sizing: border-box;
|
||
user-select: none; /* 防止文本被意外选中,提升OS质感 */
|
||
scrollbar-width: thin;
|
||
scrollbar-color: var(--neon-blue) var(--bg-dark);
|
||
}
|
||
|
||
body, html {
|
||
margin: 0;
|
||
padding: 0;
|
||
width: 100vw;
|
||
height: 100vh;
|
||
overflow: hidden;
|
||
background-color: var(--bg-dark);
|
||
font-family: var(--font-ui);
|
||
color: var(--text-main);
|
||
cursor: crosshair; /* 赛博风格光标 */
|
||
}
|
||
|
||
/* 滚动条样式 */
|
||
::-webkit-scrollbar { width: 6px; }
|
||
::-webkit-scrollbar-track { background: var(--bg-dark); }
|
||
::-webkit-scrollbar-thumb { background: var(--neon-blue); border-radius: 3px; }
|
||
|
||
/* 扫描线特效层 */
|
||
.scanlines {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: linear-gradient(
|
||
to bottom,
|
||
rgba(255,255,255,0),
|
||
rgba(255,255,255,0) 50%,
|
||
rgba(0,0,0,0.2) 50%,
|
||
rgba(0,0,0,0.2)
|
||
);
|
||
background-size: 100% 4px;
|
||
pointer-events: none;
|
||
z-index: 9999;
|
||
opacity: 0.3;
|
||
}
|
||
|
||
/* =========================================
|
||
开机动画
|
||
========================================= */
|
||
#boot-screen {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: #000;
|
||
z-index: 10000;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
align-items: center;
|
||
font-family: var(--font-code);
|
||
color: var(--neon-green);
|
||
}
|
||
|
||
.boot-text {
|
||
font-size: 1.2rem;
|
||
margin-bottom: 10px;
|
||
text-shadow: 0 0 5px var(--neon-green);
|
||
}
|
||
|
||
.loader-bar {
|
||
width: 300px;
|
||
height: 4px;
|
||
background: #111;
|
||
margin-top: 20px;
|
||
position: relative;
|
||
}
|
||
|
||
.loader-progress {
|
||
width: 0%;
|
||
height: 100%;
|
||
background: var(--neon-green);
|
||
box-shadow: 0 0 10px var(--neon-green);
|
||
transition: width 0.1s linear;
|
||
}
|
||
|
||
/* =========================================
|
||
锁屏界面
|
||
========================================= */
|
||
#lock-screen {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: rgba(5, 5, 16, 0.9);
|
||
backdrop-filter: blur(15px);
|
||
z-index: 9000;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
transition: opacity 0.5s ease;
|
||
}
|
||
|
||
.lock-time {
|
||
font-family: var(--font-head);
|
||
font-size: 6rem;
|
||
color: #fff;
|
||
text-shadow: 0 0 20px var(--neon-purple);
|
||
letter-spacing: 4px;
|
||
}
|
||
|
||
.lock-date {
|
||
font-size: 1.5rem;
|
||
color: var(--neon-blue);
|
||
margin-bottom: 40px;
|
||
}
|
||
|
||
.lock-hint {
|
||
color: rgba(255,255,255,0.5);
|
||
font-size: 0.9rem;
|
||
animation: pulse 2s infinite;
|
||
}
|
||
|
||
@keyframes pulse { 0% { opacity: 0.3; } 50% { opacity: 1; } 100% { opacity: 0.3; } }
|
||
|
||
/* =========================================
|
||
桌面环境
|
||
========================================= */
|
||
#desktop {
|
||
position: relative;
|
||
width: 100%;
|
||
height: calc(100vh - 48px); /* 减去任务栏高度 */
|
||
background-image: url('https://picsum.photos/seed/cyberpunkcity/1920/1080');
|
||
background-size: cover;
|
||
background-position: center;
|
||
overflow: hidden;
|
||
}
|
||
|
||
/* 动态背景层 */
|
||
#bg-canvas {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
opacity: 0.4;
|
||
z-index: 0;
|
||
}
|
||
|
||
/* 桌面图标网格 */
|
||
#desktop-icons {
|
||
position: absolute;
|
||
top: 20px;
|
||
left: 20px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
flex-wrap: wrap;
|
||
height: 90%;
|
||
width: 100px;
|
||
gap: 20px;
|
||
z-index: 10;
|
||
}
|
||
|
||
.desktop-icon {
|
||
width: 80px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
cursor: pointer;
|
||
padding: 10px;
|
||
border-radius: 8px;
|
||
transition: all 0.3s ease;
|
||
text-shadow: 0 0 5px #000;
|
||
}
|
||
|
||
.desktop-icon:hover {
|
||
background: rgba(255, 255, 255, 0.1);
|
||
box-shadow: inset 0 0 10px var(--neon-blue);
|
||
}
|
||
|
||
.desktop-icon i {
|
||
font-size: 2.5rem;
|
||
margin-bottom: 8px;
|
||
filter: drop-shadow(0 0 5px currentColor);
|
||
}
|
||
|
||
.desktop-icon span {
|
||
font-size: 0.8rem;
|
||
text-align: center;
|
||
color: #fff;
|
||
word-break: break-word;
|
||
}
|
||
|
||
/* 小部件区域 */
|
||
#widgets-area {
|
||
position: absolute;
|
||
top: 20px;
|
||
right: 20px;
|
||
width: 250px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 15px;
|
||
z-index: 5;
|
||
}
|
||
|
||
.widget {
|
||
background: rgba(0, 0, 0, 0.5);
|
||
border: 1px solid var(--neon-purple);
|
||
padding: 15px;
|
||
border-radius: 4px;
|
||
backdrop-filter: blur(5px);
|
||
box-shadow: 0 0 15px rgba(188, 19, 254, 0.2);
|
||
}
|
||
|
||
.widget-clock {
|
||
text-align: center;
|
||
font-family: var(--font-head);
|
||
}
|
||
.widget-time { font-size: 2rem; color: var(--neon-blue); }
|
||
.widget-date { font-size: 0.9rem; color: #aaa; }
|
||
|
||
/* =========================================
|
||
窗口系统
|
||
========================================= */
|
||
.window {
|
||
position: absolute;
|
||
background: var(--glass-bg);
|
||
backdrop-filter: blur(12px);
|
||
border: 1px solid var(--glass-border);
|
||
box-shadow: 0 10px 30px rgba(0,0,0,0.5), 0 0 15px rgba(0, 243, 255, 0.2);
|
||
border-radius: 6px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
min-width: 300px;
|
||
min-height: 200px;
|
||
overflow: hidden;
|
||
transition: transform 0.1s, opacity 0.2s;
|
||
animation: openWindow 0.3s cubic-bezier(0.18, 0.89, 0.32, 1.28);
|
||
}
|
||
|
||
@keyframes openWindow {
|
||
from { transform: scale(0.8); opacity: 0; }
|
||
to { transform: scale(1); opacity: 1; }
|
||
}
|
||
|
||
.window-header {
|
||
height: 36px;
|
||
background: rgba(0, 243, 255, 0.1);
|
||
border-bottom: 1px solid var(--glass-border);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 0 10px;
|
||
cursor: grab;
|
||
}
|
||
|
||
.window-header:active { cursor: grabbing; }
|
||
|
||
.window-title {
|
||
font-family: var(--font-head);
|
||
font-size: 0.9rem;
|
||
color: var(--neon-blue);
|
||
text-shadow: 0 0 5px var(--neon-blue);
|
||
pointer-events: none;
|
||
}
|
||
|
||
.window-controls {
|
||
display: flex;
|
||
gap: 8px;
|
||
}
|
||
|
||
.win-btn {
|
||
width: 12px;
|
||
height: 12px;
|
||
border-radius: 50%;
|
||
cursor: pointer;
|
||
border: none;
|
||
transition: all 0.2s;
|
||
}
|
||
|
||
.btn-min { background: #ffbd2e; box-shadow: 0 0 5px #ffbd2e; }
|
||
.btn-max { background: #27c93f; box-shadow: 0 0 5px #27c93f; }
|
||
.btn-close { background: #ff5f56; box-shadow: 0 0 5px #ff5f56; }
|
||
|
||
.btn-close:hover { background: #ff0000; transform: scale(1.1); }
|
||
|
||
.window-content {
|
||
flex: 1;
|
||
padding: 10px;
|
||
overflow: auto;
|
||
color: #ddd;
|
||
position: relative;
|
||
}
|
||
|
||
/* 调整大小的手柄 */
|
||
.resize-handle {
|
||
position: absolute;
|
||
bottom: 0;
|
||
right: 0;
|
||
width: 15px;
|
||
height: 15px;
|
||
cursor: se-resize;
|
||
background: linear-gradient(135deg, transparent 50%, var(--neon-blue) 50%);
|
||
opacity: 0.5;
|
||
}
|
||
|
||
/* =========================================
|
||
任务栏
|
||
========================================= */
|
||
#taskbar {
|
||
position: fixed;
|
||
bottom: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 48px;
|
||
background: rgba(5, 10, 20, 0.85);
|
||
backdrop-filter: blur(20px);
|
||
border-top: 1px solid var(--neon-blue);
|
||
display: flex;
|
||
align-items: center;
|
||
z-index: 5000;
|
||
padding: 0 10px;
|
||
}
|
||
|
||
.start-btn {
|
||
width: 36px;
|
||
height: 36px;
|
||
background: radial-gradient(circle, var(--neon-purple), transparent);
|
||
border: 1px solid var(--neon-purple);
|
||
border-radius: 4px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-right: 15px;
|
||
cursor: pointer;
|
||
transition: all 0.3s;
|
||
}
|
||
|
||
.start-btn:hover {
|
||
box-shadow: 0 0 15px var(--neon-purple);
|
||
transform: scale(1.1);
|
||
}
|
||
|
||
.taskbar-apps {
|
||
flex: 1;
|
||
display: flex;
|
||
gap: 5px;
|
||
}
|
||
|
||
.task-item {
|
||
padding: 0 15px;
|
||
height: 36px;
|
||
background: rgba(255,255,255,0.05);
|
||
border-bottom: 2px solid transparent;
|
||
color: #aaa;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
cursor: pointer;
|
||
transition: all 0.2s;
|
||
font-size: 0.8rem;
|
||
max-width: 150px;
|
||
}
|
||
|
||
.task-item.active {
|
||
background: rgba(0, 243, 255, 0.1);
|
||
border-bottom: 2px solid var(--neon-blue);
|
||
color: #fff;
|
||
text-shadow: 0 0 8px var(--neon-blue);
|
||
}
|
||
|
||
.task-item:hover {
|
||
background: rgba(255,255,255,0.1);
|
||
}
|
||
|
||
.tray {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 15px;
|
||
font-size: 0.8rem;
|
||
color: var(--neon-blue);
|
||
margin-left: 10px;
|
||
}
|
||
|
||
/* =========================================
|
||
开始菜单
|
||
========================================= */
|
||
#start-menu {
|
||
position: fixed;
|
||
bottom: 55px;
|
||
left: 10px;
|
||
width: 320px;
|
||
background: rgba(10, 15, 30, 0.95);
|
||
border: 1px solid var(--neon-blue);
|
||
border-radius: 6px;
|
||
padding: 15px;
|
||
display: none; /* JS控制显示 */
|
||
flex-direction: column;
|
||
z-index: 5001;
|
||
box-shadow: 0 0 20px rgba(0,0,0,0.8);
|
||
transform-origin: bottom left;
|
||
animation: slideUp 0.2s ease-out;
|
||
}
|
||
|
||
@keyframes slideUp {
|
||
from { opacity: 0; transform: translateY(20px); }
|
||
to { opacity: 1; transform: translateY(0); }
|
||
}
|
||
|
||
.menu-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 1fr);
|
||
gap: 10px;
|
||
margin-top: 10px;
|
||
}
|
||
|
||
.menu-item {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
padding: 10px;
|
||
border-radius: 4px;
|
||
cursor: pointer;
|
||
transition: background 0.2s;
|
||
}
|
||
|
||
.menu-item:hover { background: rgba(255,255,255,0.1); }
|
||
.menu-item i { font-size: 1.5rem; margin-bottom: 5px; color: var(--neon-blue); }
|
||
|
||
.menu-footer {
|
||
margin-top: 15px;
|
||
padding-top: 10px;
|
||
border-top: 1px solid rgba(255,255,255,0.1);
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.user-info { display: flex; align-items: center; gap: 8px; }
|
||
.avatar { width: 30px; height: 30px; border-radius: 50%; border: 1px solid var(--neon-pink); }
|
||
.power-btn { color: var(--neon-pink); cursor: pointer; }
|
||
|
||
/* =========================================
|
||
右键菜单
|
||
========================================= */
|
||
#context-menu {
|
||
position: fixed;
|
||
width: 180px;
|
||
background: rgba(0,0,0,0.9);
|
||
border: 1px solid var(--neon-purple);
|
||
z-index: 9999;
|
||
display: none;
|
||
flex-direction: column;
|
||
box-shadow: 0 0 10px rgba(188, 19, 254, 0.4);
|
||
}
|
||
|
||
.ctx-item {
|
||
padding: 8px 15px;
|
||
cursor: pointer;
|
||
font-size: 0.85rem;
|
||
color: #ddd;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.ctx-item:hover { background: var(--neon-purple); color: #fff; }
|
||
.ctx-sep { height: 1px; background: #333; margin: 2px 0; }
|
||
|
||
/* =========================================
|
||
应用特定样式
|
||
========================================= */
|
||
/* 终端 */
|
||
.terminal-app {
|
||
background: #0d0d0d;
|
||
font-family: var(--font-code);
|
||
height: 100%;
|
||
padding: 10px;
|
||
overflow-y: auto;
|
||
color: #0f0;
|
||
font-size: 0.9rem;
|
||
}
|
||
.cmd-line { display: flex; margin-top: 5px; }
|
||
.prompt { color: var(--neon-blue); margin-right: 8px; }
|
||
.cmd-input {
|
||
background: transparent;
|
||
border: none;
|
||
color: #0f0;
|
||
flex: 1;
|
||
outline: none;
|
||
font-family: var(--font-code);
|
||
font-size: 0.9rem;
|
||
}
|
||
.terminal-output { white-space: pre-wrap; margin-bottom: 10px; }
|
||
|
||
/* 文件管理器 */
|
||
.file-manager { display: flex; height: 100%; }
|
||
.file-sidebar { width: 120px; background: rgba(255,255,255,0.05); padding: 10px; border-right: 1px solid #333; }
|
||
.file-content { flex: 1; padding: 10px; display: grid; grid-template-columns: repeat(auto-fill, minmax(80px, 1fr)); gap: 10px; align-content: start; }
|
||
.file-item { display: flex; flex-direction: column; align-items: center; cursor: pointer; padding: 5px; }
|
||
.file-item:hover { background: rgba(255,255,255,0.1); }
|
||
.file-icon { font-size: 2rem; color: var(--neon-pink); margin-bottom: 5px; }
|
||
|
||
/* 系统监控 */
|
||
.charts-container { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; height: 100%; }
|
||
.chart-box { position: relative; width: 100%; height: 100%; }
|
||
|
||
/* 音乐播放器 */
|
||
.music-player { display: flex; flex-direction: column; align-items: center; height: 100%; justify-content: center; }
|
||
.album-art { width: 150px; height: 150px; background: #333; margin-bottom: 20px; border: 2px solid var(--neon-pink); box-shadow: 0 0 20px var(--neon-pink); display: flex; align-items: center; justify-content: center; font-size: 3rem; color: #fff; position: relative; overflow: hidden; }
|
||
.track-info { text-align: center; margin-bottom: 15px; }
|
||
.track-name { font-size: 1.2rem; color: #fff; text-shadow: 0 0 5px var(--neon-blue); }
|
||
.artist { font-size: 0.9rem; color: #aaa; }
|
||
.controls { display: flex; gap: 20px; font-size: 1.5rem; margin-bottom: 15px; }
|
||
.progress-bar { width: 100%; height: 4px; background: #333; border-radius: 2px; overflow: hidden; margin-bottom: 10px; }
|
||
.progress-fill { height: 100%; width: 30%; background: var(--neon-blue); box-shadow: 0 0 10px var(--neon-blue); }
|
||
|
||
/* 设置中心 */
|
||
.settings-form { padding: 20px; }
|
||
.setting-row { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; border-bottom: 1px solid #333; padding-bottom: 10px; }
|
||
.toggle-switch { position: relative; width: 40px; height: 20px; background: #333; border-radius: 10px; cursor: pointer; transition: 0.3s; }
|
||
.toggle-switch.active { background: var(--neon-green); }
|
||
.toggle-knob { position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; background: #fff; border-radius: 50%; transition: 0.3s; }
|
||
.toggle-switch.active .toggle-knob { left: 22px; }
|
||
|
||
/* 通知 */
|
||
#notification-area { position: fixed; top: 20px; right: 20px; display: flex; flex-direction: column; gap: 10px; z-index: 10000; }
|
||
.notification { width: 280px; background: rgba(0, 0, 0, 0.85); border-left: 4px solid var(--neon-blue); padding: 15px; backdrop-filter: blur(5px); box-shadow: 0 5px 15px rgba(0,0,0,0.5); transform: translateX(120%); transition: transform 0.3s ease-out; color: #fff; }
|
||
.notification.show { transform: translateX(0); }
|
||
.notif-title { font-weight: bold; color: var(--neon-blue); margin-bottom: 5px; font-family: var(--font-head); }
|
||
|
||
/* 移动端适配 */
|
||
@media (max-width: 768px) {
|
||
.window { width: 100% !important; height: calc(100% - 48px) !important; top: 0 !important; left: 0 !important; border-radius: 0; }
|
||
#desktop-icons { flex-direction: row; width: auto; height: auto; overflow-x: auto; top: auto; bottom: 60px; background: rgba(0,0,0,0.5); padding: 10px; border-radius: 15px; }
|
||
.widget { display: none; } /* 移动端隐藏桌面小部件 */
|
||
#taskbar { padding: 0 5px; }
|
||
.window-title { font-size: 0.7rem; }
|
||
}
|
||
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<!-- 扫描线滤镜 -->
|
||
<div class="scanlines"></div>
|
||
|
||
<!-- 通知容器 -->
|
||
<div id="notification-area"></div>
|
||
|
||
<!-- 开机屏幕 -->
|
||
<div id="boot-screen">
|
||
<div class="boot-text" id="boot-log">INITIALIZING CORE...</div>
|
||
<div class="loader-bar"><div class="loader-progress" id="boot-progress"></div></div>
|
||
</div>
|
||
|
||
<!-- 锁屏界面 -->
|
||
<div id="lock-screen">
|
||
<div class="lock-time" id="lock-clock">00:00</div>
|
||
<div class="lock-date" id="lock-date">YYYY-MM-DD</div>
|
||
<div class="lock-hint">CLICK TO UNLOCK // 系统就绪</div>
|
||
</div>
|
||
|
||
<!-- 桌面环境 -->
|
||
<div id="desktop">
|
||
<!-- 背景 Canvas (用于矩阵或粒子效果) -->
|
||
<canvas id="bg-canvas"></canvas>
|
||
|
||
<!-- 桌面小部件 -->
|
||
<div id="widgets-area">
|
||
<div class="widget widget-clock">
|
||
<div class="widget-time" id="widget-time">12:00:00</div>
|
||
<div class="widget-date" id="widget-date">2077-10-23</div>
|
||
</div>
|
||
<div class="widget">
|
||
<div style="color: var(--neon-green); font-size: 0.8rem; margin-bottom: 5px;">CPU LOAD</div>
|
||
<div style="height: 4px; background: #333;"><div style="width: 45%; height: 100%; background: var(--neon-green);"></div></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 桌面图标 -->
|
||
<div id="desktop-icons">
|
||
<!-- 由 JS 生成 -->
|
||
</div>
|
||
|
||
<!-- 窗口容器 -->
|
||
<div id="window-container">
|
||
<!-- 动态生成窗口 -->
|
||
</div>
|
||
|
||
<!-- 开始菜单 -->
|
||
<div id="start-menu">
|
||
<div style="color: #fff; font-family: var(--font-head); padding-bottom: 10px; border-bottom: 1px solid #333;">CYBER-OS</div>
|
||
<div class="menu-grid" id="start-menu-grid">
|
||
<!-- JS 填充 -->
|
||
</div>
|
||
<div class="menu-footer">
|
||
<div class="user-info">
|
||
<img src="https://picsum.photos/seed/cyberuser/50/50" class="avatar" alt="User">
|
||
<span style="font-size: 0.8rem;">NetRunner_01</span>
|
||
</div>
|
||
<div class="power-btn" onclick="location.reload()"><i class="fas fa-power-off"></i></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 任务栏 -->
|
||
<div id="taskbar">
|
||
<div class="start-btn" id="start-btn">
|
||
<i class="fas fa-th-large" style="color: #fff;"></i>
|
||
</div>
|
||
<div class="taskbar-apps" id="taskbar-apps">
|
||
<!-- 任务栏项 -->
|
||
</div>
|
||
<div class="tray">
|
||
<i class="fas fa-wifi"></i>
|
||
<i class="fas fa-volume-up"></i>
|
||
<span id="tray-time">12:00</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 右键菜单 -->
|
||
<div id="context-menu">
|
||
<div class="ctx-item" onclick="appManager.openApp('terminal')">Terminal</div>
|
||
<div class="ctx-item" onclick="appManager.openApp('settings')">Settings</div>
|
||
<div class="ctx-sep"></div>
|
||
<div class="ctx-item" onclick="location.reload()">Reboot System</div>
|
||
<div class="ctx-item" onclick="document.getElementById('boot-screen').style.display='flex';location.reload()">Shutdown</div>
|
||
</div>
|
||
|
||
<!-- JavaScript 逻辑 -->
|
||
<script>
|
||
/**
|
||
* 赛博朋克 OS - 核心逻辑
|
||
* 包含:系统状态管理、窗口管理器、应用逻辑、特效渲染
|
||
*/
|
||
|
||
// ================= 系统配置与状态 =================
|
||
const SystemConfig = {
|
||
themes: ['neon-blue', 'neon-pink', 'matrix-green'],
|
||
fileSystem: {
|
||
'root': ['home', 'bin', 'etc', 'var'],
|
||
'home': ['documents', 'downloads', 'music', 'secret_plan.txt'],
|
||
'documents': ['project_omega.doc', 'budget.xls'],
|
||
'downloads': ['hack_tool_v2.zip', 'wallpaper.jpg']
|
||
},
|
||
currentPath: 'root'
|
||
};
|
||
|
||
let zIndexCounter = 100;
|
||
let activeWindowId = null;
|
||
const fileSystemData = { ...SystemConfig.fileSystem }; // 深拷贝以供操作
|
||
|
||
// ================= 工具函数 =================
|
||
function getEl(id) { return document.getElementById(id); }
|
||
|
||
function showNotification(title, msg) {
|
||
const area = getEl('notification-area');
|
||
const notif = document.createElement('div');
|
||
notif.className = 'notification';
|
||
notif.innerHTML = `<div class="notif-title">${title}</div><div>${msg}</div>`;
|
||
area.appendChild(notif);
|
||
|
||
// 动画
|
||
setTimeout(() => notif.classList.add('show'), 10);
|
||
setTimeout(() => {
|
||
notif.classList.remove('show');
|
||
setTimeout(() => notif.remove(), 300);
|
||
}, 4000);
|
||
}
|
||
|
||
// ================= 窗口管理器 =================
|
||
class WindowManager {
|
||
constructor() {
|
||
this.windows = {};
|
||
this.container = getEl('window-container');
|
||
this.taskbar = getEl('taskbar-apps');
|
||
}
|
||
|
||
createWindow(appId, title, contentHtml, width = 500, height = 400) {
|
||
const id = 'win_' + Date.now();
|
||
zIndexCounter++;
|
||
|
||
const winEl = document.createElement('div');
|
||
winEl.className = 'window';
|
||
winEl.id = id;
|
||
winEl.style.width = width + 'px';
|
||
winEl.style.height = height + 'px';
|
||
winEl.style.zIndex = zIndexCounter;
|
||
// 初始位置居中
|
||
winEl.style.top = (window.innerHeight / 2 - height / 2 - 24) + 'px';
|
||
winEl.style.left = (window.innerWidth / 2 - width / 2) + 'px';
|
||
|
||
winEl.innerHTML = `
|
||
<div class="window-header" onmousedown="windowManager.startDrag(event, '${id}')">
|
||
<div class="window-title"><i class="${appIcons[appId]}"></i> ${title}</div>
|
||
<div class="window-controls">
|
||
<button class="win-btn btn-min" onclick="windowManager.minimize('${id}')"></button>
|
||
<button class="win-btn btn-max" onclick="windowManager.toggleMaximize('${id}')"></button>
|
||
<button class="win-btn btn-close" onclick="windowManager.close('${id}')"></button>
|
||
</div>
|
||
</div>
|
||
<div class="window-content" onclick="windowManager.focus('${id}')">
|
||
${contentHtml}
|
||
</div>
|
||
<div class="resize-handle" onmousedown="windowManager.startResize(event, '${id}')"></div>
|
||
`;
|
||
|
||
this.container.appendChild(winEl);
|
||
this.windows[id] = { id, appId, minimized: false, maximized: false, prevRect: null };
|
||
|
||
this.addToTaskbar(id, appId, title);
|
||
this.focus(id);
|
||
|
||
// 初始化特定应用逻辑
|
||
if (appId === 'monitor') initMonitorApp();
|
||
if (appId === 'terminal') initTerminalApp(id);
|
||
if (appId === 'filemanager') renderFileManager(id);
|
||
}
|
||
|
||
close(id) {
|
||
const win = getEl(id);
|
||
if (win) {
|
||
win.style.opacity = '0';
|
||
win.style.transform = 'scale(0.9)';
|
||
setTimeout(() => win.remove(), 200);
|
||
}
|
||
delete this.windows[id];
|
||
getEl(id + '_task').remove();
|
||
}
|
||
|
||
focus(id) {
|
||
const win = getEl(id);
|
||
if (win) {
|
||
zIndexCounter++;
|
||
win.style.zIndex = zIndexCounter;
|
||
activeWindowId = id;
|
||
// Update taskbar active state
|
||
document.querySelectorAll('.task-item').forEach(t => t.classList.remove('active'));
|
||
const taskItem = getEl(id + '_task');
|
||
if (taskItem) taskItem.classList.add('active');
|
||
}
|
||
}
|
||
|
||
minimize(id) {
|
||
const win = getEl(id);
|
||
win.style.display = 'none';
|
||
this.windows[id].minimized = true;
|
||
getEl(id + '_task').classList.remove('active');
|
||
}
|
||
|
||
restore(id) {
|
||
const win = getEl(id);
|
||
win.style.display = 'flex';
|
||
this.windows[id].minimized = false;
|
||
this.focus(id);
|
||
}
|
||
|
||
toggleMaximize(id) {
|
||
const win = getEl(id);
|
||
const state = this.windows[id];
|
||
if (state.maximized) {
|
||
win.style.top = state.prevRect.top;
|
||
win.style.left = state.prevRect.left;
|
||
win.style.width = state.prevRect.width;
|
||
win.style.height = state.prevRect.height;
|
||
state.maximized = false;
|
||
} else {
|
||
state.prevRect = {
|
||
top: win.style.top,
|
||
left: win.style.left,
|
||
width: win.style.width,
|
||
height: win.style.height
|
||
};
|
||
win.style.top = '0';
|
||
win.style.left = '0';
|
||
win.style.width = '100%';
|
||
win.style.height = 'calc(100vh - 48px)'; // 减去任务栏
|
||
state.maximized = true;
|
||
}
|
||
}
|
||
|
||
addToTaskbar(id, appId, title) {
|
||
const item = document.createElement('div');
|
||
item.className = 'task-item active';
|
||
item.id = id + '_task';
|
||
item.innerHTML = `<i class="${appIcons[appId]}"></i> ${title}`;
|
||
item.onclick = () => {
|
||
const winState = this.windows[id];
|
||
if (winState.minimized) this.restore(id);
|
||
else if (activeWindowId === id) this.minimize(id);
|
||
else this.focus(id);
|
||
};
|
||
this.taskbar.appendChild(item);
|
||
}
|
||
|
||
// 拖拽逻辑
|
||
startDrag(e, id) {
|
||
if (e.target.closest('.window-controls')) return;
|
||
e.preventDefault();
|
||
const win = getEl(id);
|
||
this.focus(id);
|
||
|
||
let shiftX = e.clientX - win.getBoundingClientRect().left;
|
||
let shiftY = e.clientY - win.getBoundingClientRect().top;
|
||
|
||
const moveAt = (pageX, pageY) => {
|
||
// 边界限制
|
||
let newLeft = pageX - shiftX;
|
||
let newTop = pageY - shiftY;
|
||
|
||
// 简单限制不让标题栏完全跑出去
|
||
if (newTop < 0) newTop = 0;
|
||
if (newTop > window.innerHeight - 48) newTop = window.innerHeight - 48;
|
||
|
||
win.style.left = newLeft + 'px';
|
||
win.style.top = newTop + 'px';
|
||
};
|
||
|
||
const onMouseMove = (event) => moveAt(event.clientX, event.clientY);
|
||
|
||
document.addEventListener('mousemove', onMouseMove);
|
||
document.addEventListener('mouseup', () => {
|
||
document.removeEventListener('mousemove', onMouseMove);
|
||
}, {once: true});
|
||
}
|
||
|
||
// 调整大小逻辑
|
||
startResize(e, id) {
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
const win = getEl(id);
|
||
const startX = e.clientX;
|
||
const startY = e.clientY;
|
||
const startWidth = parseInt(document.defaultView.getComputedStyle(win).width, 10);
|
||
const startHeight = parseInt(document.defaultView.getComputedStyle(win).height, 10);
|
||
|
||
const doDrag = (e) => {
|
||
win.style.width = (startWidth + e.clientX - startX) + 'px';
|
||
win.style.height = (startHeight + e.clientY - startY) + 'px';
|
||
}
|
||
|
||
const stopDrag = () => {
|
||
document.documentElement.removeEventListener('mousemove', doDrag, false);
|
||
document.documentElement.removeEventListener('mouseup', stopDrag, false);
|
||
}
|
||
|
||
document.documentElement.addEventListener('mousemove', doDrag, false);
|
||
document.documentElement.addEventListener('mouseup', stopDrag, false);
|
||
}
|
||
}
|
||
|
||
const windowManager = new WindowManager();
|
||
|
||
// ================= 应用管理器 =================
|
||
const appIcons = {
|
||
'terminal': 'fas fa-terminal',
|
||
'filemanager': 'fas fa-folder-open',
|
||
'browser': 'fas fa-globe',
|
||
'settings': 'fas fa-cogs',
|
||
'music': 'fas fa-music',
|
||
'notepad': 'fas fa-sticky-note',
|
||
'monitor': 'fas fa-chart-line'
|
||
};
|
||
|
||
const apps = {
|
||
'terminal': { title: 'Terminal', width: 600, height: 400 },
|
||
'filemanager': { title: 'File Explorer', width: 600, height: 450 },
|
||
'browser': { title: 'CyberBrowser', width: 800, height: 600 },
|
||
'settings': { title: 'System Settings', width: 400, height: 350 },
|
||
'music': { title: 'SynthWave Player', width: 350, height: 400 },
|
||
'notepad': { title: 'Data Note', width: 300, height: 400 },
|
||
'monitor': { title: 'System Monitor', width: 500, height: 350 }
|
||
};
|
||
|
||
const appManager = {
|
||
openApp: (appId) => {
|
||
const app = apps[appId];
|
||
let content = '';
|
||
|
||
// 根据应用ID生成内容
|
||
switch(appId) {
|
||
case 'terminal':
|
||
content = `<div class="terminal-app" id="term-${Date.now()}">
|
||
<div class="terminal-history">Welcome to Cyber-OS v2.4.0<br>Type 'help' for commands.</div>
|
||
<div class="cmd-line">
|
||
<span class="prompt">root@cyber:~$</span>
|
||
<input type="text" class="cmd-input" onkeydown="handleTerminalCommand(event, this)">
|
||
</div>
|
||
</div>`;
|
||
break;
|
||
case 'filemanager':
|
||
content = `<div class="file-manager">
|
||
<div class="file-sidebar">
|
||
<div class="ctx-item" onclick="renderFileManagerContent('${Date.now()}', 'root')">/ Root</div>
|
||
<div class="ctx-item">/ Home</div>
|
||
<div class="ctx-item">/ Bin</div>
|
||
</div>
|
||
<div class="file-content" id="fm-content-${Date.now()}">
|
||
<!-- JS Populate -->
|
||
</div>
|
||
</div>`;
|
||
break;
|
||
case 'browser':
|
||
content = `<div style="display:flex; flex-direction:column; height:100%;">
|
||
<div style="padding:5px; background:rgba(255,255,255,0.1); display:flex; gap:5px;">
|
||
<button><</button> <button>></button>
|
||
<input type="text" value="https://en.wikipedia.org/wiki/Cyberpunk" style="flex:1; background:#000; border:1px solid #333; color:#fff;">
|
||
</div>
|
||
<iframe src="https://en.m.wikipedia.org/wiki/Cyberpunk" style="flex:1; border:none; background:#fff;"></iframe>
|
||
</div>`;
|
||
break;
|
||
case 'settings':
|
||
content = `<div class="settings-form">
|
||
<div class="setting-row">
|
||
<span>Neon Glow</span>
|
||
<div class="toggle-switch active"><div class="toggle-knob"></div></div>
|
||
</div>
|
||
<div class="setting-row">
|
||
<span>Sound Effects</span>
|
||
<div class="toggle-switch active"><div class="toggle-knob"></div></div>
|
||
</div>
|
||
<div class="setting-row">
|
||
<span>Matrix Rain</span>
|
||
<div class="toggle-switch" onclick="toggleMatrixBg(this)"><div class="toggle-knob"></div></div>
|
||
</div>
|
||
<button style="width:100%; padding:10px; background:var(--neon-blue); border:none; font-family:var(--font-head); cursor:pointer;" onclick="showNotification('Settings', 'Configuration Saved')">SAVE CHANGES</button>
|
||
</div>`;
|
||
break;
|
||
case 'music':
|
||
content = `<div class="music-player">
|
||
<div class="album-art"><i class="fas fa-compact-disc fa-spin"></i></div>
|
||
<div class="track-info">
|
||
<div class="track-name">Nightcall</div>
|
||
<div class="artist">Kavinsky</div>
|
||
</div>
|
||
<div class="progress-bar"><div class="progress-fill"></div></div>
|
||
<div class="controls">
|
||
<i class="fas fa-step-backward"></i>
|
||
<i class="fas fa-play-circle" style="color:var(--neon-blue);"></i>
|
||
<i class="fas fa-step-forward"></i>
|
||
</div>
|
||
</div>`;
|
||
break;
|
||
case 'notepad':
|
||
const savedNote = localStorage.getItem('cyber_note') || '';
|
||
content = `<textarea style="width:100%; height:100%; background:rgba(0,0,0,0.5); border:none; color:#0f0; font-family:var(--font-code); padding:10px; resize:none;" placeholder="Enter data..." oninput="localStorage.setItem('cyber_note', this.value)">${savedNote}</textarea>`;
|
||
break;
|
||
case 'monitor':
|
||
content = `<div class="charts-container">
|
||
<div class="chart-box"><canvas id="chart-cpu"></canvas></div>
|
||
<div class="chart-box"><canvas id="chart-ram"></canvas></div>
|
||
</div>`;
|
||
break;
|
||
}
|
||
|
||
windowManager.createWindow(appId, app.title, content, app.width, app.height);
|
||
getEl('start-menu').style.display = 'none';
|
||
}
|
||
};
|
||
|
||
// ================= 应用逻辑实现 =================
|
||
|
||
// 1. 终端逻辑
|
||
function initTerminalApp(winId) {
|
||
// Auto focus
|
||
setTimeout(() => {
|
||
const input = getEl(winId).querySelector('input');
|
||
if(input) input.focus();
|
||
}, 100);
|
||
}
|
||
|
||
function handleTerminalCommand(e, input) {
|
||
if (e.key === 'Enter') {
|
||
const cmd = input.value.trim();
|
||
const history = input.parentElement.parentElement.querySelector('.terminal-history');
|
||
const output = executeCommand(cmd);
|
||
|
||
history.innerHTML += `<div><span style="color:var(--neon-blue)">root@cyber:~$</span> ${cmd}</div>${output}`;
|
||
history.scrollTop = history.scrollHeight;
|
||
input.value = '';
|
||
}
|
||
}
|
||
|
||
function executeCommand(cmd) {
|
||
const args = cmd.split(' ');
|
||
const command = args[0].toLowerCase();
|
||
let res = '';
|
||
|
||
switch(command) {
|
||
case 'help': res = `<div style="color:#fff">Available commands:<br>help, clear, date, whoami, ls, cat [file], echo [text], neofetch, matrix, theme [color], reboot</div>`; break;
|
||
case 'clear': return ''; // Special handling
|
||
case 'date': res = `<div>${new Date().toString()}</div>`; break;
|
||
case 'whoami': res = `<div>NetRunner_01 (Root User)</div>`; break;
|
||
case 'ls':
|
||
case 'dir': res = `<div style="color:var(--neon-pink)">documents downloads music secret_plan.txt</div>`; break;
|
||
case 'cat':
|
||
case 'type':
|
||
if(args[1]) res = `<div>Content of ${args[1]}: [ENCRYPTED DATA]... Access Denied.</div>`;
|
||
else res = `<div>Usage: cat [filename]</div>`;
|
||
break;
|
||
case 'echo': res = `<div>${args.slice(1).join(' ')}</div>`; break;
|
||
case 'neofetch':
|
||
res = `
|
||
<div style="color:var(--neon-blue)">
|
||
____ user@cyber-os
|
||
| _ \\ _ __ ___ __ __ ___ -------------
|
||
| |_) || '__|/ _ \\\\ \\ /\\ / // _ \\ OS: Cyber-OS v2.4
|
||
| _ < | | | (_) |\\ V V /| (_) | Kernel: NetKernel 9.0
|
||
|_| \\_\\|_| \\___/ \\_/\\_/ \\___/ Shell: CyberBash
|
||
Theme: Neon Blue
|
||
</div>`;
|
||
break;
|
||
case 'matrix':
|
||
showNotification('Terminal', 'Matrix Protocol Initiated');
|
||
toggleMatrixBg(null); // 修复:传递 null 让函数逻辑自动处理
|
||
res = `<div>Protocol engaged.</div>`;
|
||
break;
|
||
case 'theme':
|
||
if(args[1]) res = `<div>Theme switched to ${args[1]}</div>`;
|
||
else res = `<div>Usage: theme [blue/pink]</div>`;
|
||
break;
|
||
case 'reboot': location.reload(); break;
|
||
case '': break;
|
||
default: res = `<div>Command not found: ${command}</div>`;
|
||
}
|
||
return res;
|
||
}
|
||
|
||
// 2. 文件管理器逻辑
|
||
function renderFileManager(winId) {
|
||
setTimeout(() => renderFileManagerContent(winId, 'root'), 50);
|
||
}
|
||
|
||
function renderFileManagerContent(winId, path) {
|
||
const container = getEl(winId).querySelector('.file-content');
|
||
container.innerHTML = '';
|
||
|
||
// 模拟文件数据
|
||
const files = [
|
||
{ name: 'secret_plan.txt', icon: 'fa-file-alt' },
|
||
{ name: 'virus.exe', icon: 'fa-bug' },
|
||
{ name: 'image_data', icon: 'fa-folder' },
|
||
{ name: 'sys_config', icon: 'fa-cog' },
|
||
{ name: 'music_track.mp3', icon: 'fa-music' },
|
||
{ name: 'network_map', icon: 'fa-project-diagram' },
|
||
];
|
||
|
||
files.forEach(f => {
|
||
const el = document.createElement('div');
|
||
el.className = 'file-item';
|
||
el.innerHTML = `<i class="fas ${f.icon} file-icon"></i><span>${f.name}</span>`;
|
||
el.ondblclick = () => showNotification('System', `Opening ${f.name}...`);
|
||
container.appendChild(el);
|
||
});
|
||
}
|
||
|
||
// 3. 系统监控 (Chart.js)
|
||
function initMonitorApp() {
|
||
// 查找最新的monitor窗口
|
||
const cpuCtx = document.getElementById('chart-cpu')?.getContext('2d');
|
||
const ramCtx = document.getElementById('chart-ram')?.getContext('2d');
|
||
|
||
if(!cpuCtx || !ramCtx) return;
|
||
|
||
// 配置 Chart
|
||
Chart.defaults.color = '#888';
|
||
Chart.defaults.borderColor = '#333';
|
||
|
||
const commonOptions = {
|
||
responsive: true,
|
||
maintainAspectRatio: false,
|
||
animation: false,
|
||
plugins: { legend: { display: false } },
|
||
scales: { x: { display: false }, y: { min: 0, max: 100 } }
|
||
};
|
||
|
||
const createChart = (ctx, color) => new Chart(ctx, {
|
||
type: 'line',
|
||
data: {
|
||
labels: Array(20).fill(''),
|
||
datasets: [{
|
||
data: Array(20).fill(50),
|
||
borderColor: color,
|
||
borderWidth: 2,
|
||
backgroundColor: color.replace('1)', '0.1)'),
|
||
fill: true,
|
||
tension: 0.4,
|
||
pointRadius: 0
|
||
}]
|
||
},
|
||
options: commonOptions
|
||
});
|
||
|
||
const cpuChart = createChart(cpuCtx, '#00f3ff');
|
||
const ramChart = createChart(ramCtx, '#ff00ff');
|
||
|
||
// 模拟实时数据
|
||
setInterval(() => {
|
||
if(document.body.contains(cpuCtx.canvas)) {
|
||
updateData(cpuChart);
|
||
updateData(ramChart);
|
||
}
|
||
}, 1000);
|
||
|
||
function updateData(chart) {
|
||
const data = chart.data.datasets[0].data;
|
||
data.shift();
|
||
data.push(Math.floor(Math.random() * 60) + 20);
|
||
chart.update();
|
||
}
|
||
}
|
||
|
||
// ================= 全局交互与特效 =================
|
||
|
||
// 开机动画
|
||
window.onload = () => {
|
||
const progress = getEl('boot-progress');
|
||
const log = getEl('boot-log');
|
||
let width = 0;
|
||
const logs = ["Loading Kernel...", "Mounting Volumes...", "Starting Network...", "Decrypting User Data...", "Welcome to Night City"];
|
||
|
||
const interval = setInterval(() => {
|
||
width += 1;
|
||
progress.style.width = width + '%';
|
||
if (width % 20 === 0 && width/20 < logs.length) {
|
||
log.innerText = logs[width/20];
|
||
}
|
||
if (width >= 100) {
|
||
clearInterval(interval);
|
||
setTimeout(() => {
|
||
getEl('boot-screen').style.display = 'none';
|
||
updateClock();
|
||
setInterval(updateClock, 1000);
|
||
initBackground();
|
||
}, 500);
|
||
}
|
||
}, 30);
|
||
|
||
// 生成桌面图标
|
||
const iconContainer = getEl('desktop-icons');
|
||
const menuGrid = getEl('start-menu-grid');
|
||
|
||
Object.keys(apps).forEach(key => {
|
||
const app = apps[key];
|
||
// 桌面图标
|
||
const div = document.createElement('div');
|
||
div.className = 'desktop-icon';
|
||
div.innerHTML = `<i class="${appIcons[key]}" style="color: ${key === 'terminal' ? '#0f0' : (key === 'filemanager' ? 'var(--neon-pink)' : 'var(--neon-blue)')}"></i><span>${app.title}</span>`;
|
||
div.onclick = () => appManager.openApp(key);
|
||
iconContainer.appendChild(div);
|
||
|
||
// 开始菜单项
|
||
const menuItem = document.createElement('div');
|
||
menuItem.className = 'menu-item';
|
||
menuItem.innerHTML = `<i class="${appIcons[key]}"></i><span style="font-size:0.7rem">${app.title}</span>`;
|
||
menuItem.onclick = () => appManager.openApp(key);
|
||
menuGrid.appendChild(menuItem);
|
||
});
|
||
};
|
||
|
||
// 时钟更新
|
||
function updateClock() {
|
||
const now = new Date();
|
||
const timeStr = now.toLocaleTimeString('en-GB');
|
||
const dateStr = now.toLocaleDateString('zh-CN');
|
||
|
||
getEl('tray-time').innerText = timeStr;
|
||
getEl('widget-time').innerText = timeStr;
|
||
getEl('widget-date').innerText = dateStr;
|
||
getEl('lock-clock').innerText = timeStr;
|
||
getEl('lock-date').innerText = dateStr;
|
||
}
|
||
|
||
// 锁屏逻辑
|
||
getEl('lock-screen').addEventListener('click', () => {
|
||
getEl('lock-screen').style.opacity = '0';
|
||
setTimeout(() => getEl('lock-screen').style.display = 'none', 500);
|
||
showNotification('System', 'User Logged In Successfully');
|
||
});
|
||
|
||
// 任务栏开始按钮
|
||
getEl('start-btn').onclick = (e) => {
|
||
e.stopPropagation();
|
||
const menu = getEl('start-menu');
|
||
menu.style.display = menu.style.display === 'flex' ? 'none' : 'flex';
|
||
};
|
||
|
||
// 点击任意处关闭开始菜单
|
||
document.addEventListener('click', (e) => {
|
||
if (!e.target.closest('#start-menu') && !e.target.closest('#start-btn')) {
|
||
getEl('start-menu').style.display = 'none';
|
||
}
|
||
});
|
||
|
||
// 右键菜单
|
||
document.addEventListener('contextmenu', (e) => {
|
||
e.preventDefault();
|
||
const menu = getEl('context-menu');
|
||
menu.style.display = 'flex';
|
||
menu.style.left = e.clientX + 'px';
|
||
menu.style.top = e.clientY + 'px';
|
||
});
|
||
|
||
document.addEventListener('click', () => getEl('context-menu').style.display = 'none');
|
||
|
||
// ================= 背景特效 =================
|
||
let matrixMode = false;
|
||
|
||
// 修复后的 toggleMatrixBg 函数
|
||
function toggleMatrixBg(el) {
|
||
// 如果 el 为 null 或者不是有效的 DOM 元素(例如终端命令调用),则强制开启 Matrix 模式
|
||
if (!el || !el.classList || typeof el.classList.contains !== 'function') {
|
||
matrixMode = true;
|
||
return;
|
||
}
|
||
|
||
// 正常的 UI 点击逻辑
|
||
if (el.classList.contains('toggle-switch')) {
|
||
el.classList.toggle('active');
|
||
matrixMode = el.classList.contains('active');
|
||
}
|
||
}
|
||
|
||
function initBackground() {
|
||
const canvas = getEl('bg-canvas');
|
||
const ctx = canvas.getContext('2d');
|
||
|
||
let width, height;
|
||
|
||
function resize() {
|
||
width = canvas.width = window.innerWidth;
|
||
height = canvas.height = window.innerHeight;
|
||
}
|
||
window.addEventListener('resize', resize);
|
||
resize();
|
||
|
||
// 粒子系统
|
||
const particles = [];
|
||
const particleCount = 50;
|
||
|
||
for(let i=0; i<particleCount; i++) {
|
||
particles.push({
|
||
x: Math.random() * width,
|
||
y: Math.random() * height,
|
||
vx: (Math.random() - 0.5) * 0.5,
|
||
vy: (Math.random() - 0.5) * 0.5,
|
||
size: Math.random() * 2,
|
||
color: Math.random() > 0.5 ? '#00f3ff' : '#ff00ff'
|
||
});
|
||
}
|
||
|
||
// Matrix Rain 字符
|
||
const columns = Math.floor(width / 20);
|
||
const drops = Array(columns).fill(1);
|
||
const chars = "010101XYCYPUNK<>";
|
||
|
||
function animate() {
|
||
if (matrixMode) {
|
||
ctx.fillStyle = 'rgba(0, 0, 0, 0.05)';
|
||
ctx.fillRect(0, 0, width, height);
|
||
ctx.fillStyle = '#0f0';
|
||
ctx.font = '15px monospace';
|
||
for (let i = 0; i < drops.length; i++) {
|
||
const text = chars[Math.floor(Math.random() * chars.length)];
|
||
ctx.fillText(text, i * 20, drops[i] * 20);
|
||
if (drops[i] * 20 > height && Math.random() > 0.975) drops[i] = 0;
|
||
drops[i]++;
|
||
}
|
||
} else {
|
||
// 默认:赛博星空粒子
|
||
ctx.clearRect(0, 0, width, height);
|
||
|
||
// 绘制网格
|
||
ctx.strokeStyle = 'rgba(0, 243, 255, 0.05)';
|
||
ctx.lineWidth = 1;
|
||
const gridSize = 50;
|
||
|
||
// 移动网格效果
|
||
const time = Date.now() / 1000;
|
||
const offsetY = (time * 10) % gridSize;
|
||
|
||
ctx.beginPath();
|
||
for (let x = 0; x <= width; x += gridSize) {
|
||
ctx.moveTo(x, 0);
|
||
ctx.lineTo(x, height);
|
||
}
|
||
for (let y = offsetY; y <= height; y += gridSize) {
|
||
ctx.moveTo(0, y);
|
||
ctx.lineTo(width, y);
|
||
}
|
||
ctx.stroke();
|
||
|
||
// 绘制粒子
|
||
particles.forEach(p => {
|
||
p.x += p.vx;
|
||
p.y += p.vy;
|
||
|
||
if(p.x < 0) p.x = width;
|
||
if(p.x > width) p.x = 0;
|
||
if(p.y < 0) p.y = height;
|
||
if(p.y > height) p.y = 0;
|
||
|
||
ctx.beginPath();
|
||
ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
|
||
ctx.fillStyle = p.color;
|
||
ctx.fill();
|
||
|
||
// 连线
|
||
particles.forEach(p2 => {
|
||
const dx = p.x - p2.x;
|
||
const dy = p.y - p2.y;
|
||
const dist = Math.sqrt(dx*dx + dy*dy);
|
||
if(dist < 100) {
|
||
ctx.beginPath();
|
||
ctx.strokeStyle = `rgba(0, 243, 255, ${0.1 - dist/1000})`;
|
||
ctx.moveTo(p.x, p.y);
|
||
ctx.lineTo(p2.x, p2.y);
|
||
ctx.stroke();
|
||
}
|
||
});
|
||
});
|
||
}
|
||
requestAnimationFrame(animate);
|
||
}
|
||
animate();
|
||
}
|
||
|
||
</script>
|
||
</body>
|
||
</html> |