Files
----/前端源码/uni-app/components/MysticFantasyBg.vue

488 lines
12 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="mfgb" aria-hidden="true">
<view class="mfgb__base" />
<!-- 暗纹万字锦地极低透明度 -->
<view class="mfgb__wanzi" />
<view class="mfgb__ink" />
<view
v-for="c in clouds"
:key="c.id"
class="mfgb__cloud"
:style="getCloudStyle(c)"
/>
<!-- 八卦 + 太极先天卦序极慢旋转 -->
<view class="mfgb__bagua-wrap">
<svg
class="mfgb__bagua-svg mfgb__bagua-spin"
viewBox="-100 -100 200 200"
xmlns="http://www.w3.org/2000/svg"
>
<defs>
<linearGradient id="mfgb-gold-stroke" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color: rgba(212, 175, 55, 0.55)" />
<stop offset="100%" style="stop-color: rgba(212, 175, 55, 0.2)" />
</linearGradient>
</defs>
<circle
cx="0"
cy="0"
r="90"
fill="none"
stroke="url(#mfgb-gold-stroke)"
stroke-width="0.6"
opacity="0.35"
/>
<circle
cx="0"
cy="0"
r="78"
fill="none"
stroke="rgba(212, 175, 55, 0.12)"
stroke-width="0.4"
stroke-dasharray="4 6"
opacity="0.5"
/>
<g
v-for="(trig, ti) in baguaTrigrams"
:key="'bg-' + ti"
:transform="'rotate(' + (ti * 45 - 90) + ') translate(0 -72)'"
>
<g v-for="(solid, li) in trig" :key="'ln-' + ti + '-' + li">
<line
v-if="solid"
x1="-11"
:y1="8 - li * 8"
x2="11"
:y2="8 - li * 8"
stroke="rgba(212, 175, 55, 0.42)"
stroke-width="2.4"
stroke-linecap="round"
/>
<g v-else>
<line
x1="-11"
:y1="8 - li * 8"
x2="-3.5"
:y2="8 - li * 8"
stroke="rgba(212, 175, 55, 0.42)"
stroke-width="2.4"
stroke-linecap="round"
/>
<line
x1="3.5"
:y1="8 - li * 8"
x2="11"
:y2="8 - li * 8"
stroke="rgba(212, 175, 55, 0.42)"
stroke-width="2.4"
stroke-linecap="round"
/>
</g>
</g>
</g>
<!-- 太极简形阴阳鱼 -->
<circle cx="0" cy="0" r="20" fill="none" stroke="rgba(212, 175, 55, 0.28)" stroke-width="0.8" />
<path
d="M0-20 A20 20 0 0 1 0 20 A10 10 0 0 1 0 0 A10 10 0 0 0 0-20 Z"
fill="rgba(212, 175, 55, 0.08)"
/>
<path
d="M0 20 A20 20 0 0 1 0-20 A10 10 0 0 1 0 0 A10 10 0 0 0 0 20 Z"
fill="rgba(15, 23, 42, 0.45)"
/>
<circle cx="0" cy="-10" r="3.2" fill="rgba(15, 23, 42, 0.75)" />
<circle cx="0" cy="10" r="3.2" fill="rgba(212, 175, 55, 0.4)" />
</svg>
</view>
<!-- 四角卷云纹 -->
<view
v-for="corner in cornerKeys"
:key="corner"
class="mfgb__corner"
:class="'mfgb__corner--' + corner"
>
<svg class="mfgb__corner-svg" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg">
<path
class="mfgb__corner-path"
d="M6 58 C6 14 14 6 58 6 M10 54 C10 22 22 10 54 10 M16 48 C18 28 28 18 48 16"
fill="none"
stroke="rgba(212, 175, 55, 0.22)"
stroke-width="1.2"
stroke-linecap="round"
/>
<path
class="mfgb__corner-path"
d="M52 12 Q40 12 36 20 Q32 28 38 36"
fill="none"
stroke="rgba(212, 175, 55, 0.16)"
stroke-width="0.9"
stroke-linecap="round"
/>
</svg>
</view>
<view
class="mfgb__sigil mfgb__sigil--outer"
:class="sigilModifier"
/>
<view
class="mfgb__sigil mfgb__sigil--inner"
:class="sigilModifier"
/>
<view v-for="s in stars" :key="s.id" class="mfgb__star" :style="getStarStyle(s)" />
<view class="mfgb__glow" />
<view class="mfgb__mist" />
<view class="mfgb__vignette" />
</view>
</template>
<script setup lang="ts">
import { computed } from 'vue';
export type MysticRoundPhase = 'ready' | 'loading' | 'result';
const props = withDefaults(
defineProps<{
/** 测名页:测算中加快阵法转速;登录页可省略(默认 ready */
roundPhase?: MysticRoundPhase;
}>(),
{ roundPhase: 'ready' },
);
const sigilModifier = computed(() => {
if (props.roundPhase === 'loading') return 'mfgb__sigil--phase-loading';
if (props.roundPhase === 'result') return 'mfgb__sigil--phase-result';
return '';
});
/** 先天八卦卦序(自下而上爻):乾 兑 离 震 巽 坎 艮 坤 */
const baguaTrigrams: boolean[][] = [
[true, true, true],
[true, true, false],
[true, false, true],
[true, false, false],
[false, true, true],
[false, true, false],
[false, false, true],
[false, false, false],
];
const cornerKeys = ['tl', 'tr', 'bl', 'br'] as const;
const stars = Array.from({ length: 36 }, (_, i) => ({
id: `mfgb-st-${i}`,
top: Math.random() * 100,
left: Math.random() * 100,
size: Math.random() * 2.2 + 0.8,
opacity: Math.random() * 0.35 + 0.12,
duration: Math.random() * 4 + 3,
delay: Math.random() * 4,
}));
const clouds = Array.from({ length: 7 }, (_, i) => ({
id: `mfgb-cl-${i}`,
top: Math.random() * 70 - 10,
left: Math.random() * 90 - 5,
w: 180 + Math.random() * 220,
h: 60 + Math.random() * 80,
opacity: 0.08 + Math.random() * 0.12,
duration: 28 + Math.random() * 18,
delay: Math.random() * -40,
}));
const getStarStyle = (s: (typeof stars)[number]) => ({
top: `${s.top}%`,
left: `${s.left}%`,
width: `${s.size}px`,
height: `${s.size}px`,
opacity: s.opacity,
animationDuration: `${s.duration}s`,
animationDelay: `${s.delay}s`,
});
const getCloudStyle = (c: (typeof clouds)[number]) => ({
top: `${c.top}%`,
left: `${c.left}%`,
width: `${c.w}px`,
height: `${c.h}px`,
opacity: c.opacity,
animationDuration: `${c.duration}s`,
animationDelay: `${c.delay}s`,
});
</script>
<style scoped>
.mfgb {
position: fixed;
inset: 0;
z-index: 0;
pointer-events: none;
overflow: hidden;
}
.mfgb__base {
position: absolute;
inset: 0;
background:
radial-gradient(ellipse 120% 80% at 50% -20%, rgba(212, 175, 55, 0.14), transparent 52%),
radial-gradient(ellipse 90% 60% at 80% 20%, rgba(88, 28, 135, 0.18), transparent 45%),
radial-gradient(ellipse 70% 50% at 10% 30%, rgba(127, 29, 29, 0.12), transparent 50%),
linear-gradient(165deg, #070a12 0%, #12102a 38%, #0a1628 72%, #05080f 100%);
}
/* 万字锦地暗纹 */
.mfgb__wanzi {
position: absolute;
inset: 0;
opacity: 0.04;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='48' height='48' viewBox='0 0 48 48'%3E%3Cpath fill='none' stroke='%23d4af37' stroke-width='0.6' d='M24 4v40M4 24h40M12 12l24 24M36 12L12 36'/%3E%3C/svg%3E");
background-size: 48px 48px;
}
.mfgb__ink {
position: absolute;
left: 0;
right: 0;
bottom: -5%;
height: 42%;
opacity: 0.35;
background:
radial-gradient(ellipse 100% 55% at 20% 100%, rgba(15, 23, 42, 0.95), transparent 70%),
radial-gradient(ellipse 90% 50% at 55% 100%, rgba(30, 41, 59, 0.85), transparent 68%),
radial-gradient(ellipse 80% 45% at 85% 100%, rgba(15, 23, 42, 0.9), transparent 65%);
filter: blur(1px);
mask-image: linear-gradient(to top, black 0%, black 55%, transparent 100%);
}
.mfgb__cloud {
position: absolute;
border-radius: 50%;
background: radial-gradient(ellipse at center, rgba(212, 175, 55, 0.2) 0%, rgba(212, 175, 55, 0.06) 45%, transparent 70%);
filter: blur(18px);
animation: mfgb-cloud-drift linear infinite;
}
@keyframes mfgb-cloud-drift {
0% {
transform: translate(0, 0) scale(1);
}
50% {
transform: translate(-2%, 1.5%) scale(1.03);
}
100% {
transform: translate(0, 0) scale(1);
}
}
/* 八卦层:置于祥云之下、阵环之上,略透明 */
.mfgb__bagua-wrap {
position: absolute;
left: 50%;
top: 52%;
width: min(92vw, 680px);
height: min(92vw, 680px);
transform: translate(-50%, -50%);
opacity: 0.38;
z-index: 1;
}
.mfgb__bagua-svg {
width: 100%;
height: 100%;
display: block;
overflow: visible;
}
.mfgb__bagua-spin {
animation: mfgb-bagua-spin 200s linear infinite;
transform-origin: center center;
}
@keyframes mfgb-bagua-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.mfgb__corner {
position: absolute;
width: 72px;
height: 72px;
z-index: 1;
opacity: 0.85;
}
.mfgb__corner-svg {
width: 100%;
height: 100%;
display: block;
}
.mfgb__corner--tl {
top: calc(12px + env(safe-area-inset-top, 0px));
left: 12px;
}
.mfgb__corner--tr {
top: calc(12px + env(safe-area-inset-top, 0px));
right: 12px;
transform: scaleX(-1);
}
.mfgb__corner--bl {
bottom: calc(12px + env(safe-area-inset-bottom, 0px));
left: 12px;
transform: scaleY(-1);
}
.mfgb__corner--br {
bottom: calc(12px + env(safe-area-inset-bottom, 0px));
right: 12px;
transform: scale(-1);
}
.mfgb__sigil {
position: absolute;
left: 50%;
top: 54%;
border-radius: 50%;
transform: translate(-50%, -50%);
border: 1px dashed rgba(212, 175, 55, 0.28);
box-shadow:
0 0 40px rgba(212, 175, 55, 0.08),
inset 0 0 30px rgba(212, 175, 55, 0.04);
opacity: 0.65;
z-index: 2;
}
.mfgb__sigil--outer {
width: min(72vw, 560px);
height: min(72vw, 560px);
animation: mfgb-sigil-cw 48s linear infinite;
}
.mfgb__sigil--inner {
width: min(52vw, 400px);
height: min(52vw, 400px);
border-style: dotted;
border-color: rgba(212, 175, 55, 0.2);
animation: mfgb-sigil-ccw 32s linear infinite;
}
.mfgb__sigil--outer.mfgb__sigil--phase-loading {
animation-duration: 2.8s;
border-color: rgba(212, 175, 55, 0.55);
opacity: 0.85;
box-shadow:
0 0 56px rgba(212, 175, 55, 0.15),
inset 0 0 36px rgba(212, 175, 55, 0.08);
}
.mfgb__sigil--inner.mfgb__sigil--phase-loading {
animation-duration: 2s;
border-color: rgba(212, 175, 55, 0.42);
opacity: 0.88;
}
.mfgb__sigil--outer.mfgb__sigil--phase-result {
animation-duration: 5.5s;
border-color: rgba(212, 175, 55, 0.35);
opacity: 0.62;
}
.mfgb__sigil--inner.mfgb__sigil--phase-result {
animation-duration: 4s;
border-color: rgba(212, 175, 55, 0.22);
opacity: 0.58;
}
@keyframes mfgb-sigil-cw {
from {
transform: translate(-50%, -50%) rotate(0deg);
}
to {
transform: translate(-50%, -50%) rotate(360deg);
}
}
@keyframes mfgb-sigil-ccw {
from {
transform: translate(-50%, -50%) rotate(0deg);
}
to {
transform: translate(-50%, -50%) rotate(-360deg);
}
}
.mfgb__star {
position: absolute;
border-radius: 50%;
background: rgba(255, 248, 220, 0.95);
box-shadow: 0 0 12px rgba(212, 175, 55, 0.45), 0 0 2px rgba(255, 255, 255, 0.8);
animation: mfgb-twinkle 2.8s ease-in-out infinite;
z-index: 3;
}
@keyframes mfgb-twinkle {
0%,
100% {
transform: scale(0.9);
opacity: 0.2;
}
50% {
transform: scale(1.2);
opacity: 0.65;
}
}
.mfgb__glow {
position: absolute;
left: 50%;
top: 48%;
width: min(90vw, 680px);
height: min(90vw, 680px);
transform: translate(-50%, -50%);
background: radial-gradient(circle at 50% 35%, rgba(212, 175, 55, 0.22), rgba(139, 92, 246, 0.06) 45%, transparent 65%);
filter: blur(36px);
opacity: 0.9;
animation: mfgb-glow-pulse 4.2s ease-in-out infinite;
z-index: 2;
}
@keyframes mfgb-glow-pulse {
0%,
100% {
transform: translate(-50%, -50%) scale(0.94);
opacity: 0.7;
}
50% {
transform: translate(-50%, -50%) scale(1.06);
opacity: 1;
}
}
.mfgb__mist {
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: 38%;
background: linear-gradient(to top, rgba(2, 6, 23, 0.75), rgba(2, 6, 23, 0));
pointer-events: none;
z-index: 4;
}
.mfgb__vignette {
position: absolute;
inset: 0;
background: radial-gradient(ellipse 75% 65% at 50% 45%, transparent 30%, rgba(0, 0, 0, 0.45) 100%);
pointer-events: none;
z-index: 5;
}
</style>