1292 lines
33 KiB
Vue
1292 lines
33 KiB
Vue
<script setup lang="ts">
|
||
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from "vue";
|
||
import { useRoute, useRouter } from "vue-router";
|
||
import { dataList } from "./data";
|
||
|
||
interface SkillOption {
|
||
videoUrl?: string;
|
||
txt1: string;
|
||
txt2?: string;
|
||
txt3?: string;
|
||
}
|
||
|
||
interface SkillItem {
|
||
lev: number;
|
||
skillName: string;
|
||
type: "Up" | "VP" | string;
|
||
videoUrl?: string;
|
||
options: SkillOption[];
|
||
}
|
||
|
||
interface SkillGroup {
|
||
type: "Up" | "VP" | string;
|
||
skills: SkillItem[];
|
||
}
|
||
|
||
interface PersonData {
|
||
peoTit?: string;
|
||
peoName: string;
|
||
linkUrl: string;
|
||
skillsByType: SkillGroup[];
|
||
}
|
||
|
||
interface RoleData {
|
||
title: string;
|
||
data: PersonData[];
|
||
}
|
||
|
||
interface RoleOption {
|
||
roleType: string;
|
||
title: string;
|
||
}
|
||
|
||
const asset = "https://game.gtimg.cn/images/dnf/cp/a20250612vp";
|
||
const route = useRoute();
|
||
const router = useRouter();
|
||
const detailData = dataList as Record<string, RoleData>;
|
||
const roleOrder = [
|
||
"gjsNv",
|
||
"gjsNan",
|
||
"gdjNan",
|
||
"gdjNv",
|
||
"sqsNan",
|
||
"sqsNv",
|
||
"mfsNan",
|
||
"mfsNv",
|
||
"gzzNan",
|
||
"gzzNv",
|
||
"aysz",
|
||
"dzz",
|
||
"haws",
|
||
"mqs",
|
||
"qjs",
|
||
"shz",
|
||
"gongjs",
|
||
];
|
||
|
||
const roles = computed<RoleOption[]>(() =>
|
||
roleOrder
|
||
.filter((roleType) => Boolean(detailData[roleType]))
|
||
.map((roleType) => ({ roleType, title: detailData[roleType]!.title })),
|
||
);
|
||
const activeRoleType = ref("gjsNv");
|
||
const activePerson = ref(0);
|
||
const activeTab = ref<"skill" | "break">("skill");
|
||
const activeSkill = ref(0);
|
||
const activeSkillSlide = ref(0);
|
||
const activeBreakSlide = ref(0);
|
||
const isMobile = ref(false);
|
||
const mobilePanelOpen = ref(false);
|
||
const roleTrackRef = ref<HTMLElement>();
|
||
let previousTitle = "";
|
||
let previousRootFontSize = "";
|
||
|
||
const currentRole = computed<RoleOption>(() => roles.value.find((item) => item.roleType === activeRoleType.value) ?? roles.value[0]!);
|
||
const currentRoleIndex = computed(() => Math.max(0, roles.value.findIndex((item) => item.roleType === currentRole.value.roleType)));
|
||
const currentRoleData = computed<RoleData>(() => detailData[currentRole.value.roleType] ?? detailData.gjsNv!);
|
||
const currentPerson = computed<PersonData>(() => currentRoleData.value.data[activePerson.value] ?? currentRoleData.value.data[0]!);
|
||
const personIndexes = computed(() => Array.from({ length: currentRoleData.value.data.length }, (_, index) => index));
|
||
const upSkills = computed<SkillItem[]>(() => currentPerson.value.skillsByType.find((item) => item.type === "Up")?.skills ?? []);
|
||
const vpSkills = computed<SkillItem[]>(() => currentPerson.value.skillsByType.find((item) => item.type === "VP")?.skills ?? []);
|
||
const currentVpSkill = computed<SkillItem>(() => vpSkills.value[activeSkill.value] ?? vpSkills.value[0]!);
|
||
const currentOptions = computed<SkillOption[]>(() => currentVpSkill.value?.options ?? []);
|
||
const skillSlideCount = computed(() => 1 + currentOptions.value.length);
|
||
const breakSkills = computed(() => [
|
||
...upSkills.value.map((skill, index) => ({ skill, source: "up" as const, sourceIndex: index })),
|
||
...vpSkills.value.map((skill, index) => ({ skill, source: "vp" as const, sourceIndex: index })),
|
||
]);
|
||
const bgUrl = computed(() => {
|
||
const prefix = isMobile.value ? `${asset}/m` : asset;
|
||
return `url(${prefix}/${currentRole.value.roleType}/body-bg.jpg)`;
|
||
});
|
||
const personUrl = computed(() => {
|
||
const prefix = isMobile.value ? `${asset}/m` : asset;
|
||
return `${prefix}/${currentRole.value.roleType}/${activePerson.value + 1}/peo.png`;
|
||
});
|
||
const panelBgUrl = computed(() => {
|
||
const bg = activeTab.value === "skill" ? "bg1.png" : "bg2.png";
|
||
return `url(${asset}/${currentRole.value.roleType}/${bg})`;
|
||
});
|
||
const recommendUrl = computed(() => currentPerson.value.linkUrl || "https://hub.bbs.colg.cn/tools/vp/index.html");
|
||
const personKey = computed(() => `${currentRole.value.roleType}-${activePerson.value}`);
|
||
|
||
function syncRoleFromRoute() {
|
||
const roleType = typeof route.query.roleType === "string" ? route.query.roleType : "";
|
||
activeRoleType.value = roles.value.some((item) => item.roleType === roleType) ? roleType : "gjsNv";
|
||
activePerson.value = 0;
|
||
activeSkill.value = 0;
|
||
}
|
||
|
||
function changeRole(roleType: string) {
|
||
void router.replace({ name: "dnf-vp-detail", query: { roleType } });
|
||
}
|
||
|
||
function stepRole(delta: number) {
|
||
const nextIndex = (currentRoleIndex.value + delta + roles.value.length) % roles.value.length;
|
||
const role = roles.value[nextIndex];
|
||
if (role) {
|
||
changeRole(role.roleType);
|
||
}
|
||
}
|
||
|
||
async function scrollActiveRoleIntoView() {
|
||
await nextTick();
|
||
const track = roleTrackRef.value;
|
||
const active = track?.querySelector<HTMLElement>(".role-option.active");
|
||
if (!track || !active) return;
|
||
const target = active.offsetLeft - track.clientWidth / 2 + active.clientWidth / 2;
|
||
track.scrollTo({ left: Math.max(0, target), behavior: "smooth" });
|
||
}
|
||
|
||
function changeTab(tab: "skill" | "break") {
|
||
activeTab.value = tab;
|
||
mobilePanelOpen.value = true;
|
||
}
|
||
|
||
function changeSkill(index: number) {
|
||
activeSkill.value = index;
|
||
activeSkillSlide.value = 0;
|
||
}
|
||
|
||
function stepSkillSlide(step: number) {
|
||
activeSkillSlide.value = (activeSkillSlide.value + step + skillSlideCount.value) % skillSlideCount.value;
|
||
}
|
||
|
||
function stepBreakSlide(step: number) {
|
||
activeBreakSlide.value = (activeBreakSlide.value + step + breakSkills.value.length) % breakSkills.value.length;
|
||
}
|
||
|
||
function backHome() {
|
||
void router.push({ name: "dnf-vp" });
|
||
}
|
||
|
||
function syncViewport() {
|
||
const mobile = window.matchMedia("(max-width: 768px)").matches;
|
||
const width = document.documentElement.clientWidth;
|
||
const height = document.documentElement.clientHeight;
|
||
isMobile.value = mobile;
|
||
|
||
if (mobile) {
|
||
document.documentElement.style.fontSize = "";
|
||
document.documentElement.style.setProperty("--dnf-detail-rem", `${Math.min(width, 750) / 750}px`);
|
||
return;
|
||
}
|
||
|
||
const scale = Math.min(width / 1920, height / 1080, 1);
|
||
document.documentElement.style.fontSize = `${scale}px`;
|
||
document.documentElement.style.removeProperty("--dnf-detail-rem");
|
||
}
|
||
|
||
function hideBrokenImage(event: Event) {
|
||
const el = event.target;
|
||
if (el instanceof HTMLImageElement) {
|
||
el.style.visibility = "hidden";
|
||
}
|
||
}
|
||
|
||
function videoUrl(path?: string) {
|
||
if (!path) return "";
|
||
return encodeURI(`https://skillvideo.cdn.dnf.qq.com/${path}.mp4`);
|
||
}
|
||
|
||
watch(() => route.query.roleType, syncRoleFromRoute, { immediate: true });
|
||
watch(activeRoleType, scrollActiveRoleIntoView);
|
||
watch(isMobile, scrollActiveRoleIntoView);
|
||
watch(activePerson, () => {
|
||
activeSkill.value = 0;
|
||
activeSkillSlide.value = 0;
|
||
activeBreakSlide.value = 0;
|
||
});
|
||
watch(vpSkills, () => {
|
||
if (activeSkill.value >= vpSkills.value.length) {
|
||
activeSkill.value = 0;
|
||
}
|
||
activeSkillSlide.value = 0;
|
||
activeBreakSlide.value = 0;
|
||
});
|
||
watch(currentOptions, () => {
|
||
if (activeSkillSlide.value >= skillSlideCount.value) {
|
||
activeSkillSlide.value = 0;
|
||
}
|
||
});
|
||
watch(breakSkills, () => {
|
||
if (activeBreakSlide.value >= breakSkills.value.length) {
|
||
activeBreakSlide.value = 0;
|
||
}
|
||
});
|
||
|
||
onMounted(() => {
|
||
previousTitle = document.title;
|
||
previousRootFontSize = document.documentElement.style.fontSize;
|
||
document.title = "雾神武器系统站-DNF官网-腾讯游戏";
|
||
syncViewport();
|
||
window.addEventListener("resize", syncViewport);
|
||
});
|
||
|
||
onBeforeUnmount(() => {
|
||
window.removeEventListener("resize", syncViewport);
|
||
document.documentElement.style.fontSize = previousRootFontSize;
|
||
document.documentElement.style.removeProperty("--dnf-detail-rem");
|
||
document.title = previousTitle;
|
||
});
|
||
</script>
|
||
|
||
<template>
|
||
<main class="vp-detail" :class="{ mobile: isMobile }" :style="{ backgroundImage: bgUrl }">
|
||
<header class="detail-header">
|
||
<a class="detail-logo" href="https://dnf.qq.com/main.shtml" target="_blank" aria-label="DNF 官网"></a>
|
||
<button class="back-button" type="button" @click="backHome">返回首页</button>
|
||
</header>
|
||
|
||
<Transition name="person-swap" mode="out-in">
|
||
<img class="person" :key="personKey" :src="personUrl" :alt="currentPerson.peoName" @error="hideBrokenImage" />
|
||
</Transition>
|
||
|
||
<section class="mobile-info">
|
||
<Transition name="name-swap" mode="out-in">
|
||
<p class="person-name" :key="personKey">{{ currentPerson.peoName }}</p>
|
||
</Transition>
|
||
<div class="tab-buttons">
|
||
<button :class="{ active: activeTab === 'skill' }" type="button" @click="changeTab('skill')">技能进化</button>
|
||
<button :class="{ active: activeTab === 'break' }" type="button" @click="changeTab('break')">雾神突破</button>
|
||
</div>
|
||
</section>
|
||
|
||
<div class="avatar-wheel" v-if="personIndexes.length > 1">
|
||
<button
|
||
v-for="index in personIndexes"
|
||
:key="index"
|
||
class="avatar"
|
||
:class="{ active: activePerson === index, five: personIndexes.length > 4 }"
|
||
type="button"
|
||
@click="activePerson = index"
|
||
>
|
||
<img class="avatar-normal" :src="`${asset}/${currentRole.roleType}/${index + 1}/avatar1.png`" alt="" @error="hideBrokenImage" />
|
||
<img class="avatar-active" :src="`${asset}/${currentRole.roleType}/${index + 1}/avatar2.png`" alt="" @error="hideBrokenImage" />
|
||
</button>
|
||
</div>
|
||
|
||
<aside class="role-select">
|
||
<button class="mobile-role-arrow mobile-role-prev" type="button" aria-label="上一个职业" @click="stepRole(-1)"></button>
|
||
<div ref="roleTrackRef" class="role-track">
|
||
<button
|
||
v-for="(role, index) in roles"
|
||
:key="role.roleType"
|
||
class="role-option"
|
||
:class="{ active: role.roleType === currentRole.roleType }"
|
||
type="button"
|
||
@click="changeRole(role.roleType)"
|
||
>
|
||
<span class="role-icon" :style="{ '--i': index }"></span>
|
||
<span>{{ role.title }}</span>
|
||
</button>
|
||
</div>
|
||
<button class="mobile-role-arrow mobile-role-next" type="button" aria-label="下一个职业" @click="stepRole(1)"></button>
|
||
</aside>
|
||
|
||
<section class="skill-panel" :class="{ show: mobilePanelOpen || !isMobile }">
|
||
<div class="panel-inner">
|
||
<button class="panel-close" type="button" @click="mobilePanelOpen = false">关闭</button>
|
||
<div class="tab-buttons desktop-tabs">
|
||
<button :class="{ active: activeTab === 'skill' }" type="button" @click="changeTab('skill')">技能进化</button>
|
||
<button :class="{ active: activeTab === 'break' }" type="button" @click="changeTab('break')">雾神突破</button>
|
||
</div>
|
||
<p class="tips" v-if="activeTab === 'skill'">核心进化点:可在每个 VP 技能的两种进化路线中选择一种,改变技能形态与操作手感。</p>
|
||
<p class="tips" v-else>通过雾神突破强化 3 个技能,可选择攻击力增加或攻击力/冷却时间组合强化。</p>
|
||
|
||
<div v-if="activeTab === 'skill'" class="tab-main tab-skill" :style="{ backgroundImage: panelBgUrl }">
|
||
<div class="skill-tabs">
|
||
<button
|
||
v-for="(skill, index) in vpSkills"
|
||
:key="skill.skillName"
|
||
:class="{ active: activeSkill === index }"
|
||
type="button"
|
||
@click="changeSkill(index)"
|
||
>
|
||
{{ skill.lev }}
|
||
<span>{{ skill.skillName }}</span>
|
||
</button>
|
||
</div>
|
||
|
||
<div class="skill-cards">
|
||
<article class="skill-card" :class="{ active: activeSkillSlide === 0 }">
|
||
<div class="skill-title">
|
||
<img :src="`${asset}/${currentRole.roleType}/${activePerson + 1}/vp/skill${activeSkill}.png`" alt="" @error="hideBrokenImage" />
|
||
<strong>{{ currentVpSkill.skillName }}</strong>
|
||
</div>
|
||
<div class="video-thumb">
|
||
<video v-if="currentVpSkill.videoUrl" :src="videoUrl(currentVpSkill.videoUrl)" muted controls playsinline preload="metadata" />
|
||
<img v-else :src="`${asset}/img1.png`" alt="" />
|
||
</div>
|
||
</article>
|
||
<article
|
||
v-for="(option, optionIndex) in currentOptions"
|
||
:key="optionIndex"
|
||
class="skill-card"
|
||
:class="{ active: activeSkillSlide === optionIndex + 1 }"
|
||
>
|
||
<div class="skill-title">
|
||
<img
|
||
:src="`${asset}/${currentRole.roleType}/${activePerson + 1}/skillBreak/${optionIndex === 0 ? 'skill' : 'skill-'}${activeSkill}.png`"
|
||
alt=""
|
||
@error="hideBrokenImage"
|
||
/>
|
||
<strong>{{ option.txt1 }}</strong>
|
||
</div>
|
||
<p v-if="option.txt2" class="option-summary" v-html="option.txt2"></p>
|
||
<div class="video-thumb">
|
||
<video v-if="option.videoUrl" :src="videoUrl(option.videoUrl)" muted controls playsinline preload="metadata" />
|
||
<img v-else :src="`${asset}/img1.png`" alt="" />
|
||
</div>
|
||
<p v-if="option.txt3" v-html="option.txt3"></p>
|
||
</article>
|
||
</div>
|
||
<div class="mobile-slide-controls" v-if="skillSlideCount > 1">
|
||
<button class="slide-arrow slide-prev" type="button" aria-label="上一张技能卡" @click="stepSkillSlide(-1)"></button>
|
||
<span>{{ activeSkillSlide + 1 }} / {{ skillSlideCount }}</span>
|
||
<button class="slide-arrow slide-next" type="button" aria-label="下一张技能卡" @click="stepSkillSlide(1)"></button>
|
||
</div>
|
||
</div>
|
||
|
||
<div v-else class="tab-main tab-break" :style="{ backgroundImage: panelBgUrl }">
|
||
<article
|
||
v-for="({ skill, source, sourceIndex }, index) in breakSkills"
|
||
:key="`${skill.type}-${skill.skillName}-${index}`"
|
||
class="break-card"
|
||
:class="{ active: activeBreakSlide === index }"
|
||
>
|
||
<p class="level">{{ skill.lev }}级</p>
|
||
<div class="break-icon">
|
||
<img
|
||
:src="`${asset}/${currentRole.roleType}/${activePerson + 1}/${source}/skill${sourceIndex}.png`"
|
||
alt=""
|
||
@error="hideBrokenImage"
|
||
/>
|
||
<span>{{ skill.skillName }}</span>
|
||
</div>
|
||
<div class="break-info">
|
||
<p>{{ skill.lev < 35 ? "技能攻击力 +55%" : "技能攻击力 +60%" }}</p>
|
||
<p>技能攻击力 +{{ skill.lev < 35 ? "38" : "43" }}% / 冷却时间 -15%</p>
|
||
</div>
|
||
</article>
|
||
<div class="mobile-slide-controls" v-if="breakSkills.length > 1">
|
||
<button class="slide-arrow slide-prev" type="button" aria-label="上一个突破技能" @click="stepBreakSlide(-1)"></button>
|
||
<span>{{ activeBreakSlide + 1 }} / {{ breakSkills.length }}</span>
|
||
<button class="slide-arrow slide-next" type="button" aria-label="下一个突破技能" @click="stepBreakSlide(1)"></button>
|
||
</div>
|
||
</div>
|
||
|
||
<a class="recommend-link" :href="recommendUrl" target="_blank">COLG 加点推荐 >></a>
|
||
<p class="mod-tips">* 具体数据请以正式服上线后为准</p>
|
||
</div>
|
||
</section>
|
||
</main>
|
||
</template>
|
||
|
||
<style scoped>
|
||
@font-face {
|
||
font-family: dnf-vp;
|
||
src: url("https://game.gtimg.cn/images/dnf/cp/a20250612vp/font.ttf");
|
||
}
|
||
|
||
:global(body:has(.vp-detail)) {
|
||
overflow: hidden;
|
||
background: #000;
|
||
}
|
||
|
||
.vp-detail {
|
||
position: fixed;
|
||
inset: 0;
|
||
z-index: 9999;
|
||
overflow: hidden;
|
||
background-color: #242220;
|
||
background-position: 50% 50%;
|
||
background-repeat: no-repeat;
|
||
background-size: cover;
|
||
color: #fff;
|
||
font-family: "Microsoft Yahei", Arial, sans-serif;
|
||
}
|
||
|
||
.detail-header {
|
||
position: relative;
|
||
z-index: 10;
|
||
}
|
||
|
||
.detail-logo {
|
||
position: absolute;
|
||
top: 42rem;
|
||
left: 44rem;
|
||
display: block;
|
||
width: 397rem;
|
||
height: 65rem;
|
||
background: url("https://game.gtimg.cn/images/dnf/cp/a20250612vp/logo.png") center / 100% 100% no-repeat;
|
||
}
|
||
|
||
.back-button {
|
||
position: absolute;
|
||
top: 44rem;
|
||
right: 58rem;
|
||
width: 194rem;
|
||
height: 45rem;
|
||
color: #111114;
|
||
font-size: 18rem;
|
||
line-height: 45rem;
|
||
text-align: center;
|
||
cursor: pointer;
|
||
background: url("https://game.gtimg.cn/images/dnf/cp/a20250612vp/m/back-bg.png") center / 100% 100% no-repeat;
|
||
border: 0;
|
||
}
|
||
|
||
.person {
|
||
position: absolute;
|
||
right: 0;
|
||
bottom: 0;
|
||
height: 100%;
|
||
user-select: none;
|
||
will-change: opacity, transform;
|
||
}
|
||
|
||
.person-swap-enter-active,
|
||
.person-swap-leave-active {
|
||
transition: opacity 0.42s ease, transform 0.62s cubic-bezier(0.16, 1, 0.3, 1), filter 0.42s ease;
|
||
}
|
||
|
||
.person-swap-enter-from {
|
||
opacity: 0;
|
||
filter: blur(2px);
|
||
transform: translate3d(90rem, 0, 0) scale(1.018);
|
||
}
|
||
|
||
.person-swap-leave-to {
|
||
opacity: 0;
|
||
filter: blur(2px);
|
||
transform: translate3d(-42rem, 0, 0) scale(0.992);
|
||
}
|
||
|
||
.person-swap-enter-to,
|
||
.person-swap-leave-from {
|
||
opacity: 1;
|
||
filter: blur(0);
|
||
transform: translate3d(0, 0, 0) scale(1);
|
||
}
|
||
|
||
.avatar-wheel {
|
||
position: absolute;
|
||
right: 0;
|
||
bottom: 0;
|
||
width: 375rem;
|
||
height: 313rem;
|
||
background: url("https://game.gtimg.cn/images/dnf/cp/a20250612vp/zhuanpan.png") center / 100% 100% no-repeat;
|
||
}
|
||
|
||
.avatar {
|
||
position: absolute;
|
||
width: 119rem;
|
||
height: 119rem;
|
||
padding: 0;
|
||
cursor: pointer;
|
||
background: transparent;
|
||
border: 0;
|
||
border-radius: 999px;
|
||
}
|
||
|
||
.avatar img {
|
||
position: absolute;
|
||
inset: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
transition: width 0.26s ease, height 0.26s ease, opacity 0.26s ease, transform 0.26s ease;
|
||
}
|
||
|
||
.avatar .avatar-active,
|
||
.avatar.active .avatar-normal {
|
||
width: 0;
|
||
height: 0;
|
||
opacity: 0;
|
||
}
|
||
|
||
.avatar.active .avatar-active {
|
||
width: 100%;
|
||
height: 100%;
|
||
opacity: 1;
|
||
transform: scale(1.04);
|
||
}
|
||
|
||
.avatar:nth-of-type(1) { left: -34rem; bottom: 23rem; }
|
||
.avatar:nth-of-type(2) { left: 24rem; bottom: 122rem; }
|
||
.avatar:nth-of-type(3) { left: 112rem; top: -5rem; }
|
||
.avatar:nth-of-type(4) { right: 40rem; top: -47rem; }
|
||
.avatar:nth-of-type(5) { right: 10rem; top: -55rem; }
|
||
.avatar:nth-of-type(1).five { left: -37rem; bottom: 6rem; }
|
||
.avatar:nth-of-type(2).five { left: 2rem; bottom: 95rem; }
|
||
.avatar:nth-of-type(3).five { top: auto; left: 70rem; bottom: 168rem; }
|
||
.avatar:nth-of-type(4).five { right: 110rem; top: -34rem; }
|
||
|
||
.role-select {
|
||
position: absolute;
|
||
top: 50%;
|
||
left: 45rem;
|
||
z-index: 8;
|
||
transform: translateY(-50%);
|
||
}
|
||
|
||
.role-track {
|
||
display: block;
|
||
}
|
||
|
||
.mobile-role-arrow {
|
||
display: none;
|
||
}
|
||
|
||
.role-option {
|
||
display: flex;
|
||
align-items: center;
|
||
height: 40rem;
|
||
margin: -4rem 0;
|
||
color: #fff;
|
||
font-family: dnf-vp, "Microsoft Yahei", sans-serif;
|
||
font-size: 15rem;
|
||
cursor: pointer;
|
||
background: transparent;
|
||
border: 0;
|
||
transition: color 0.22s ease, transform 0.22s ease, opacity 0.22s ease;
|
||
}
|
||
|
||
.role-option.active {
|
||
color: #20dff5;
|
||
transform: translateX(4rem);
|
||
}
|
||
|
||
.role-icon {
|
||
display: block;
|
||
width: 28rem;
|
||
height: 28rem;
|
||
margin-right: 12rem;
|
||
background: url("https://game.gtimg.cn/images/dnf/cp/a20250612vp/sp.png") 0 calc(var(--i) * -35rem) / 28rem 587rem no-repeat;
|
||
}
|
||
|
||
.role-option.active .role-icon {
|
||
width: 40rem;
|
||
height: 40rem;
|
||
margin-right: 6rem;
|
||
background-image: url("https://game.gtimg.cn/images/dnf/cp/a20250612vp/sp2.png");
|
||
background-position: 0 calc(var(--i) * -41.6rem);
|
||
background-size: 40rem 707rem;
|
||
}
|
||
|
||
.skill-panel {
|
||
position: absolute;
|
||
top: 150rem;
|
||
left: 280rem;
|
||
z-index: 9;
|
||
}
|
||
|
||
.panel-close,
|
||
.mobile-info {
|
||
display: none;
|
||
}
|
||
|
||
.tab-buttons {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 15rem;
|
||
}
|
||
|
||
.tab-buttons button {
|
||
display: block;
|
||
width: 228rem;
|
||
height: 61rem;
|
||
cursor: pointer;
|
||
background: url("https://game.gtimg.cn/images/dnf/cp/a20250612vp/tab1.png") center / 100% 100% no-repeat;
|
||
border: 0;
|
||
font-size: 0;
|
||
}
|
||
|
||
.tab-buttons button.active {
|
||
background-image: url("https://game.gtimg.cn/images/dnf/cp/a20250612vp/tab1-1.png");
|
||
}
|
||
|
||
.tab-buttons button:nth-child(2) {
|
||
background-image: url("https://game.gtimg.cn/images/dnf/cp/a20250612vp/tab2.png");
|
||
}
|
||
|
||
.tab-buttons button:nth-child(2).active {
|
||
background-image: url("https://game.gtimg.cn/images/dnf/cp/a20250612vp/tab2-1.png");
|
||
}
|
||
|
||
.tips {
|
||
display: flex;
|
||
align-items: center;
|
||
width: 1104rem;
|
||
height: 42rem;
|
||
margin: 0;
|
||
color: #fff;
|
||
font-size: 14rem;
|
||
}
|
||
|
||
.tab-main {
|
||
width: 1104rem;
|
||
height: 578rem;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.tab-skill {
|
||
padding: 25rem 35rem 0;
|
||
background: url("https://game.gtimg.cn/images/dnf/cp/a20250612vp/bg1.png") 0 0 / 100% no-repeat;
|
||
}
|
||
|
||
.skill-tabs {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
align-content: flex-start;
|
||
align-items: center;
|
||
height: 97rem;
|
||
padding: 0 15rem;
|
||
background: url("https://game.gtimg.cn/images/dnf/cp/a20250612vp/line.png") center bottom / auto 2rem no-repeat;
|
||
}
|
||
|
||
.skill-tabs button {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 188rem;
|
||
height: 35rem;
|
||
margin: 0 0 9rem;
|
||
color: #f0fefd;
|
||
font-size: 15rem;
|
||
line-height: 1;
|
||
cursor: pointer;
|
||
background: url("https://game.gtimg.cn/images/dnf/cp/a20250612vp/icon1.png") center / 100% 100% no-repeat;
|
||
border: 0;
|
||
}
|
||
|
||
.skill-tabs button.active {
|
||
color: #101b38;
|
||
background-image: url("https://game.gtimg.cn/images/dnf/cp/a20250612vp/icon2.png");
|
||
}
|
||
|
||
.skill-tabs span {
|
||
max-width: 130rem;
|
||
margin-left: 5rem;
|
||
}
|
||
|
||
.skill-cards {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
height: 425rem;
|
||
padding-left: 10rem;
|
||
overflow-y: auto;
|
||
}
|
||
|
||
.skill-card {
|
||
width: 330rem;
|
||
margin-right: 11rem;
|
||
color: #202029;
|
||
font-size: 14rem;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.skill-title {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
height: 92rem;
|
||
color: #101b38;
|
||
font-family: dnf-vp, "Microsoft Yahei", sans-serif;
|
||
font-size: 18rem;
|
||
text-align: center;
|
||
}
|
||
|
||
.skill-title img {
|
||
display: block;
|
||
height: 34rem;
|
||
margin-right: 10rem;
|
||
}
|
||
|
||
.video-thumb {
|
||
width: 328rem;
|
||
height: 195rem;
|
||
border: 1rem solid #101b38;
|
||
}
|
||
|
||
.video-thumb img,
|
||
.video-thumb video {
|
||
display: block;
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
background: #000;
|
||
}
|
||
|
||
.skill-card p {
|
||
padding: 0 20rem;
|
||
}
|
||
|
||
.tab-break {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 9rem 14rem;
|
||
padding: 25rem;
|
||
overflow-y: auto;
|
||
background: url("https://game.gtimg.cn/images/dnf/cp/a20250612vp/bg2.png") 0 0 / 100% 100% no-repeat;
|
||
}
|
||
|
||
.break-card {
|
||
display: flex;
|
||
width: 338rem;
|
||
height: 80rem;
|
||
color: #101b38;
|
||
font-size: 14rem;
|
||
background: #fff;
|
||
border: 1rem solid #989694;
|
||
}
|
||
|
||
.level {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 44rem;
|
||
border-right: 1rem solid #989694;
|
||
}
|
||
|
||
.break-icon {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 110rem;
|
||
border-right: 1rem solid #989694;
|
||
font-family: dnf-vp, "Microsoft Yahei", sans-serif;
|
||
line-height: 1;
|
||
text-align: center;
|
||
}
|
||
|
||
.break-icon img {
|
||
height: 28rem;
|
||
}
|
||
|
||
.break-info {
|
||
display: flex;
|
||
flex: 1;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
line-height: 1.2;
|
||
}
|
||
|
||
.break-info p {
|
||
margin: 0;
|
||
padding: 5rem 8rem;
|
||
}
|
||
|
||
.break-info p + p {
|
||
border-top: 1rem solid #989694;
|
||
}
|
||
|
||
.recommend-link {
|
||
display: block;
|
||
margin-top: 12rem;
|
||
color: #1ce8fb;
|
||
font-size: 18rem;
|
||
}
|
||
|
||
.mod-tips {
|
||
margin-top: 10rem;
|
||
color: #fff;
|
||
font-size: 14rem;
|
||
}
|
||
|
||
.mobile-slide-controls {
|
||
display: none;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.vp-detail {
|
||
--r: var(--dnf-detail-rem, 0.52px);
|
||
overflow: hidden;
|
||
background-position: center top;
|
||
background-size: cover;
|
||
}
|
||
|
||
.detail-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
padding: calc(60 * var(--r)) calc(26 * var(--r)) 0;
|
||
}
|
||
|
||
.detail-logo,
|
||
.back-button {
|
||
position: relative;
|
||
top: auto;
|
||
left: auto;
|
||
right: auto;
|
||
}
|
||
|
||
.detail-logo {
|
||
width: calc(357 * var(--r));
|
||
height: calc(60 * var(--r));
|
||
background-image: url("https://game.gtimg.cn/images/dnf/cp/a20250612vp/m/logo.png");
|
||
}
|
||
|
||
.back-button {
|
||
width: calc(194 * var(--r));
|
||
height: calc(45 * var(--r));
|
||
font-size: calc(18 * var(--r));
|
||
line-height: calc(45 * var(--r));
|
||
}
|
||
|
||
.person {
|
||
top: 0;
|
||
right: auto;
|
||
left: 50%;
|
||
z-index: 1;
|
||
width: calc(750 * var(--r));
|
||
height: calc(1624 * var(--r));
|
||
margin-left: calc(-375 * var(--r));
|
||
object-fit: contain;
|
||
}
|
||
|
||
.person-swap-enter-from {
|
||
transform: translate3d(calc(34 * var(--r)), 0, 0) scale(1.02);
|
||
}
|
||
|
||
.person-swap-leave-to {
|
||
transform: translate3d(calc(-24 * var(--r)), 0, 0) scale(0.99);
|
||
}
|
||
|
||
.mobile-info {
|
||
position: absolute;
|
||
bottom: calc(404 * var(--r));
|
||
left: 0;
|
||
z-index: 8;
|
||
display: block;
|
||
width: 100%;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.person-name {
|
||
width: auto;
|
||
min-width: calc(313 * var(--r));
|
||
height: calc(54 * var(--r));
|
||
margin: 0 auto;
|
||
padding: 0 calc(28 * var(--r));
|
||
color: #fff;
|
||
font-family: dnf-vp, "Microsoft Yahei", sans-serif;
|
||
font-size: calc(28 * var(--r));
|
||
font-style: italic;
|
||
line-height: calc(54 * var(--r));
|
||
text-align: center;
|
||
text-shadow: 0 2px 8px rgb(0 0 0 / 75%);
|
||
background: none;
|
||
}
|
||
|
||
.name-swap-enter-active,
|
||
.name-swap-leave-active {
|
||
transition: opacity 0.28s ease, transform 0.32s ease;
|
||
}
|
||
|
||
.name-swap-enter-from {
|
||
opacity: 0;
|
||
transform: translateY(calc(14 * var(--r)));
|
||
}
|
||
|
||
.name-swap-leave-to {
|
||
opacity: 0;
|
||
transform: translateY(calc(-8 * var(--r)));
|
||
}
|
||
|
||
.name-swap-enter-to,
|
||
.name-swap-leave-from {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
|
||
.mobile-info .tab-buttons {
|
||
justify-content: center;
|
||
gap: calc(15 * var(--r));
|
||
margin-top: calc(30 * var(--r));
|
||
pointer-events: auto;
|
||
}
|
||
|
||
.mobile-info .tab-buttons button {
|
||
width: calc(322 * var(--r));
|
||
height: calc(80 * var(--r));
|
||
background-image: url("https://game.gtimg.cn/images/dnf/cp/a20250612vp/m/tab1-b.png");
|
||
}
|
||
|
||
.mobile-info .tab-buttons button.active {
|
||
background-image: url("https://game.gtimg.cn/images/dnf/cp/a20250612vp/m/tab1-1-b.png");
|
||
}
|
||
|
||
.mobile-info .tab-buttons button:nth-child(2) {
|
||
background-image: url("https://game.gtimg.cn/images/dnf/cp/a20250612vp/m/tab2-b.png");
|
||
}
|
||
|
||
.mobile-info .tab-buttons button:nth-child(2).active {
|
||
background-image: url("https://game.gtimg.cn/images/dnf/cp/a20250612vp/m/tab2-1-b.png");
|
||
}
|
||
|
||
.avatar-wheel {
|
||
bottom: calc(198 * var(--r));
|
||
left: 0;
|
||
z-index: 6;
|
||
width: 100%;
|
||
height: calc(119 * var(--r));
|
||
text-align: center;
|
||
background: none;
|
||
}
|
||
|
||
.avatar-wheel::before {
|
||
position: absolute;
|
||
top: calc(58 * var(--r));
|
||
left: 0;
|
||
width: 100%;
|
||
height: calc(4 * var(--r));
|
||
content: "";
|
||
background: #13e6ff;
|
||
}
|
||
|
||
.avatar {
|
||
position: relative;
|
||
inset: auto !important;
|
||
display: inline-block;
|
||
width: calc(119 * var(--r));
|
||
height: calc(119 * var(--r));
|
||
vertical-align: top;
|
||
transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1), filter 0.3s ease;
|
||
}
|
||
|
||
.avatar:not(.active) {
|
||
transform: scale(0.82);
|
||
filter: saturate(0.85) brightness(0.88);
|
||
}
|
||
|
||
.avatar.active {
|
||
transform: scale(1.08);
|
||
filter: none;
|
||
}
|
||
|
||
.role-select {
|
||
top: auto;
|
||
right: calc(28 * var(--r));
|
||
bottom: calc(34 * var(--r));
|
||
left: calc(28 * var(--r));
|
||
z-index: 7;
|
||
display: flex;
|
||
align-items: flex-start;
|
||
height: calc(88 * var(--r));
|
||
overflow: visible;
|
||
transform: none;
|
||
border-bottom: 1px solid rgb(255 255 255 / 42%);
|
||
scrollbar-width: none;
|
||
}
|
||
|
||
.role-select::-webkit-scrollbar {
|
||
display: none;
|
||
}
|
||
|
||
.role-track {
|
||
display: flex;
|
||
flex: 1;
|
||
height: calc(78 * var(--r));
|
||
overflow-x: auto;
|
||
overflow-y: visible;
|
||
scroll-behavior: smooth;
|
||
scroll-snap-type: x proximity;
|
||
scrollbar-width: none;
|
||
}
|
||
|
||
.role-track::-webkit-scrollbar {
|
||
display: none;
|
||
}
|
||
|
||
.role-option {
|
||
position: relative;
|
||
flex: 0 0 calc(174 * var(--r));
|
||
justify-content: center;
|
||
height: calc(62 * var(--r));
|
||
margin: 0;
|
||
padding: 0;
|
||
color: #f4f2f2;
|
||
font-size: calc(24 * var(--r));
|
||
text-shadow: 0 2px 6px rgb(0 0 0 / 80%);
|
||
background: transparent;
|
||
border: 0;
|
||
scroll-snap-align: center;
|
||
transform: none;
|
||
}
|
||
|
||
.role-option.active {
|
||
color: #101013;
|
||
text-shadow: none;
|
||
background: url("https://game.gtimg.cn/images/dnf/cp/a20250612vp/m/name-bg2.png") center / 100% 100% no-repeat;
|
||
transform: translateY(calc(-4 * var(--r)));
|
||
}
|
||
|
||
.role-option.active::after {
|
||
position: absolute;
|
||
right: calc(18 * var(--r));
|
||
bottom: calc(-20 * var(--r));
|
||
left: calc(18 * var(--r));
|
||
height: calc(7 * var(--r));
|
||
content: "";
|
||
background: #1be8fc;
|
||
}
|
||
|
||
.mobile-role-arrow {
|
||
position: relative;
|
||
z-index: 2;
|
||
display: block;
|
||
flex: 0 0 calc(36 * var(--r));
|
||
width: calc(36 * var(--r));
|
||
height: calc(62 * var(--r));
|
||
padding: 0;
|
||
cursor: pointer;
|
||
background: transparent;
|
||
border: 0;
|
||
}
|
||
|
||
.mobile-role-arrow::before {
|
||
position: absolute;
|
||
top: 50%;
|
||
width: calc(18 * var(--r));
|
||
height: calc(30 * var(--r));
|
||
margin-top: calc(-15 * var(--r));
|
||
content: "";
|
||
background: center / 100% 100% no-repeat;
|
||
}
|
||
|
||
.mobile-role-prev::before {
|
||
left: 0;
|
||
background-image: url("https://game.gtimg.cn/images/dnf/cp/a20250612vp/m/icon-left.png");
|
||
}
|
||
|
||
.mobile-role-next::before {
|
||
right: 0;
|
||
background-image: url("https://game.gtimg.cn/images/dnf/cp/a20250612vp/m/icon-right.png");
|
||
}
|
||
|
||
.role-icon {
|
||
display: none;
|
||
}
|
||
|
||
.skill-panel {
|
||
position: fixed;
|
||
inset: 0;
|
||
z-index: 20;
|
||
pointer-events: none;
|
||
opacity: 0;
|
||
transform: translateY(100%);
|
||
transition: 0.35s;
|
||
}
|
||
|
||
.skill-panel.show {
|
||
pointer-events: auto;
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
|
||
.panel-inner {
|
||
position: absolute;
|
||
bottom: 0;
|
||
left: 0;
|
||
width: calc(750 * var(--r));
|
||
height: calc(1100 * var(--r));
|
||
padding-top: calc(40 * var(--r));
|
||
background: url("https://game.gtimg.cn/images/dnf/cp/a20250612vp/m/dia-bg.png") top / 100% auto no-repeat;
|
||
}
|
||
|
||
.panel-close {
|
||
position: absolute;
|
||
top: calc(-83 * var(--r));
|
||
right: calc(48 * var(--r));
|
||
display: block;
|
||
width: calc(62 * var(--r));
|
||
height: calc(62 * var(--r));
|
||
overflow: hidden;
|
||
text-indent: -999px;
|
||
background: url("https://game.gtimg.cn/images/dnf/cp/a20250612vp/m/dia-close.png") center / 100% 100% no-repeat;
|
||
border: 0;
|
||
}
|
||
|
||
.desktop-tabs {
|
||
justify-content: space-between;
|
||
width: calc(676 * var(--r));
|
||
margin: 0 auto;
|
||
}
|
||
|
||
.desktop-tabs button {
|
||
width: calc(326 * var(--r));
|
||
height: calc(85 * var(--r));
|
||
background-image: url("https://game.gtimg.cn/images/dnf/cp/a20250612vp/m/tab1.png");
|
||
}
|
||
|
||
.desktop-tabs button.active {
|
||
background-image: url("https://game.gtimg.cn/images/dnf/cp/a20250612vp/m/tab1-1.png");
|
||
}
|
||
|
||
.desktop-tabs button:nth-child(2) {
|
||
background-image: url("https://game.gtimg.cn/images/dnf/cp/a20250612vp/m/tab2.png");
|
||
}
|
||
|
||
.desktop-tabs button:nth-child(2).active {
|
||
background-image: url("https://game.gtimg.cn/images/dnf/cp/a20250612vp/m/tab2-1.png");
|
||
}
|
||
|
||
.tips {
|
||
width: calc(700 * var(--r));
|
||
height: calc(85 * var(--r));
|
||
margin: 0 auto;
|
||
color: #101b38;
|
||
font-size: calc(20 * var(--r));
|
||
line-height: 1.3;
|
||
}
|
||
|
||
.tab-main {
|
||
position: relative;
|
||
width: calc(700 * var(--r));
|
||
height: calc(810 * var(--r));
|
||
margin: 0 auto;
|
||
}
|
||
|
||
.tab-skill {
|
||
padding: 0;
|
||
overflow-y: auto;
|
||
background: none;
|
||
border-top: 1px solid #777;
|
||
}
|
||
|
||
.skill-tabs {
|
||
height: calc(220 * var(--r));
|
||
padding-top: calc(8 * var(--r));
|
||
background: none;
|
||
}
|
||
|
||
.skill-tabs button {
|
||
width: calc(233 * var(--r));
|
||
height: calc(53 * var(--r));
|
||
font-size: calc(20 * var(--r));
|
||
background-image: url("https://game.gtimg.cn/images/dnf/cp/a20250612vp/m/icon1.png");
|
||
}
|
||
|
||
.skill-tabs button.active {
|
||
background-image: url("https://game.gtimg.cn/images/dnf/cp/a20250612vp/m/icon2.png");
|
||
}
|
||
|
||
.skill-cards {
|
||
display: block;
|
||
height: auto;
|
||
padding: 0;
|
||
overflow: visible;
|
||
}
|
||
|
||
.skill-card {
|
||
display: none;
|
||
width: 100%;
|
||
margin: 0;
|
||
font-size: calc(24 * var(--r));
|
||
}
|
||
|
||
.skill-card.active {
|
||
display: block;
|
||
}
|
||
|
||
.skill-title {
|
||
height: calc(100 * var(--r));
|
||
font-size: calc(26 * var(--r));
|
||
}
|
||
|
||
.skill-title img {
|
||
height: calc(45 * var(--r));
|
||
}
|
||
|
||
.video-thumb {
|
||
width: calc(586 * var(--r));
|
||
height: calc(400 * var(--r));
|
||
margin: calc(10 * var(--r)) auto 0;
|
||
}
|
||
|
||
.tab-break {
|
||
display: block;
|
||
height: calc(830 * var(--r));
|
||
padding: calc(25 * var(--r)) 0 0;
|
||
overflow-y: auto;
|
||
background: none;
|
||
border-top: 3px solid #101b38;
|
||
border-bottom: 1px solid #777;
|
||
}
|
||
|
||
.break-card {
|
||
display: none;
|
||
width: calc(594 * var(--r));
|
||
height: calc(145 * var(--r));
|
||
margin: 0 auto calc(12 * var(--r));
|
||
font-size: calc(22 * var(--r));
|
||
}
|
||
|
||
.break-card.active {
|
||
display: flex;
|
||
}
|
||
|
||
.level {
|
||
width: calc(67 * var(--r));
|
||
}
|
||
|
||
.break-icon {
|
||
width: calc(156 * var(--r));
|
||
}
|
||
|
||
.break-icon img {
|
||
height: calc(48 * var(--r));
|
||
}
|
||
|
||
.recommend-link,
|
||
.mod-tips {
|
||
display: none;
|
||
}
|
||
|
||
.mobile-slide-controls {
|
||
position: absolute;
|
||
right: 0;
|
||
bottom: calc(16 * var(--r));
|
||
left: 0;
|
||
z-index: 2;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: calc(26 * var(--r));
|
||
color: #101b38;
|
||
font-family: dnf-vp, "Microsoft Yahei", sans-serif;
|
||
font-size: calc(24 * var(--r));
|
||
pointer-events: none;
|
||
}
|
||
|
||
.slide-arrow {
|
||
width: calc(52 * var(--r));
|
||
height: calc(52 * var(--r));
|
||
padding: 0;
|
||
cursor: pointer;
|
||
background: rgb(16 27 56 / 88%);
|
||
border: 1px solid rgb(28 232 251 / 70%);
|
||
pointer-events: auto;
|
||
}
|
||
|
||
.slide-arrow::before {
|
||
display: block;
|
||
width: calc(16 * var(--r));
|
||
height: calc(16 * var(--r));
|
||
margin: auto;
|
||
content: "";
|
||
border-top: calc(4 * var(--r)) solid #1ce8fb;
|
||
border-right: calc(4 * var(--r)) solid #1ce8fb;
|
||
}
|
||
|
||
.slide-prev::before {
|
||
transform: rotate(-135deg);
|
||
}
|
||
|
||
.slide-next::before {
|
||
transform: rotate(45deg);
|
||
}
|
||
}
|
||
</style>
|