Browse Source

character角色选择界面布局调整

master
秦汉 3 days ago
parent
commit
0e366427a5
  1. 203
      Build_God_Game/src/components/GlitchText/GlitchText.vue
  2. 120
      Build_God_Game/src/views/CharacterView.vue

203
Build_God_Game/src/components/GlitchText/GlitchText.vue

@ -0,0 +1,203 @@
<template>
<div :class="computedClasses" :style="inlineStyles" :data-text="children">
{{ children }}
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue';
import type { CSSProperties } from 'vue';
interface GlitchTextProps {
children: string;
speed?: number;
enableShadows?: boolean;
enableOnHover?: boolean;
className?: string;
}
interface CustomCSSProperties extends CSSProperties {
'--after-duration': string;
'--before-duration': string;
'--after-shadow': string;
'--before-shadow': string;
}
const props = withDefaults(defineProps<GlitchTextProps>(), {
speed: 0.5,
enableShadows: true,
enableOnHover: false,
className: ''
});
const inlineStyles = computed(
(): CustomCSSProperties => ({
'--after-duration': `${props.speed * 3}s`,
'--before-duration': `${props.speed * 2}s`,
'--after-shadow': props.enableShadows ? '-5px 0 red' : 'none',
'--before-shadow': props.enableShadows ? '5px 0 cyan' : 'none'
})
);
const baseClasses = [
'text-white',
'font-black',
'whitespace-nowrap',
'relative',
'mx-auto',
'select-none',
'cursor-pointer',
'text-[clamp(2rem,10vw,8rem)]',
'before:content-[attr(data-text)]',
'before:absolute',
'before:top-0',
'before:text-white',
'before:bg-[#0b0b0b]',
'before:overflow-hidden',
'before:[clip-path:inset(0_0_0_0)]',
'after:content-[attr(data-text)]',
'after:absolute',
'after:top-0',
'after:text-white',
'after:bg-[#0b0b0b]',
'after:overflow-hidden',
'after:[clip-path:inset(0_0_0_0)]'
];
const normalGlitchClasses = [
'after:left-[10px]',
'after:[text-shadow:var(--after-shadow,-10px_0_red)]',
'after:[animation:animate-glitch_var(--after-duration,3s)_infinite_linear_alternate-reverse]',
'before:left-[-10px]',
'before:[text-shadow:var(--before-shadow,10px_0_cyan)]',
'before:[animation:animate-glitch_var(--before-duration,2s)_infinite_linear_alternate-reverse]'
];
const hoverOnlyClasses = [
'before:content-[""]',
'before:opacity-0',
'before:[animation:none]',
'after:content-[""]',
'after:opacity-0',
'after:[animation:none]',
'hover:before:content-[attr(data-text)]',
'hover:before:opacity-100',
'hover:before:left-[-10px]',
'hover:before:[text-shadow:var(--before-shadow,10px_0_cyan)]',
'hover:before:[animation:animate-glitch_var(--before-duration,2s)_infinite_linear_alternate-reverse]',
'hover:after:content-[attr(data-text)]',
'hover:after:opacity-100',
'hover:after:left-[10px]',
'hover:after:[text-shadow:var(--after-shadow,-10px_0_red)]',
'hover:after:[animation:animate-glitch_var(--after-duration,3s)_infinite_linear_alternate-reverse]'
];
const computedClasses = computed(() => {
const classes = [...baseClasses];
if (props.enableOnHover) {
classes.push(...hoverOnlyClasses);
} else {
classes.push(...normalGlitchClasses);
}
if (props.className) {
classes.push(props.className);
}
return classes.join(' ');
});
</script>
<style>
@keyframes animate-glitch {
0% {
clip-path: inset(20% 0 50% 0);
}
5% {
clip-path: inset(10% 0 60% 0);
}
10% {
clip-path: inset(15% 0 55% 0);
}
15% {
clip-path: inset(25% 0 35% 0);
}
20% {
clip-path: inset(30% 0 40% 0);
}
25% {
clip-path: inset(40% 0 20% 0);
}
30% {
clip-path: inset(10% 0 60% 0);
}
35% {
clip-path: inset(15% 0 55% 0);
}
40% {
clip-path: inset(25% 0 35% 0);
}
45% {
clip-path: inset(30% 0 40% 0);
}
50% {
clip-path: inset(20% 0 50% 0);
}
55% {
clip-path: inset(10% 0 60% 0);
}
60% {
clip-path: inset(15% 0 55% 0);
}
65% {
clip-path: inset(25% 0 35% 0);
}
70% {
clip-path: inset(30% 0 40% 0);
}
75% {
clip-path: inset(40% 0 20% 0);
}
80% {
clip-path: inset(20% 0 50% 0);
}
85% {
clip-path: inset(10% 0 60% 0);
}
90% {
clip-path: inset(15% 0 55% 0);
}
95% {
clip-path: inset(25% 0 35% 0);
}
100% {
clip-path: inset(30% 0 40% 0);
}
}
</style>

