|
| 1 | +/*! |
| 2 | + * Qtine WebUI Motion & Skeleton |
| 3 | + * 依照 mdui 2.x (Material Design 3) 设计规范: |
| 4 | + * - 复用 MD3 emphasized / standard 缓动曲线 |
| 5 | + * - 骨架屏采用 MD3 推荐的脉冲式 shimmer(而非线性进度条) |
| 6 | + * - 跨页过渡使用 View Transitions API,不支持时自动回退 |
| 7 | + * 本文件自托管,mdui CDN 仅提供组件令牌默认值,缺失也能工作。 |
| 8 | + */ |
| 9 | + |
| 10 | +/* ── MD3 / mdui motion 令牌(本地 fallback,避免 CDN 失败时动画丢失)────── */ |
| 11 | +:root{ |
| 12 | + /* 缓动曲线:MD3 motion */ |
| 13 | + --mdui-motion-easing-linear:cubic-bezier(0,0,1,1); |
| 14 | + --mdui-motion-easing-standard:cubic-bezier(.2,0,0,1); |
| 15 | + --mdui-motion-easing-emphasized:cubic-bezier(.3,0,0,1); |
| 16 | + --mdui-motion-easing-emphasized-accelerate:cubic-bezier(.3,0,1,1); |
| 17 | + --mdui-motion-easing-emphasized-decelerate:cubic-bezier(.05,.7,.1,1); |
| 18 | + /* 时长(ms) */ |
| 19 | + --mdui-motion-duration-short1:50ms; |
| 20 | + --mdui-motion-duration-short2:100ms; |
| 21 | + --mdui-motion-duration-short3:150ms; |
| 22 | + --mdui-motion-duration-short4:200ms; |
| 23 | + --mdui-motion-duration-medium1:250ms; |
| 24 | + --mdui-motion-duration-medium2:300ms; |
| 25 | + --mdui-motion-duration-medium3:350ms; |
| 26 | + --mdui-motion-duration-medium4:400ms; |
| 27 | + --mdui-motion-duration-long1:450ms; |
| 28 | + --mdui-motion-duration-long2:500ms; |
| 29 | + /* 骨架屏专用 */ |
| 30 | + --qtine-skel-base:#2B2930; /* = 现有 --sfc */ |
| 31 | + --qtine-skel-hi:#3A3742; /* shimmer 高光 */ |
| 32 | +} |
| 33 | + |
| 34 | +/* ── 页面入场动画(首次加载 / 真正刷新时使用)──────────────────────────── */ |
| 35 | +@keyframes qtine-page-enter{ |
| 36 | + from{opacity:0;transform:translateY(8px)} |
| 37 | + to{opacity:1;transform:translateY(0)} |
| 38 | +} |
| 39 | +.main{ |
| 40 | + animation:qtine-page-enter var(--mdui-motion-duration-medium1) |
| 41 | + var(--mdui-motion-easing-emphasized-decelerate) both; |
| 42 | +} |
| 43 | +#login-page{ |
| 44 | + animation:qtine-page-enter var(--mdui-motion-duration-medium2) |
| 45 | + var(--mdui-motion-easing-emphasized-decelerate) both; |
| 46 | +} |
| 47 | + |
| 48 | +/* ── View Transitions API:SPA 同文档过渡(无白屏)─────────────────────── |
| 49 | + * 不使用 @view-transition{navigation:auto}(那是跨文档 MPA 方案,会导致白屏)。 |
| 50 | + * 这里配合 JS 的 startViewTransition(callback) 同文档替换 <body>, |
| 51 | + * 旧 root 快照在新内容就绪前始终可见,彻底消除白屏闪烁。 |
| 52 | + */ |
| 53 | +@keyframes qtine-vt-fade-out{ |
| 54 | + from{opacity:1} |
| 55 | + to{opacity:.6} |
| 56 | +} |
| 57 | +@keyframes qtine-vt-fade-in{ |
| 58 | + from{opacity:0;transform:translateY(6px)} |
| 59 | + to{opacity:1;transform:translateY(0)} |
| 60 | +} |
| 61 | +::view-transition-old(root){ |
| 62 | + animation:qtine-vt-fade-out var(--mdui-motion-duration-short3) |
| 63 | + var(--mdui-motion-easing-emphasized-accelerate) both; |
| 64 | +} |
| 65 | +::view-transition-new(root){ |
| 66 | + animation:qtine-vt-fade-in var(--mdui-motion-duration-medium1) |
| 67 | + var(--mdui-motion-easing-emphasized-decelerate) both; |
| 68 | +} |
| 69 | + |
| 70 | +/* ── 骨架屏:MD3 脉冲式 shimmer ─────────────────────────────────────── */ |
| 71 | +@keyframes qtine-skel-wave{ |
| 72 | + 0%{background-position:-200% 0} |
| 73 | + 100%{background-position:200% 0} |
| 74 | +} |
| 75 | +.skel{ |
| 76 | + background:linear-gradient( |
| 77 | + 90deg, |
| 78 | + var(--qtine-skel-base) 0%, |
| 79 | + var(--qtine-skel-hi) 50%, |
| 80 | + var(--qtine-skel-base) 100% |
| 81 | + ); |
| 82 | + background-size:200% 100%; |
| 83 | + animation:qtine-skel-wave 1.6s |
| 84 | + var(--mdui-motion-easing-emphasized) infinite; |
| 85 | + border-radius:var(--rad-s,8px); |
| 86 | + display:block; |
| 87 | +} |
| 88 | + |
| 89 | +/* 单行文字骨架 */ |
| 90 | +.skel-line{ |
| 91 | + height:14px; |
| 92 | + margin-bottom:8px; |
| 93 | + border-radius:var(--rad-s,8px); |
| 94 | +} |
| 95 | +.skel-line.w90{width:90%} |
| 96 | +.skel-line.w80{width:80%} |
| 97 | +.skel-line.w70{width:70%} |
| 98 | +.skel-line.w60{width:60%} |
| 99 | +.skel-line.w50{width:50%} |
| 100 | +.skel-line.w40{width:40%} |
| 101 | +.skel-line.w30{width:30%} |
| 102 | + |
| 103 | +/* 卡片骨架:复用现有 .card 容器 */ |
| 104 | +.skel-card{ |
| 105 | + display:flex; |
| 106 | + flex-direction:column; |
| 107 | + gap:10px; |
| 108 | +} |
| 109 | +.skel-card .skel-title{height:18px;width:55%} |
| 110 | +.skel-card .skel-meta{height:12px;width:35%} |
| 111 | +.skel-card .skel-actions{display:flex;gap:8px;margin-top:6px} |
| 112 | +.skel-card .skel-actions .skel{height:28px;border-radius:var(--rad-f,9999px)} |
| 113 | +.skel-card .skel-actions .skel.a1{width:64px} |
| 114 | +.skel-card .skel-actions .skel.a2{width:56px} |
| 115 | +.skel-card .skel-actions .skel.a3{width:56px} |
| 116 | + |
| 117 | +/* 表格行骨架(adapters 用) */ |
| 118 | +.skel-card .skel-table{display:flex;flex-direction:column;gap:8px;margin-top:6px} |
| 119 | +.skel-card .skel-table .skel{height:14px;width:100%} |
| 120 | + |
| 121 | +/* 表单字段骨架(settings 用) */ |
| 122 | +.skel-field{margin-bottom:18px} |
| 123 | +.skel-field .skel-label{height:12px;width:80px;margin-bottom:8px} |
| 124 | +.skel-field .skel-input{height:40px;width:100%;border-radius:var(--rad-s,8px)} |
| 125 | + |
| 126 | +/* 块状骨架(logs/dashboard 图表用) */ |
| 127 | +.skel-block{height:200px;border-radius:var(--rad-m,12px)} |
| 128 | +.skel-stat{height:64px;border-radius:var(--rad-m,12px)} |
| 129 | + |
| 130 | +/* ── 无障碍:减少动作偏好 ───────────────────────────────────────────── */ |
| 131 | +@media (prefers-reduced-motion:reduce){ |
| 132 | + .main{animation:none} |
| 133 | + .skel{animation:none;background:var(--qtine-skel-base)} |
| 134 | + ::view-transition-old(root), |
| 135 | + ::view-transition-new(root){animation:none} |
| 136 | +} |
0 commit comments