mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-08-30 04:26:43 +08:00
style(no-data): 重做 404 空状态视觉
- 移除 44KB 外链插画 SVG,改为纯内联 SVG + CSS 绘制,消除加载延迟 - 中间 "0" 设计为对称电影胶卷盘,呼应影视主题 - 数字采用主题色渐变,明暗主题自动适配 - 加入入场动画、柔光呼吸晕与漂浮胶片帧装饰,适配 prefers-reduced-motion - 删除无引用的 404.svg / 404.png 死资源
This commit is contained in:
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 41 KiB |
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import page404 from '@images/pages/404.svg'
|
||||
|
||||
// 国际化
|
||||
const { t } = useI18n()
|
||||
@@ -14,13 +14,45 @@ interface Props {
|
||||
icon?: string
|
||||
iconColor?: string
|
||||
}
|
||||
|
||||
// 逐字符渲染错误码,"0" 替换为旋转的胶卷盘
|
||||
const codeChars = computed(() => (props.errorCode || '404').split(''))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="no-data-container">
|
||||
<!-- 图标容器 -->
|
||||
<div class="icon-wrapper">
|
||||
<img :src="page404" alt="404" />
|
||||
<!-- 错误码 -->
|
||||
<div class="code-wrapper" role="img" :aria-label="props.errorCode || '404'">
|
||||
<span class="code-glow" aria-hidden="true" />
|
||||
|
||||
<template v-for="(char, index) in codeChars" :key="index">
|
||||
<!-- 胶卷盘替代 "0" -->
|
||||
<span v-if="char === '0'" class="reel-holder" aria-hidden="true">
|
||||
<svg class="reel" viewBox="0 0 200 200" fill="none">
|
||||
<!-- 盘缘 -->
|
||||
<circle cx="100" cy="100" r="88" class="reel-rim" />
|
||||
<!-- 盘面 6 个镂孔,围绕中心均匀分布 -->
|
||||
<g class="reel-holes">
|
||||
<circle cx="148" cy="100" r="18" />
|
||||
<circle cx="124" cy="141.6" r="18" />
|
||||
<circle cx="76" cy="141.6" r="18" />
|
||||
<circle cx="52" cy="100" r="18" />
|
||||
<circle cx="76" cy="58.4" r="18" />
|
||||
<circle cx="124" cy="58.4" r="18" />
|
||||
</g>
|
||||
<!-- 中心轴孔 -->
|
||||
<circle cx="100" cy="100" r="11" class="reel-hub" />
|
||||
</svg>
|
||||
</span>
|
||||
|
||||
<!-- 普通数字 -->
|
||||
<span v-else class="code-digit" aria-hidden="true">{{ char }}</span>
|
||||
</template>
|
||||
|
||||
<!-- 漂浮的胶片帧装饰 -->
|
||||
<span class="film-chip chip-a" aria-hidden="true" />
|
||||
<span class="film-chip chip-b" aria-hidden="true" />
|
||||
<span class="film-chip chip-c" aria-hidden="true" />
|
||||
</div>
|
||||
|
||||
<!-- 标题 -->
|
||||
@@ -51,20 +83,110 @@ interface Props {
|
||||
min-block-size: 300px;
|
||||
padding-block-start: 3rem;
|
||||
text-align: center;
|
||||
animation: rise-in 0.6s ease both;
|
||||
}
|
||||
|
||||
/* 图标样式 */
|
||||
.icon-wrapper {
|
||||
/* ---------- 错误码 ---------- */
|
||||
.code-wrapper {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
inline-size: 15rem;
|
||||
margin-block: 0 1rem;
|
||||
margin-inline: auto;
|
||||
gap: 0.06em;
|
||||
margin-block: 0 1.5rem;
|
||||
font-size: clamp(5rem, 16vw, 9rem);
|
||||
font-weight: 800;
|
||||
line-height: 1;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
|
||||
/* 文字样式 */
|
||||
.code-digit {
|
||||
background: linear-gradient(175deg, rgba(var(--v-theme-primary), 1) 12%, rgba(var(--v-theme-primary), 0.45) 100%);
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
color: transparent;
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
/* 背景柔光 */
|
||||
.code-glow {
|
||||
position: absolute;
|
||||
inset: -30% -10%;
|
||||
z-index: -1;
|
||||
background: radial-gradient(closest-side, rgba(var(--v-theme-primary), 0.16), transparent 70%);
|
||||
animation: breathe 5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* ---------- 胶卷盘 ---------- */
|
||||
.reel-holder {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
block-size: 0.92em;
|
||||
inline-size: 0.92em;
|
||||
margin-inline: 0.06em;
|
||||
}
|
||||
|
||||
.reel {
|
||||
block-size: 100%;
|
||||
inline-size: 100%;
|
||||
animation: reel-spin 24s linear infinite;
|
||||
}
|
||||
|
||||
.reel-rim {
|
||||
fill: rgba(var(--v-theme-primary), 0.08);
|
||||
stroke: rgba(var(--v-theme-primary), 0.9);
|
||||
stroke-width: 9;
|
||||
}
|
||||
|
||||
/* 盘面 6 个镂孔:仅用描边,孔内透出背景色 */
|
||||
.reel-holes circle {
|
||||
stroke: rgba(var(--v-theme-on-surface), 0.5);
|
||||
stroke-width: 4;
|
||||
}
|
||||
|
||||
.reel-hub {
|
||||
fill: rgba(var(--v-theme-primary), 0.95);
|
||||
}
|
||||
|
||||
/* ---------- 漂浮胶片帧 ---------- */
|
||||
.film-chip {
|
||||
position: absolute;
|
||||
border: 1.5px solid rgba(var(--v-theme-on-surface), 0.18);
|
||||
border-radius: 4px;
|
||||
/* 上下齿孔用重复渐变模拟 */
|
||||
background-image:
|
||||
repeating-linear-gradient(90deg, transparent 0 6px, rgba(var(--v-theme-on-surface), 0.22) 6px 10px);
|
||||
background-position: center top 3px;
|
||||
background-repeat: repeat-x;
|
||||
background-size: 100% 3px;
|
||||
block-size: 14px;
|
||||
inline-size: 34px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.chip-a {
|
||||
inset-block-start: -8%;
|
||||
inset-inline-start: 6%;
|
||||
rotate: -18deg;
|
||||
animation: drift 6s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.chip-b {
|
||||
inset-block-end: -6%;
|
||||
inset-inline-end: 4%;
|
||||
rotate: 14deg;
|
||||
animation: drift 7s ease-in-out 1.2s infinite;
|
||||
}
|
||||
|
||||
.chip-c {
|
||||
inset-block-start: 12%;
|
||||
inset-inline-end: -6%;
|
||||
rotate: 26deg;
|
||||
animation: drift 8s ease-in-out 2.4s infinite;
|
||||
}
|
||||
|
||||
/* ---------- 文字 ---------- */
|
||||
.error-title {
|
||||
position: relative;
|
||||
color: rgba(var(--v-theme-on-surface), 0.95);
|
||||
@@ -81,6 +203,7 @@ interface Props {
|
||||
block-size: 3px;
|
||||
content: '';
|
||||
inline-size: 60px;
|
||||
margin-block-start: 0.5rem;
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
@@ -91,4 +214,58 @@ interface Props {
|
||||
margin-inline: auto;
|
||||
max-inline-size: 80%;
|
||||
}
|
||||
|
||||
/* ---------- 动画 ---------- */
|
||||
@keyframes rise-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(14px);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes breathe {
|
||||
|
||||
0%,
|
||||
100% {
|
||||
opacity: 0.7;
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
50% {
|
||||
opacity: 1;
|
||||
transform: scale(1.08);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes reel-spin {
|
||||
to {
|
||||
rotate: 360deg;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes drift {
|
||||
|
||||
0%,
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translateY(-8px);
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.no-data-container,
|
||||
.code-glow,
|
||||
.reel,
|
||||
.film-chip {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user