120
Build_God_Game/src/views/CharacterView.vue

@ -7,6 +7,7 @@ import { ElProgress } from 'element-plus'
import Particles from '@/components/Particles/Particles.vue' import Particles from '@/components/Particles/Particles.vue'
import GlareHover from '@/components/GlareHover/GlareHover.vue' import GlareHover from '@/components/GlareHover/GlareHover.vue'
import ElectricBorder from '@/components/ElectricBorder/ElectricBorder.vue' import ElectricBorder from '@/components/ElectricBorder/ElectricBorder.vue'
import GlitchText from '@/components/GlitchText/GlitchText.vue'
const router = useRouter() const router = useRouter()
const characterStore = useCharacterStore() const characterStore = useCharacterStore()
@ -104,6 +105,10 @@ const getExpProgress = (currentExp: number, nextLevelMinExp?: number) => {
if (!nextLevelMinExp) return 0 if (!nextLevelMinExp) return 0
return Math.min(100, (currentExp / nextLevelMinExp) * 100) return Math.min(100, (currentExp / nextLevelMinExp) * 100)
} }
const formatNumber = (num: number) => {
return Math.floor(num).toLocaleString('zh-CN')
}
</script> </script>
<template> <template>
@ -113,15 +118,9 @@ const getExpProgress = (currentExp: number, nextLevelMinExp?: number) => {
<div class="character-container"> <div class="character-container">
<div class="page-header"> <div class="page-header">
<h1 class="title">选择角色</h1> <h1 class="title">选择角色</h1>
<p class="subtitle">修仙者: {{ authStore.username }}</p>
</div> </div>
<div class="character-list"> <div class="character-list">
<div v-for="char in characterStore.characters" :key="char.id" class="character-card" <div v-for="char in characterStore.characters" :key="char.id" class="character-card"
@click="handleSelectCharacter(char.id)"> @click="handleSelectCharacter(char.id)">
<div class="character-info"> <div class="character-info">
@ -132,15 +131,15 @@ const getExpProgress = (currentExp: number, nextLevelMinExp?: number) => {
<div class="character-stats"> <div class="character-stats">
<div class="stat-item"> <div class="stat-item">
<span class="stat-label">生命</span> <span class="stat-label">生命</span>
<span class="stat-value">{{ Math.floor(char.maxHP) }}</span> <span class="stat-value">{{ formatNumber(char.maxHP) }}</span>
</div> </div>
<div class="stat-item"> <div class="stat-item">
<span class="stat-label">攻击</span> <span class="stat-label">攻击</span>
<span class="stat-value">{{ Math.floor(char.attack) }}</span> <span class="stat-value">{{ formatNumber(char.attack) }}</span>
</div> </div>
<div class="stat-item"> <div class="stat-item">
<span class="stat-label">防御</span> <span class="stat-label">防御</span>
<span class="stat-value">{{ Math.floor(char.defend) }}</span> <span class="stat-value">{{ formatNumber(char.defend) }}</span>
</div> </div>
<div class="stat-item"> <div class="stat-item">
<span class="stat-label">暴击</span> <span class="stat-label">暴击</span>
@ -148,32 +147,25 @@ const getExpProgress = (currentExp: number, nextLevelMinExp?: number) => {
</div> </div>
</div> </div>
<div class="character-second-stats"> <div class="character-basic-stats">
<div v-if="char.professionName" class="stat-item"> <div v-if="char.professionName">
<span class="stat-label">职业</span> <span class="stat-label">职业</span>
<span class="stat-value">{{ char.professionName }}</span> <span class="stat-value" style="margin-left: 10px;">{{ char.professionName }}</span>
</div> </div>
<div class="stat-item"> <div>
<span class="stat-label">灵石</span> <span class="stat-label">灵石</span>
<span class="stat-value">{{ char.money }}</span> <span class="stat-value" style="margin-left: 10px;">{{ formatNumber(char.money) }}</span>
</div> </div>
</div> </div>
<div class="character-extended-stats"> <div class="character-exp-section">
<div class="stat-item exp-item"> <div class="exp-header">
<span class="stat-label">经验</span> <span class="stat-label">经验</span>
<el-progress :percentage="getExpProgress(char.currentExp, char.nextLevelMinExp)" color="#ff8844" <span class="stat-value-small">{{ formatNumber(char.currentExp) }}/{{ formatNumber(char.nextLevelMinExp ||
:show-text="false" :stroke-width="4" /> 0) }}</span>
<span class="stat-value-small">{{ Math.floor(char.currentExp) }}/{{ char.nextLevelMinExp }}</span>
</div>
<div class="stat-item">
<span class="stat-label">修炼</span>
<span class="stat-value">{{ char.trainingExpRate }}x</span>
</div>
<div class="stat-item">
<span class="stat-label">突破</span>
<span class="stat-value">{{ char.breakthroughRate }}%</span>
</div> </div>
<el-progress :percentage="getExpProgress(char.currentExp, char.nextLevelMinExp)" color="#ff8844"
:show-text="false" :stroke-width="8" />
</div> </div>
<div class="character-meta"> <div class="character-meta">
@ -377,37 +369,69 @@ const getExpProgress = (currentExp: number, nextLevelMinExp?: number) => {
.character-stats { .character-stats {
display: grid; display: grid;
grid-template-columns: repeat(4, 1fr); grid-template-columns: repeat(4, 1fr);
gap: 8px; gap: 0;
margin-bottom: 8px; margin-bottom: 8px;
padding: 12px 0;
border-top: 1px solid rgba(255, 255, 255, 0.05);
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
} }
.character-second-stats { .character-stats .stat-item {
display: flex;
flex-direction: column;
padding: 0 8px;
border-right: 1px solid rgba(255, 255, 255, 0.05);
}
.character-stats .stat-item:last-child {
border-right: none;
}
.character-stats .stat-label {
margin-bottom: 4px;
}
.character-stats .stat-value {
text-align: left;
}
.character-basic-stats {
display: grid; display: grid;
grid-template-columns: repeat(2, 1fr); grid-template-columns: 1fr 1fr;
gap: 8px; gap: 16px;
padding: 12px 0;
margin-bottom: 8px; margin-bottom: 8px;
} }
.character-basic-stats .stat-item {
display: flex;
justify-content: space-between;
align-items: center;
}
.stat-item { .stat-item {
text-align: center; display: flex;
justify-content: space-between;
align-items: center;
} }
.stat-label { .stat-label {
display: block;
color: #888888; color: #888888;
font-size: 0.7rem; font-size: 0.75rem;
margin-bottom: 2px;
} }
.stat-value { .stat-value {
color: #ffffff; color: #ffffff;
font-size: 0.85rem; font-size: 0.8rem;
} }
.character-meta { .character-meta {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
margin-top: 16px;
padding-top: 12px;
border-top: 1px solid rgba(255, 255, 255, 0.05);
} }
.last-login { .last-login {
@ -415,29 +439,19 @@ const getExpProgress = (currentExp: number, nextLevelMinExp?: number) => {
font-size: 0.75rem; font-size: 0.75rem;
} }
.character-extended-stats { .character-exp-section {
display: flex; margin-bottom: 8px;
justify-content: space-between;
gap: 8px;
padding-top: 8px;
border-top: 1px solid rgba(255, 255, 255, 0.05);
margin-top: 8px;
}
.character-extended-stats .stat-item {
flex: 1;
text-align: center;
} }
.exp-item { .exp-header {
flex: 2 !important; display: flex;
justify-content: space-between;
margin-bottom: 8px;
} }
.stat-value-small { .stat-value-small {
display: block;
color: #666666; color: #666666;
font-size: 0.65rem; font-size: 0.7rem;
margin-top: 2px;
} }
.delete-btn { .delete-btn {

Loading…
Cancel
Save