feat(display): falling snow in three depth layers with wind, mouse and bullet time; appearance panel; dark glass tinted from the background [CI SKIP]

This commit is contained in:
justme committed 2026-10-09 03:54:49 +08:00
1 parent f8e8bb8acb
commit e49990776b
10 files changed
+1003 -7

No files matched your search

+275
View File
@@ -1012,6 +1012,8 @@
background-size: cover;
background-position: center;
pointer-events: none;
/* 在预先模糊的基础上再模糊多少,右下角「外观」里调;默认 none */
filter: var(--bg-filter, none);
}
:root {
@@ -1382,3 +1384,276 @@ input {
height: 20px;
flex: none;
}
/* 天气(雪、雨):铺满窗口,和背景图同一层(在背景图后面画、所有内容的下面),不接收鼠标;所以卡片的毛玻璃会把它一起模糊,不会挡住任何组件 */
.weather-layer {
position: fixed;
inset: 0;
width: 100%;
height: 100%;
z-index: -1;
pointer-events: none;
/* 滤色:粒子只会把背后的图提亮,边缘羽化的地方自然融进图里 */
mix-blend-mode: screen;
}
@media (max-width: 640px), ((pointer: coarse) and (max-width: 900px)) {
.weather-layer {
display: none;
}
}
/* ── 右下角的「外观」:玻璃小按钮,点开是带分类的设置面板(亚克力同一套变量);手机上不画雪也不能晃背景,这个也不显示 ── */
.appearance {
position: fixed;
right: 16px;
bottom: 16px;
z-index: 35;
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 10px;
}
.appearance-fab {
display: grid;
place-items: center;
width: 40px;
height: 40px;
padding: 0;
border: 0;
border-radius: 50%;
color: var(--app-ink-muted);
background: var(--glass-bg);
backdrop-filter: var(--glass-filter);
box-shadow:
inset 0 0 0 1px var(--glass-ring),
inset 0 1px 0 var(--glass-top),
var(--glass-drop);
cursor: pointer;
transition: color 0.15s;
}
.appearance-fab svg {
transition: transform 0.35s cubic-bezier(0.34, 1.4, 0.64, 1);
}
.appearance-fab:hover,
.appearance-fab[aria-expanded='true'] {
color: var(--app-ink);
}
.appearance-fab[aria-expanded='true'] svg {
transform: rotate(60deg);
}
/* 面板:从按钮的位置(右下角)展开 / 收起;一直在 DOM 里,用 data-open 切换,这样收起的时候也有动画 */
.appearance-panel {
width: 300px;
padding: 14px 16px 16px;
border-radius: var(--r-card);
background: var(--glass-bg);
backdrop-filter: var(--glass-filter);
box-shadow:
inset 0 0 0 1px var(--glass-ring),
inset 0 1px 0 var(--glass-top),
var(--glass-drop);
color: var(--app-ink);
font-size: 13px;
transform-origin: bottom right;
transition:
opacity 0.22s ease,
transform 0.28s cubic-bezier(0.2, 0.9, 0.3, 1.15),
visibility 0s linear 0.28s;
}
.appearance-panel[data-open='false'] {
opacity: 0;
visibility: hidden;
transform: translateY(10px) scale(0.92);
pointer-events: none;
}
.appearance-panel[data-open='true'] {
opacity: 1;
visibility: visible;
transform: none;
transition-delay: 0s;
}
.appearance-tabs {
position: relative;
display: grid;
grid-template-columns: 1fr 1fr;
margin-bottom: 12px;
padding: 3px;
border-radius: var(--r-pill);
background: color-mix(in srgb, var(--app-ink) 8%, transparent);
}
/* 选中的那一格的底:随选中的分类滑过去 */
.appearance-tabs::before {
content: '';
position: absolute;
top: 3px;
bottom: 3px;
left: 3px;
width: calc(50% - 3px);
border-radius: var(--r-pill);
background: var(--app-card);
box-shadow: 0 1px 3px color-mix(in srgb, var(--glass-shade) 25%, transparent);
transform: translateX(var(--tab-offset, 0));
transition: transform 0.28s cubic-bezier(0.3, 0.9, 0.3, 1.1);
}
.appearance-tabs[data-tab='background']::before {
--tab-offset: 100%;
}
.appearance-tabs button {
position: relative;
padding: 5px 0;
border: 0;
border-radius: var(--r-pill);
color: var(--app-ink-muted);
background: none;
cursor: pointer;
transition: color 0.2s;
}
.appearance-tabs button[aria-selected='true'] {
color: var(--app-ink);
font-weight: 600;
}
/* 切换分类时内容淡入、轻轻上浮;每一行再错开一点 */
.appearance-body {
display: grid;
gap: 9px;
}
.appearance-body > * {
animation: appearance-in 0.32s ease both;
}
.appearance-body > :nth-child(2) { animation-delay: 0.03s; }
.appearance-body > :nth-child(3) { animation-delay: 0.06s; }
.appearance-body > :nth-child(4) { animation-delay: 0.09s; }
.appearance-body > :nth-child(5) { animation-delay: 0.12s; }
.appearance-body > :nth-child(6) { animation-delay: 0.15s; }
.appearance-body > :nth-child(7) { animation-delay: 0.18s; }
.appearance-body > :nth-child(8) { animation-delay: 0.21s; }
.appearance-body > :nth-child(9) { animation-delay: 0.24s; }
@keyframes appearance-in {
from {
opacity: 0;
transform: translateY(6px);
}
}
.appearance-body[data-off='true'] .appearance-row:not(.appearance-master) {
opacity: 0.45;
pointer-events: none;
}
.appearance-row {
display: grid;
grid-template-columns: 52px 1fr 44px;
align-items: center;
gap: 8px;
transition: opacity 0.2s;
}
.appearance-row-switch {
grid-template-columns: 1fr auto;
}
.appearance-row output {
text-align: right;
color: var(--app-ink-muted);
font-variant-numeric: tabular-nums;
}
.appearance-row input[type='range'] {
width: 100%;
accent-color: var(--app-primary);
}
.appearance-switch {
position: relative;
display: inline-block;
width: 34px;
height: 20px;
}
.appearance-switch input {
position: absolute;
inset: 0;
margin: 0;
opacity: 0;
cursor: pointer;
}
.appearance-switch span {
position: absolute;
inset: 0;
border-radius: 10px;
background: color-mix(in srgb, var(--app-ink) 22%, transparent);
pointer-events: none;
transition: background 0.2s;
}
.appearance-switch span::after {
content: '';
position: absolute;
top: 2px;
left: 2px;
width: 16px;
height: 16px;
border-radius: 50%;
background: var(--app-card);
transition: transform 0.25s cubic-bezier(0.34, 1.4, 0.64, 1);
}
.appearance-switch input:checked + span {
background: var(--app-primary);
}
.appearance-switch input:checked + span::after {
transform: translateX(14px);
}
.appearance-reset {
margin-top: 12px;
width: 100%;
padding: 6px 0;
border: 0;
border-radius: var(--r-pill);
color: var(--app-ink-muted);
background: color-mix(in srgb, var(--app-ink) 8%, transparent);
cursor: pointer;
transition: opacity 0.2s;
}
.appearance-reset:disabled {
opacity: 0.4;
cursor: default;
}
@media (prefers-reduced-motion: reduce) {
.appearance-panel,
.appearance-tabs::before,
.appearance-body > *,
.appearance-fab svg,
.appearance-switch span::after {
animation: none;
transition: none;
}
}
@media (max-width: 640px), ((pointer: coarse) and (max-width: 900px)) {
.appearance {
display: none;
}
}