mirror of
https://github.com/wwiinnddyy/LanMountainDesktop.git
synced 2026-06-20 15:44:25 +08:00
Introduce GridPreviewControl and CornerRadiusPreviewControl for visual previews and wire them into the Components settings (add ScreenAspectRatio, CornerRadiusPreviewValue, and screen aspect init). Refactor ComponentsSettingsPage UI to show live previews. Improve DataSettingsPage layout and storage bar logic (use item percentages directly, include remaining segment, adjust visuals and visibility triggers). Simplify LauncherSettingsPage header/appearance layout. Add SECURITY_AUDIT_REPORT.md, analysis summary, mockup HTML, and a local .claude settings file.
899 lines
31 KiB
HTML
899 lines
31 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>噪音等级组件改造 Mockup v2</title>
|
||
<style>
|
||
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;600;700&display=swap');
|
||
|
||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||
|
||
:root {
|
||
--quiet: #34D399;
|
||
--normal: #60A5FA;
|
||
--noisy: #F59E0B;
|
||
--extreme: #EF4444;
|
||
--bg: #0B1220;
|
||
--surface: #131D2E;
|
||
--surface-raised: #182438;
|
||
--border: rgba(255,255,255,0.08);
|
||
--text-primary: #EFF3FF;
|
||
--text-secondary: #8B9BB8;
|
||
--text-muted: #5C6D86;
|
||
--radius-component: 24px;
|
||
--radius-xs: 12px;
|
||
}
|
||
|
||
body {
|
||
font-family: 'Noto Sans SC', -apple-system, sans-serif;
|
||
background: var(--bg);
|
||
color: var(--text-primary);
|
||
min-height: 100vh;
|
||
padding: 40px 20px;
|
||
}
|
||
|
||
.page-header {
|
||
text-align: center;
|
||
margin-bottom: 48px;
|
||
}
|
||
|
||
.page-header h1 {
|
||
font-size: 28px;
|
||
font-weight: 700;
|
||
letter-spacing: -0.5px;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.page-header p {
|
||
font-size: 14px;
|
||
color: var(--text-secondary);
|
||
}
|
||
|
||
.section {
|
||
max-width: 1200px;
|
||
margin: 0 auto 56px;
|
||
}
|
||
|
||
.section-title {
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
margin-bottom: 6px;
|
||
color: var(--text-primary);
|
||
}
|
||
|
||
.section-desc {
|
||
font-size: 13px;
|
||
color: var(--text-secondary);
|
||
margin-bottom: 24px;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.mockup-row {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
|
||
gap: 24px;
|
||
}
|
||
|
||
.widget-card {
|
||
background: var(--surface-raised);
|
||
border: 1px solid var(--border);
|
||
border-radius: var(--radius-component);
|
||
padding: 16px 14px;
|
||
position: relative;
|
||
overflow: hidden;
|
||
animation: fadeIn 0.5s ease both;
|
||
}
|
||
|
||
.widget-card:nth-child(2) { animation-delay: 0.08s; }
|
||
|
||
.widget-label {
|
||
position: absolute;
|
||
top: -1px;
|
||
right: 20px;
|
||
background: rgba(96, 165, 250, 0.2);
|
||
border: 1px solid rgba(96, 165, 250, 0.3);
|
||
border-top: none;
|
||
border-radius: 0 0 8px 8px;
|
||
padding: 3px 10px;
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
color: #8BE8FF;
|
||
}
|
||
|
||
@keyframes fadeIn {
|
||
from { opacity: 0; transform: translateY(8px); }
|
||
to { opacity: 1; transform: translateY(0); }
|
||
}
|
||
|
||
/* ====== 通用组件样式 ====== */
|
||
.chart-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.chart-title {
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
color: var(--text-secondary);
|
||
}
|
||
|
||
.chart-badge {
|
||
padding: 3px 8px;
|
||
border-radius: var(--radius-xs);
|
||
border: 1px solid rgba(255,255,255,0.3);
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
color: #fff;
|
||
transition: background 0.4s ease;
|
||
}
|
||
|
||
.chart-area {
|
||
position: relative;
|
||
height: 170px;
|
||
border-radius: 8px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.y-axis {
|
||
position: absolute;
|
||
left: 0;
|
||
top: 0;
|
||
bottom: 22px;
|
||
width: 52px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: space-between;
|
||
padding: 4px 0;
|
||
z-index: 3;
|
||
}
|
||
|
||
.y-label {
|
||
font-size: 10px;
|
||
padding-left: 2px;
|
||
color: var(--text-muted);
|
||
transition: color 0.4s ease, font-weight 0.3s ease, opacity 0.4s ease;
|
||
opacity: 0.45;
|
||
}
|
||
|
||
.y-label.active {
|
||
font-weight: 600;
|
||
opacity: 1;
|
||
}
|
||
.y-label.active.l-quiet { color: var(--quiet); }
|
||
.y-label.active.l-normal { color: var(--normal); }
|
||
.y-label.active.l-noisy { color: var(--noisy); }
|
||
.y-label.active.l-extreme { color: var(--extreme); }
|
||
|
||
.curve-area {
|
||
position: absolute;
|
||
left: 56px;
|
||
right: 0;
|
||
top: 0;
|
||
bottom: 22px;
|
||
}
|
||
|
||
.curve-area svg { width: 100%; height: 100%; }
|
||
|
||
.x-axis {
|
||
position: absolute;
|
||
left: 56px;
|
||
right: 0;
|
||
bottom: 0;
|
||
height: 22px;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.x-label {
|
||
font-size: 10px;
|
||
color: var(--text-muted);
|
||
}
|
||
|
||
/* ====== 聚光灯渐变核心 ====== */
|
||
.spotlight-layer {
|
||
position: absolute;
|
||
inset: 0;
|
||
pointer-events: none;
|
||
z-index: 1;
|
||
transition: opacity 0.5s ease;
|
||
}
|
||
|
||
.spotlight-glow {
|
||
position: absolute;
|
||
left: 0;
|
||
right: 0;
|
||
height: 55%;
|
||
border-radius: 50%;
|
||
filter: blur(20px);
|
||
opacity: 0;
|
||
transition: top 0.6s cubic-bezier(0.4, 0, 0.2, 1),
|
||
opacity 0.5s ease,
|
||
background 0.5s ease,
|
||
height 0.5s ease;
|
||
}
|
||
|
||
.spotlight-glow.active {
|
||
opacity: 1;
|
||
}
|
||
|
||
/* 分隔线 - 等级边界 */
|
||
.level-divider {
|
||
position: absolute;
|
||
left: 52px;
|
||
right: 0;
|
||
height: 1px;
|
||
background: rgba(255,255,255,0.04);
|
||
z-index: 2;
|
||
}
|
||
|
||
/* ====== 指示条 ====== */
|
||
.indicator-track {
|
||
position: absolute;
|
||
right: 6px;
|
||
top: 8px;
|
||
bottom: 30px;
|
||
width: 3px;
|
||
border-radius: 1.5px;
|
||
background: rgba(255,255,255,0.04);
|
||
z-index: 4;
|
||
}
|
||
|
||
.indicator-dot {
|
||
position: absolute;
|
||
right: 2px;
|
||
width: 11px;
|
||
height: 11px;
|
||
border-radius: 50%;
|
||
transform: translate(50%, -50%);
|
||
z-index: 5;
|
||
transition: top 0.6s cubic-bezier(0.4, 0, 0.2, 1),
|
||
background 0.4s ease,
|
||
box-shadow 0.4s ease;
|
||
}
|
||
|
||
@keyframes glowPulse {
|
||
0%, 100% { opacity: 0.7; }
|
||
50% { opacity: 1; }
|
||
}
|
||
|
||
.indicator-dot::after {
|
||
content: '';
|
||
position: absolute;
|
||
inset: -4px;
|
||
border-radius: 50%;
|
||
background: inherit;
|
||
opacity: 0.25;
|
||
animation: glowPulse 2.5s ease-in-out infinite;
|
||
}
|
||
|
||
/* ====== 条形图 ====== */
|
||
.bar-section {
|
||
margin-top: 10px;
|
||
}
|
||
|
||
.bar-label {
|
||
font-size: 11px;
|
||
color: var(--text-muted);
|
||
margin-bottom: 5px;
|
||
}
|
||
|
||
.bar-track {
|
||
height: 6px;
|
||
border-radius: 3px;
|
||
overflow: hidden;
|
||
display: flex;
|
||
background: rgba(255,255,255,0.03);
|
||
}
|
||
|
||
.bar-seg {
|
||
height: 100%;
|
||
transition: width 0.6s cubic-bezier(0.4, 0, 0.2, 1);
|
||
}
|
||
|
||
.bar-seg.quiet { background: var(--quiet); }
|
||
.bar-seg.normal { background: var(--normal); }
|
||
.bar-seg.noisy { background: var(--noisy); }
|
||
.bar-seg.extreme { background: var(--extreme); }
|
||
|
||
.bar-legend {
|
||
display: flex;
|
||
gap: 10px;
|
||
margin-top: 6px;
|
||
}
|
||
|
||
.bar-legend-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 3px;
|
||
font-size: 10px;
|
||
color: var(--text-muted);
|
||
}
|
||
|
||
.bar-legend-dot {
|
||
width: 5px;
|
||
height: 5px;
|
||
border-radius: 50%;
|
||
}
|
||
|
||
/* ====== Baseline ====== */
|
||
.baseline-bands {
|
||
position: absolute;
|
||
inset: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
z-index: 0;
|
||
}
|
||
|
||
.baseline-band { flex: 1; }
|
||
.baseline-band.extreme { background: rgba(239,68,68,0.09); }
|
||
.baseline-band.noisy { background: rgba(245,158,11,0.08); }
|
||
.baseline-band.normal { background: rgba(96,165,250,0.08); }
|
||
.baseline-band.quiet { background: rgba(52,211,153,0.09); }
|
||
|
||
/* ====== 控制面板 ====== */
|
||
.controls {
|
||
max-width: 1200px;
|
||
margin: 0 auto 32px;
|
||
padding: 20px 24px;
|
||
background: var(--surface);
|
||
border: 1px solid var(--border);
|
||
border-radius: var(--radius-component);
|
||
}
|
||
|
||
.controls-title {
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
margin-bottom: 16px;
|
||
color: var(--text-secondary);
|
||
}
|
||
|
||
.controls-row {
|
||
display: flex;
|
||
gap: 32px;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.control-group {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
}
|
||
|
||
.control-label {
|
||
font-size: 12px;
|
||
color: var(--text-muted);
|
||
}
|
||
|
||
.control-buttons {
|
||
display: flex;
|
||
gap: 6px;
|
||
}
|
||
|
||
.control-btn {
|
||
padding: 6px 14px;
|
||
border-radius: 8px;
|
||
border: 1px solid var(--border);
|
||
background: var(--surface-raised);
|
||
color: var(--text-secondary);
|
||
font-size: 12px;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
font-family: inherit;
|
||
}
|
||
|
||
.control-btn:hover {
|
||
background: rgba(255,255,255,0.08);
|
||
color: var(--text-primary);
|
||
}
|
||
|
||
.control-btn.active-quiet { border-color: rgba(52,211,153,0.4); background: rgba(52,211,153,0.15); color: var(--quiet); }
|
||
.control-btn.active-normal { border-color: rgba(96,165,250,0.4); background: rgba(96,165,250,0.15); color: var(--normal); }
|
||
.control-btn.active-noisy { border-color: rgba(245,158,11,0.4); background: rgba(245,158,11,0.15); color: var(--noisy); }
|
||
.control-btn.active-extreme { border-color: rgba(239,68,68,0.4); background: rgba(239,68,68,0.15); color: var(--extreme); }
|
||
|
||
/* ====== 对比标注 ====== */
|
||
.comparison-note {
|
||
max-width: 1200px;
|
||
margin: 0 auto 40px;
|
||
padding: 16px 20px;
|
||
background: rgba(96,165,250,0.06);
|
||
border: 1px solid rgba(96,165,250,0.12);
|
||
border-radius: 12px;
|
||
font-size: 13px;
|
||
color: var(--text-secondary);
|
||
line-height: 1.7;
|
||
}
|
||
|
||
.comparison-note strong {
|
||
color: var(--text-primary);
|
||
}
|
||
|
||
.comparison-note .highlight {
|
||
padding: 1px 6px;
|
||
border-radius: 4px;
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.comparison-note .hl-quiet { background: rgba(52,211,153,0.15); color: var(--quiet); }
|
||
.comparison-note .hl-normal { background: rgba(96,165,250,0.15); color: var(--normal); }
|
||
.comparison-note .hl-noisy { background: rgba(245,158,11,0.15); color: var(--noisy); }
|
||
.comparison-note .hl-extreme { background: rgba(239,68,68,0.15); color: var(--extreme); }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<div class="page-header">
|
||
<h1>噪音等级组件改造 · 聚光灯方案</h1>
|
||
<p>StudyNoiseDistributionWidget 视觉升级 Mockup v2 — 局部渐变聚焦,而非全图渐变</p>
|
||
</div>
|
||
|
||
<div class="comparison-note">
|
||
<strong>设计理念:</strong>不是整张图都铺满渐变色带,而是只在<strong>当前等级附近</strong>产生一个柔和的聚光灯光晕。
|
||
其余区域保持低调暗淡,让用户视线自然聚焦到当前所处等级。
|
||
当等级切换时,光晕平滑滑动到新位置,颜色同步过渡。
|
||
<br><br>
|
||
<span class="highlight hl-quiet">Quiet 安静</span> →
|
||
<span class="highlight hl-normal">Normal 正常</span> →
|
||
<span class="highlight hl-noisy">Noisy 嘈杂</span> →
|
||
<span class="highlight hl-extreme">Extreme 极端</span>
|
||
点击下方按钮切换等级,观察光晕移动效果
|
||
</div>
|
||
|
||
<!-- 控制面板 -->
|
||
<div class="controls">
|
||
<div class="controls-title">🎛 模拟噪音等级切换</div>
|
||
<div class="controls-row">
|
||
<div class="control-group">
|
||
<div class="control-label">当前等级</div>
|
||
<div class="control-buttons" id="level-buttons">
|
||
<button class="control-btn" data-level="quiet" onclick="setLevel('quiet')">Quiet 安静</button>
|
||
<button class="control-btn active-normal" data-level="normal" onclick="setLevel('normal')">Normal 正常</button>
|
||
<button class="control-btn" data-level="noisy" onclick="setLevel('noisy')">Noisy 嘈杂</button>
|
||
<button class="control-btn" data-level="extreme" onclick="setLevel('extreme')">Extreme 极端</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 当前实现 Baseline -->
|
||
<div class="section">
|
||
<div class="section-title">当前实现 (Baseline)</div>
|
||
<div class="section-desc">四色硬切色带,各等级区域均匀着色,没有视觉焦点。用户需要主动寻找"我在哪个等级"。</div>
|
||
<div class="mockup-row">
|
||
<div class="widget-card">
|
||
<div class="chart-header">
|
||
<span class="chart-title">噪音等级分布</span>
|
||
<span class="chart-badge" style="background: rgba(47,93,168,0.55);">Realtime</span>
|
||
</div>
|
||
<div class="chart-area">
|
||
<div class="baseline-bands">
|
||
<div class="baseline-band extreme"></div>
|
||
<div class="baseline-band noisy"></div>
|
||
<div class="baseline-band normal"></div>
|
||
<div class="baseline-band quiet"></div>
|
||
</div>
|
||
<div class="y-axis">
|
||
<span class="y-label">Extreme</span>
|
||
<span class="y-label">Noisy</span>
|
||
<span class="y-label">Normal</span>
|
||
<span class="y-label">Quiet</span>
|
||
</div>
|
||
<div class="curve-area">
|
||
<svg viewBox="0 0 280 140" preserveAspectRatio="none">
|
||
<defs>
|
||
<linearGradient id="fill0" x1="0" y1="0" x2="0" y2="1">
|
||
<stop offset="0%" stop-color="#EF4444" stop-opacity="0.34"/>
|
||
<stop offset="28%" stop-color="#F59E0B" stop-opacity="0.34"/>
|
||
<stop offset="62%" stop-color="#60A5FA" stop-opacity="0.34"/>
|
||
<stop offset="100%" stop-color="#34D399" stop-opacity="0.34"/>
|
||
</linearGradient>
|
||
</defs>
|
||
<path d="M0,95 C20,92 40,78 60,72 C80,66 100,56 120,50 C140,44 160,52 180,48 C200,44 220,38 240,42 C260,46 280,54 280,52 L280,140 L0,140 Z" fill="url(#fill0)"/>
|
||
<path d="M0,95 C20,92 40,78 60,72 C80,66 100,56 120,50 C140,44 160,52 180,48 C200,44 220,38 240,42 C260,46 280,54 280,52" fill="none" stroke="#52AEEA" stroke-width="1.5"/>
|
||
<circle cx="280" cy="52" r="4" fill="#fff" stroke="#52AEEA" stroke-width="1.2"/>
|
||
<circle cx="280" cy="52" r="10" fill="rgba(82,174,234,0.2)"/>
|
||
</svg>
|
||
</div>
|
||
<div class="x-axis">
|
||
<span class="x-label">-12s</span>
|
||
<span class="x-label">-6s</span>
|
||
<span class="x-label">Now</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 聚光灯方案 -->
|
||
<div class="section">
|
||
<div class="section-title">聚光灯方案 — 局部渐变聚焦</div>
|
||
<div class="section-desc">只在当前等级附近产生柔和光晕,其余区域保持暗淡。等级切换时光晕平滑滑动,Y轴标签联动高亮,右侧指示条标注当前位置。下方条形图展示分布占比。</div>
|
||
<div class="mockup-row">
|
||
<!-- 标准尺寸 -->
|
||
<div class="widget-card">
|
||
<div class="widget-label">推荐</div>
|
||
<div class="chart-header">
|
||
<span class="chart-title">噪音等级分布</span>
|
||
<span class="chart-badge" id="spot-badge" style="background: rgba(47,93,168,0.55);">Realtime</span>
|
||
</div>
|
||
<div class="chart-area" id="spot-chart">
|
||
<!-- 极淡的分隔线标记等级边界 -->
|
||
<div class="level-divider" style="top: 25%;"></div>
|
||
<div class="level-divider" style="top: 50%;"></div>
|
||
<div class="level-divider" style="top: 75%;"></div>
|
||
|
||
<!-- 聚光灯光晕层 -->
|
||
<div class="spotlight-layer">
|
||
<div class="spotlight-glow active" id="spot-glow"
|
||
style="top: 35%; height: 50%; background: radial-gradient(ellipse at center, rgba(96,165,250,0.14) 0%, rgba(96,165,250,0.04) 50%, transparent 80%);">
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Y轴 -->
|
||
<div class="y-axis">
|
||
<span class="y-label l-extreme" id="sy-extreme">Extreme</span>
|
||
<span class="y-label l-noisy" id="sy-noisy">Noisy</span>
|
||
<span class="y-label l-normal active" id="sy-normal">Normal</span>
|
||
<span class="y-label l-quiet" id="sy-quiet">Quiet</span>
|
||
</div>
|
||
|
||
<!-- 指示条 -->
|
||
<div class="indicator-track"></div>
|
||
<div class="indicator-dot" id="spot-dot" style="top: 55%; background: var(--normal); box-shadow: 0 0 8px rgba(96,165,250,0.5);"></div>
|
||
|
||
<!-- 曲线 -->
|
||
<div class="curve-area" style="right: 18px;">
|
||
<svg viewBox="0 0 270 140" preserveAspectRatio="none">
|
||
<defs>
|
||
<linearGradient id="fillSpot" x1="0" y1="0" x2="0" y2="1">
|
||
<stop offset="0%" stop-color="#EF4444" stop-opacity="0.28"/>
|
||
<stop offset="28%" stop-color="#F59E0B" stop-opacity="0.28"/>
|
||
<stop offset="62%" stop-color="#60A5FA" stop-opacity="0.28"/>
|
||
<stop offset="100%" stop-color="#34D399" stop-opacity="0.28"/>
|
||
</linearGradient>
|
||
</defs>
|
||
<path d="M0,95 C20,92 40,78 60,72 C80,66 100,56 120,50 C140,44 160,52 180,48 C200,44 220,38 240,42 C260,46 270,54 270,52 L270,140 L0,140 Z" fill="url(#fillSpot)"/>
|
||
<path d="M0,95 C20,92 40,78 60,72 C80,66 100,56 120,50 C140,44 160,52 180,48 C200,44 220,38 240,42 C260,46 270,54 270,52" fill="none" stroke="#8BE8FF" stroke-width="1.8"/>
|
||
<circle cx="270" cy="52" r="3.5" fill="#fff" stroke="#52D6FF" stroke-width="1.2" id="spot-latest-dot"/>
|
||
<circle cx="270" cy="52" r="9" fill="rgba(82,214,255,0.12)" id="spot-latest-glow"/>
|
||
</svg>
|
||
</div>
|
||
|
||
<div class="x-axis">
|
||
<span class="x-label">-12s</span>
|
||
<span class="x-label">-6s</span>
|
||
<span class="x-label">Now</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 条形图 -->
|
||
<div class="bar-section">
|
||
<div class="bar-label">等级分布</div>
|
||
<div class="bar-track" id="spot-bar">
|
||
<div class="bar-seg quiet" style="width: 35%;"></div>
|
||
<div class="bar-seg normal" style="width: 40%;"></div>
|
||
<div class="bar-seg noisy" style="width: 18%;"></div>
|
||
<div class="bar-seg extreme" style="width: 7%;"></div>
|
||
</div>
|
||
<div class="bar-legend" id="spot-legend">
|
||
<span class="bar-legend-item"><span class="bar-legend-dot" style="background:var(--quiet);"></span>安静 35%</span>
|
||
<span class="bar-legend-item"><span class="bar-legend-dot" style="background:var(--normal);"></span>正常 40%</span>
|
||
<span class="bar-legend-item"><span class="bar-legend-dot" style="background:var(--noisy);"></span>嘈杂 18%</span>
|
||
<span class="bar-legend-item"><span class="bar-legend-dot" style="background:var(--extreme);"></span>极端 7%</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 紧凑模式 -->
|
||
<div class="widget-card">
|
||
<div class="widget-label">紧凑模式</div>
|
||
<div class="chart-header">
|
||
<span class="chart-title">噪音等级分布</span>
|
||
<span class="chart-badge" id="spot-badge-c" style="background: rgba(47,93,168,0.55);">Session</span>
|
||
</div>
|
||
<div class="chart-area" style="height: 120px;" id="spot-chart-c">
|
||
<div class="level-divider" style="top: 25%;"></div>
|
||
<div class="level-divider" style="top: 50%;"></div>
|
||
<div class="level-divider" style="top: 75%;"></div>
|
||
|
||
<div class="spotlight-layer">
|
||
<div class="spotlight-glow active" id="spot-glow-c"
|
||
style="top: 35%; height: 50%; background: radial-gradient(ellipse at center, rgba(96,165,250,0.14) 0%, rgba(96,165,250,0.04) 50%, transparent 80%);">
|
||
</div>
|
||
</div>
|
||
|
||
<div class="y-axis" style="width: 36px;">
|
||
<span class="y-label l-extreme" id="syc-extreme" style="font-size:8px;">Ext</span>
|
||
<span class="y-label l-noisy" id="syc-noisy" style="font-size:8px;">Noisy</span>
|
||
<span class="y-label l-normal active" id="syc-normal" style="font-size:8px;">Norm</span>
|
||
<span class="y-label l-quiet" id="syc-quiet" style="font-size:8px;">Quiet</span>
|
||
</div>
|
||
|
||
<div class="indicator-track" style="right: 4px; width: 2px;"></div>
|
||
<div class="indicator-dot" id="spot-dot-c" style="right: 0; width: 8px; height: 8px; top: 55%; background: var(--normal); box-shadow: 0 0 6px rgba(96,165,250,0.4);"></div>
|
||
|
||
<div class="curve-area" style="left: 40px; right: 14px;">
|
||
<svg viewBox="0 0 260 95" preserveAspectRatio="none">
|
||
<path d="M0,65 C30,60 60,50 90,45 C120,40 150,48 180,42 C210,36 240,40 260,38 L260,95 L0,95 Z" fill="url(#fillSpot)" opacity="0.7"/>
|
||
<path d="M0,65 C30,60 60,50 90,45 C120,40 150,48 180,42 C210,36 240,40 260,38" fill="none" stroke="#8BE8FF" stroke-width="1.4"/>
|
||
</svg>
|
||
</div>
|
||
|
||
<div class="x-axis" style="left: 40px;">
|
||
<span class="x-label">-12s</span>
|
||
<span class="x-label">-6s</span>
|
||
<span class="x-label">Now</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="bar-section">
|
||
<div class="bar-track" style="height: 4px; border-radius: 2px;">
|
||
<div class="bar-seg quiet" style="width: 55%;"></div>
|
||
<div class="bar-seg normal" style="width: 30%;"></div>
|
||
<div class="bar-seg noisy" style="width: 10%;"></div>
|
||
<div class="bar-seg extreme" style="width: 5%;"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 四种等级状态展示 -->
|
||
<div class="section">
|
||
<div class="section-title">四种等级状态一览</div>
|
||
<div class="section-desc">同时展示四个等级的聚光灯效果,可以直观对比光晕位置、颜色和强度的差异。</div>
|
||
<div class="mockup-row" style="grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));">
|
||
|
||
<div class="widget-card" style="padding: 12px;">
|
||
<div style="font-size: 12px; font-weight: 600; color: var(--quiet); margin-bottom: 8px;">● Quiet 安静</div>
|
||
<div class="chart-area" style="height: 100px;">
|
||
<div class="level-divider" style="top: 25%;"></div>
|
||
<div class="level-divider" style="top: 50%;"></div>
|
||
<div class="level-divider" style="top: 75%;"></div>
|
||
<div class="spotlight-layer">
|
||
<div class="spotlight-glow active" style="top: 60%; height: 45%; background: radial-gradient(ellipse at center, rgba(52,211,153,0.16) 0%, rgba(52,211,153,0.04) 50%, transparent 80%);"></div>
|
||
</div>
|
||
<div class="y-axis" style="width: 32px; bottom: 16px;">
|
||
<span class="y-label" style="font-size:8px;">Ext</span>
|
||
<span class="y-label" style="font-size:8px;">Noisy</span>
|
||
<span class="y-label" style="font-size:8px;">Norm</span>
|
||
<span class="y-label l-quiet active" style="font-size:8px;">Quiet</span>
|
||
</div>
|
||
<div class="indicator-track" style="right: 3px; width: 2px;"></div>
|
||
<div class="indicator-dot" style="right: 0; width: 7px; height: 7px; top: 82%; background: var(--quiet); box-shadow: 0 0 6px rgba(52,211,153,0.5);"></div>
|
||
<div class="curve-area" style="left: 36px; right: 12px;">
|
||
<svg viewBox="0 0 220 80" preserveAspectRatio="none">
|
||
<path d="M0,55 C20,52 40,58 60,60 C80,62 100,65 120,63 C140,61 160,58 180,60 C200,62 220,64 220,63 L220,80 L0,80 Z" fill="url(#fillSpot)" opacity="0.5"/>
|
||
<path d="M0,55 C20,52 40,58 60,60 C80,62 100,65 120,63 C140,61 160,58 180,60 C200,62 220,64 220,63" fill="none" stroke="#8BE8FF" stroke-width="1.2"/>
|
||
<circle cx="220" cy="63" r="2.5" fill="#34D399" stroke="#fff" stroke-width="0.8"/>
|
||
</svg>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="widget-card" style="padding: 12px;">
|
||
<div style="font-size: 12px; font-weight: 600; color: var(--normal); margin-bottom: 8px;">● Normal 正常</div>
|
||
<div class="chart-area" style="height: 100px;">
|
||
<div class="level-divider" style="top: 25%;"></div>
|
||
<div class="level-divider" style="top: 50%;"></div>
|
||
<div class="level-divider" style="top: 75%;"></div>
|
||
<div class="spotlight-layer">
|
||
<div class="spotlight-glow active" style="top: 35%; height: 50%; background: radial-gradient(ellipse at center, rgba(96,165,250,0.14) 0%, rgba(96,165,250,0.04) 50%, transparent 80%);"></div>
|
||
</div>
|
||
<div class="y-axis" style="width: 32px; bottom: 16px;">
|
||
<span class="y-label" style="font-size:8px;">Ext</span>
|
||
<span class="y-label" style="font-size:8px;">Noisy</span>
|
||
<span class="y-label l-normal active" style="font-size:8px;">Norm</span>
|
||
<span class="y-label" style="font-size:8px;">Quiet</span>
|
||
</div>
|
||
<div class="indicator-track" style="right: 3px; width: 2px;"></div>
|
||
<div class="indicator-dot" style="right: 0; width: 7px; height: 7px; top: 55%; background: var(--normal); box-shadow: 0 0 6px rgba(96,165,250,0.5);"></div>
|
||
<div class="curve-area" style="left: 36px; right: 12px;">
|
||
<svg viewBox="0 0 220 80" preserveAspectRatio="none">
|
||
<path d="M0,50 C20,48 40,42 60,38 C80,34 100,40 120,36 C140,32 160,38 180,35 C200,32 220,38 220,36 L220,80 L0,80 Z" fill="url(#fillSpot)" opacity="0.5"/>
|
||
<path d="M0,50 C20,48 40,42 60,38 C80,34 100,40 120,36 C140,32 160,38 180,35 C200,32 220,38 220,36" fill="none" stroke="#8BE8FF" stroke-width="1.2"/>
|
||
<circle cx="220" cy="36" r="2.5" fill="#60A5FA" stroke="#fff" stroke-width="0.8"/>
|
||
</svg>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="widget-card" style="padding: 12px;">
|
||
<div style="font-size: 12px; font-weight: 600; color: var(--noisy); margin-bottom: 8px;">● Noisy 嘈杂</div>
|
||
<div class="chart-area" style="height: 100px;">
|
||
<div class="level-divider" style="top: 25%;"></div>
|
||
<div class="level-divider" style="top: 50%;"></div>
|
||
<div class="level-divider" style="top: 75%;"></div>
|
||
<div class="spotlight-layer">
|
||
<div class="spotlight-glow active" style="top: 15%; height: 45%; background: radial-gradient(ellipse at center, rgba(245,158,11,0.16) 0%, rgba(245,158,11,0.04) 50%, transparent 80%);"></div>
|
||
</div>
|
||
<div class="y-axis" style="width: 32px; bottom: 16px;">
|
||
<span class="y-label" style="font-size:8px;">Ext</span>
|
||
<span class="y-label l-noisy active" style="font-size:8px;">Noisy</span>
|
||
<span class="y-label" style="font-size:8px;">Norm</span>
|
||
<span class="y-label" style="font-size:8px;">Quiet</span>
|
||
</div>
|
||
<div class="indicator-track" style="right: 3px; width: 2px;"></div>
|
||
<div class="indicator-dot" style="right: 0; width: 7px; height: 7px; top: 30%; background: var(--noisy); box-shadow: 0 0 6px rgba(245,158,11,0.5);"></div>
|
||
<div class="curve-area" style="left: 36px; right: 12px;">
|
||
<svg viewBox="0 0 220 80" preserveAspectRatio="none">
|
||
<path d="M0,30 C20,28 40,22 60,18 C80,14 100,20 120,16 C140,12 160,18 180,15 C200,12 220,16 220,14 L220,80 L0,80 Z" fill="url(#fillSpot)" opacity="0.5"/>
|
||
<path d="M0,30 C20,28 40,22 60,18 C80,14 100,20 120,16 C140,12 160,18 180,15 C200,12 220,16 220,14" fill="none" stroke="#FF8BE8" stroke-width="1.2"/>
|
||
<circle cx="220" cy="14" r="2.5" fill="#F59E0B" stroke="#fff" stroke-width="0.8"/>
|
||
</svg>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="widget-card" style="padding: 12px;">
|
||
<div style="font-size: 12px; font-weight: 600; color: var(--extreme); margin-bottom: 8px;">● Extreme 极端</div>
|
||
<div class="chart-area" style="height: 100px;">
|
||
<div class="level-divider" style="top: 25%;"></div>
|
||
<div class="level-divider" style="top: 50%;"></div>
|
||
<div class="level-divider" style="top: 75%;"></div>
|
||
<div class="spotlight-layer">
|
||
<div class="spotlight-glow active" style="top: 0%; height: 40%; background: radial-gradient(ellipse at center, rgba(239,68,68,0.18) 0%, rgba(239,68,68,0.05) 50%, transparent 80%); animation: extremePulse 2s ease-in-out infinite;"></div>
|
||
</div>
|
||
<div class="y-axis" style="width: 32px; bottom: 16px;">
|
||
<span class="y-label l-extreme active" style="font-size:8px;">Ext</span>
|
||
<span class="y-label" style="font-size:8px;">Noisy</span>
|
||
<span class="y-label" style="font-size:8px;">Norm</span>
|
||
<span class="y-label" style="font-size:8px;">Quiet</span>
|
||
</div>
|
||
<div class="indicator-track" style="right: 3px; width: 2px;"></div>
|
||
<div class="indicator-dot" style="right: 0; width: 7px; height: 7px; top: 10%; background: var(--extreme); box-shadow: 0 0 8px rgba(239,68,68,0.6);"></div>
|
||
<div class="curve-area" style="left: 36px; right: 12px;">
|
||
<svg viewBox="0 0 220 80" preserveAspectRatio="none">
|
||
<path d="M0,14 C20,12 40,8 60,6 C80,4 100,10 120,7 C140,4 160,8 180,5 C200,2 220,6 220,4 L220,80 L0,80 Z" fill="url(#fillSpot)" opacity="0.5"/>
|
||
<path d="M0,14 C20,12 40,8 60,6 C80,4 100,10 120,7 C140,4 160,8 180,5 C200,2 220,6 220,4" fill="none" stroke="#FF8BE8" stroke-width="1.2"/>
|
||
<circle cx="220" cy="4" r="2.5" fill="#EF4444" stroke="#fff" stroke-width="0.8"/>
|
||
</svg>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
@keyframes extremePulse {
|
||
0%, 100% { opacity: 0.8; }
|
||
50% { opacity: 1; }
|
||
}
|
||
|
||
<script>
|
||
const levels = {
|
||
quiet: {
|
||
color: '#34D399',
|
||
cssVar: 'var(--quiet)',
|
||
label: '安静',
|
||
glowTop: '58%',
|
||
glowHeight: '45%',
|
||
glowBg: 'radial-gradient(ellipse at center, rgba(52,211,153,0.16) 0%, rgba(52,211,153,0.04) 50%, transparent 80%)',
|
||
dotTop: '80%',
|
||
dotShadow: '0 0 8px rgba(52,211,153,0.5)',
|
||
badgeBg: 'rgba(15,107,73,0.8)',
|
||
barWidths: [55, 30, 10, 5],
|
||
barLegends: ['安静 55%', '正常 30%', '嘈杂 10%', '极端 5%'],
|
||
latestDotColor: '#34D399',
|
||
latestGlowColor: 'rgba(52,211,153,0.12)',
|
||
},
|
||
normal: {
|
||
color: '#60A5FA',
|
||
cssVar: 'var(--normal)',
|
||
label: '正常',
|
||
glowTop: '30%',
|
||
glowHeight: '50%',
|
||
glowBg: 'radial-gradient(ellipse at center, rgba(96,165,250,0.14) 0%, rgba(96,165,250,0.04) 50%, transparent 80%)',
|
||
dotTop: '55%',
|
||
dotShadow: '0 0 8px rgba(96,165,250,0.5)',
|
||
badgeBg: 'rgba(47,93,168,0.55)',
|
||
barWidths: [35, 40, 18, 7],
|
||
barLegends: ['安静 35%', '正常 40%', '嘈杂 18%', '极端 7%'],
|
||
latestDotColor: '#60A5FA',
|
||
latestGlowColor: 'rgba(96,165,250,0.12)',
|
||
},
|
||
noisy: {
|
||
color: '#F59E0B',
|
||
cssVar: 'var(--noisy)',
|
||
label: '嘈杂',
|
||
glowTop: '10%',
|
||
glowHeight: '45%',
|
||
glowBg: 'radial-gradient(ellipse at center, rgba(245,158,11,0.16) 0%, rgba(245,158,11,0.04) 50%, transparent 80%)',
|
||
dotTop: '28%',
|
||
dotShadow: '0 0 8px rgba(245,158,11,0.5)',
|
||
badgeBg: 'rgba(128,80,24,0.8)',
|
||
barWidths: [15, 25, 42, 18],
|
||
barLegends: ['安静 15%', '正常 25%', '嘈杂 42%', '极端 18%'],
|
||
latestDotColor: '#F59E0B',
|
||
latestGlowColor: 'rgba(245,158,11,0.12)',
|
||
},
|
||
extreme: {
|
||
color: '#EF4444',
|
||
cssVar: 'var(--extreme)',
|
||
label: '极端',
|
||
glowTop: '-5%',
|
||
glowHeight: '40%',
|
||
glowBg: 'radial-gradient(ellipse at center, rgba(239,68,68,0.18) 0%, rgba(239,68,68,0.05) 50%, transparent 80%)',
|
||
dotTop: '10%',
|
||
dotShadow: '0 0 10px rgba(239,68,68,0.6)',
|
||
badgeBg: 'rgba(141,42,58,0.8)',
|
||
barWidths: [5, 12, 30, 53],
|
||
barLegends: ['安静 5%', '正常 12%', '嘈杂 30%', '极端 53%'],
|
||
latestDotColor: '#EF4444',
|
||
latestGlowColor: 'rgba(239,68,68,0.15)',
|
||
}
|
||
};
|
||
|
||
function setLevel(level) {
|
||
const cfg = levels[level];
|
||
|
||
document.querySelectorAll('#level-buttons .control-btn').forEach(b => {
|
||
b.className = 'control-btn';
|
||
});
|
||
const activeBtn = document.querySelector(`#level-buttons .control-btn[data-level="${level}"]`);
|
||
if (activeBtn) activeBtn.classList.add(`active-${level}`);
|
||
|
||
['spot', 'spot-c'].forEach(prefix => {
|
||
const glow = document.getElementById(prefix === 'spot' ? 'spot-glow' : 'spot-glow-c');
|
||
const dot = document.getElementById(prefix === 'spot' ? 'spot-dot' : 'spot-dot-c');
|
||
if (glow) {
|
||
glow.style.top = cfg.glowTop;
|
||
glow.style.height = cfg.glowHeight;
|
||
glow.style.background = cfg.glowBg;
|
||
if (level === 'extreme') {
|
||
glow.style.animation = 'extremePulse 2s ease-in-out infinite';
|
||
} else {
|
||
glow.style.animation = 'none';
|
||
}
|
||
}
|
||
if (dot) {
|
||
dot.style.top = cfg.dotTop;
|
||
dot.style.background = cfg.color;
|
||
dot.style.boxShadow = cfg.dotShadow;
|
||
}
|
||
});
|
||
|
||
['extreme', 'noisy', 'normal', 'quiet'].forEach(l => {
|
||
const isCurrent = l === level;
|
||
['sy', 'syc'].forEach(prefix => {
|
||
const el = document.getElementById(`${prefix}-${l}`);
|
||
if (el) el.classList.toggle('active', isCurrent);
|
||
});
|
||
});
|
||
|
||
const badge = document.getElementById('spot-badge');
|
||
const badgeC = document.getElementById('spot-badge-c');
|
||
[badge, badgeC].forEach(b => {
|
||
if (b) { b.textContent = cfg.label; b.style.background = cfg.badgeBg; }
|
||
});
|
||
|
||
const latestDot = document.getElementById('spot-latest-dot');
|
||
const latestGlow = document.getElementById('spot-latest-glow');
|
||
if (latestDot) latestDot.setAttribute('fill', cfg.latestDotColor);
|
||
if (latestGlow) latestGlow.setAttribute('fill', cfg.latestGlowColor);
|
||
|
||
const barTrack = document.getElementById('spot-bar');
|
||
if (barTrack) {
|
||
const segs = barTrack.querySelectorAll('.bar-seg');
|
||
const classes = ['quiet', 'normal', 'noisy', 'extreme'];
|
||
cfg.barWidths.forEach((w, i) => { if (segs[i]) segs[i].style.width = w + '%'; });
|
||
}
|
||
|
||
const legend = document.getElementById('spot-legend');
|
||
if (legend) {
|
||
const items = legend.querySelectorAll('.bar-legend-item');
|
||
cfg.barLegends.forEach((text, i) => { if (items[i]) items[i].childNodes[1].textContent = ' ' + text; });
|
||
}
|
||
}
|
||
|
||
setLevel('normal');
|
||
</script>
|
||
|
||
</body>
|
||
</html>
|