mirror of
https://github.com/jiangrui1994/CloudSaver.git
synced 2026-01-08 14:18:46 +08:00
feat:增加项目鸣谢页
This commit is contained in:
18
backend/src/controllers/sponsors.ts
Normal file
18
backend/src/controllers/sponsors.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Request, Response } from "express";
|
||||
import { injectable, inject } from "inversify";
|
||||
import { TYPES } from "../core/types";
|
||||
import { SponsorsService } from "../services/SponsorsService";
|
||||
import { BaseController } from "./BaseController";
|
||||
|
||||
@injectable()
|
||||
export class SponsorsController extends BaseController {
|
||||
constructor(@inject(TYPES.SponsorsService) private sponsorsService: SponsorsService) {
|
||||
super();
|
||||
}
|
||||
|
||||
async get(req: Request, res: Response): Promise<void> {
|
||||
await this.handleRequest(req, res, async () => {
|
||||
return await this.sponsorsService.getSponsors();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ export const TYPES = {
|
||||
ImageService: Symbol.for("ImageService"),
|
||||
SettingService: Symbol.for("SettingService"),
|
||||
UserService: Symbol.for("UserService"),
|
||||
SponsorsService: Symbol.for("SponsorsService"),
|
||||
|
||||
Cloud115Controller: Symbol.for("Cloud115Controller"),
|
||||
QuarkController: Symbol.for("QuarkController"),
|
||||
@@ -15,4 +16,5 @@ export const TYPES = {
|
||||
ImageController: Symbol.for("ImageController"),
|
||||
SettingController: Symbol.for("SettingController"),
|
||||
UserController: Symbol.for("UserController"),
|
||||
SponsorsController: Symbol.for("SponsorsController"),
|
||||
};
|
||||
|
||||
@@ -10,7 +10,7 @@ import { DoubanService } from "./services/DoubanService";
|
||||
import { UserService } from "./services/UserService";
|
||||
import { ImageService } from "./services/ImageService";
|
||||
import { SettingService } from "./services/SettingService";
|
||||
|
||||
import { SponsorsService } from "./services/SponsorsService";
|
||||
// Controllers
|
||||
import { Cloud115Controller } from "./controllers/cloud115";
|
||||
import { QuarkController } from "./controllers/quark";
|
||||
@@ -19,7 +19,7 @@ import { DoubanController } from "./controllers/douban";
|
||||
import { ImageController } from "./controllers/teleImages";
|
||||
import { SettingController } from "./controllers/setting";
|
||||
import { UserController } from "./controllers/user";
|
||||
|
||||
import { SponsorsController } from "./controllers/sponsors";
|
||||
const container = new Container();
|
||||
|
||||
// Services
|
||||
@@ -31,7 +31,7 @@ container.bind<ImageService>(TYPES.ImageService).to(ImageService).inSingletonSco
|
||||
container.bind<SettingService>(TYPES.SettingService).to(SettingService).inSingletonScope();
|
||||
container.bind<DoubanService>(TYPES.DoubanService).to(DoubanService).inSingletonScope();
|
||||
container.bind<UserService>(TYPES.UserService).to(UserService).inSingletonScope();
|
||||
|
||||
container.bind<SponsorsService>(TYPES.SponsorsService).to(SponsorsService).inSingletonScope();
|
||||
// Controllers
|
||||
container.bind<Cloud115Controller>(TYPES.Cloud115Controller).to(Cloud115Controller);
|
||||
container.bind<QuarkController>(TYPES.QuarkController).to(QuarkController);
|
||||
@@ -40,5 +40,6 @@ container.bind<DoubanController>(TYPES.DoubanController).to(DoubanController);
|
||||
container.bind<ImageController>(TYPES.ImageController).to(ImageController);
|
||||
container.bind<SettingController>(TYPES.SettingController).to(SettingController);
|
||||
container.bind<UserController>(TYPES.UserController).to(UserController);
|
||||
container.bind<SponsorsController>(TYPES.SponsorsController).to(SponsorsController);
|
||||
|
||||
export { container };
|
||||
|
||||
@@ -8,6 +8,7 @@ import { DoubanController } from "../controllers/douban";
|
||||
import { ImageController } from "../controllers/teleImages";
|
||||
import { SettingController } from "../controllers/setting";
|
||||
import { UserController } from "../controllers/user";
|
||||
import { SponsorsController } from "../controllers/sponsors";
|
||||
|
||||
const router = Router();
|
||||
|
||||
@@ -19,6 +20,7 @@ const doubanController = container.get<DoubanController>(TYPES.DoubanController)
|
||||
const imageController = container.get<ImageController>(TYPES.ImageController);
|
||||
const settingController = container.get<SettingController>(TYPES.SettingController);
|
||||
const userController = container.get<UserController>(TYPES.UserController);
|
||||
const sponsorsController = container.get<SponsorsController>(TYPES.SponsorsController);
|
||||
|
||||
// 用户相关路由
|
||||
router.post("/user/login", (req, res) => userController.login(req, res));
|
||||
@@ -34,6 +36,9 @@ router.post("/setting/save", (req, res) => settingController.save(req, res));
|
||||
// 资源搜索
|
||||
router.get("/search", (req, res) => resourceController.search(req, res));
|
||||
|
||||
// 获取赞助者列表
|
||||
router.get("/sponsors", (req, res) => sponsorsController.get(req, res));
|
||||
|
||||
// 115网盘相关
|
||||
router.get("/cloud115/share-info", (req, res) => cloud115Controller.getShareInfo(req, res));
|
||||
router.get("/cloud115/folders", (req, res) => cloud115Controller.getFolderList(req, res));
|
||||
|
||||
25
backend/src/services/SponsorsService.ts
Normal file
25
backend/src/services/SponsorsService.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { injectable } from "inversify";
|
||||
import { createAxiosInstance } from "../utils/axiosInstance";
|
||||
import { AxiosInstance } from "axios";
|
||||
import sponsors from "../sponsors/sponsors.json";
|
||||
|
||||
@injectable()
|
||||
export class SponsorsService {
|
||||
private axiosInstance: AxiosInstance;
|
||||
|
||||
constructor() {
|
||||
this.axiosInstance = createAxiosInstance("http://oss.jiangmuxin.cn/cloudsaver/");
|
||||
}
|
||||
async getSponsors() {
|
||||
try {
|
||||
const response = await this.axiosInstance.get("sponsors.json");
|
||||
return {
|
||||
data: response.data.sponsors,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
data: sponsors.sponsors,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
54
backend/src/sponsors/sponsors.json
Normal file
54
backend/src/sponsors/sponsors.json
Normal file
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"sponsors": [
|
||||
{
|
||||
"name": "立本狗头",
|
||||
"avatar": "http://oss.jiangmuxin.cn/cloudsaver/sponsors/thanks1.jpg",
|
||||
"message": "怒搓楼上狗头! "
|
||||
},
|
||||
{
|
||||
"name": "帝国鼻屎",
|
||||
"avatar": "http://oss.jiangmuxin.cn/cloudsaver/sponsors/thanks2.jpg",
|
||||
"message": "芜湖起飞! "
|
||||
},
|
||||
{
|
||||
"name": "雷霆222",
|
||||
"avatar": "http://oss.jiangmuxin.cn/cloudsaver/sponsors/thanks3.jpg",
|
||||
"message": "把我弄帅点 "
|
||||
},
|
||||
{
|
||||
"name": "黑田奈奈子",
|
||||
"avatar": "http://oss.jiangmuxin.cn/cloudsaver/sponsors/thanks4.jpg",
|
||||
"message": "流年笑掷 未来可期 ",
|
||||
"link": "https://github.com/htnanako"
|
||||
},
|
||||
{
|
||||
"name": "原野🐇",
|
||||
"avatar": "http://oss.jiangmuxin.cn/cloudsaver/sponsors/thanks5.jpg"
|
||||
},
|
||||
{
|
||||
"name": "我摆烂!",
|
||||
"avatar": "http://oss.jiangmuxin.cn/cloudsaver/sponsors/thanks6.jpg",
|
||||
"message": "人生苦短,及时行乐,卷什么卷,随缘摆烂 "
|
||||
},
|
||||
{
|
||||
"name": "田培",
|
||||
"avatar": "http://oss.jiangmuxin.cn/cloudsaver/sponsors/thanks7.jpg"
|
||||
},
|
||||
{
|
||||
"name": "River",
|
||||
"avatar": "http://oss.jiangmuxin.cn/cloudsaver/sponsors/thanks8.jpg"
|
||||
},
|
||||
{
|
||||
"name": "午夜学徒",
|
||||
"avatar": "http://oss.jiangmuxin.cn/cloudsaver/sponsors/thanks9.jpg"
|
||||
},
|
||||
{
|
||||
"name": "阿潘",
|
||||
"avatar": "http://oss.jiangmuxin.cn/cloudsaver/sponsors/thanks10.jpg"
|
||||
},
|
||||
{
|
||||
"name": "闹闹黑",
|
||||
"avatar": "http://oss.jiangmuxin.cn/cloudsaver/sponsors/thanks11.jpg"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -8,7 +8,7 @@ interface ProxyConfig {
|
||||
|
||||
export function createAxiosInstance(
|
||||
baseURL: string,
|
||||
headers: AxiosRequestHeaders,
|
||||
headers?: AxiosRequestHeaders,
|
||||
useProxy: boolean = false,
|
||||
proxyConfig?: ProxyConfig
|
||||
): AxiosInstance {
|
||||
|
||||
4
frontend/components.d.ts
vendored
4
frontend/components.d.ts
vendored
@@ -9,6 +9,7 @@ declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
AsideMenu: typeof import('./src/components/AsideMenu.vue')['default']
|
||||
ElAside: typeof import('element-plus/es')['ElAside']
|
||||
ElAvatar: typeof import('element-plus/es')['ElAvatar']
|
||||
ElBacktop: typeof import('element-plus/es')['ElBacktop']
|
||||
ElButton: typeof import('element-plus/es')['ElButton']
|
||||
ElCard: typeof import('element-plus/es')['ElCard']
|
||||
@@ -50,15 +51,12 @@ declare module 'vue' {
|
||||
VanCheckbox: typeof import('vant/es')['Checkbox']
|
||||
VanCheckboxGroup: typeof import('vant/es')['CheckboxGroup']
|
||||
VanEmpty: typeof import('vant/es')['Empty']
|
||||
VanField: typeof import('vant/es')['Field']
|
||||
VanForm: typeof import('vant/es')['Form']
|
||||
VanIcon: typeof import('vant/es')['Icon']
|
||||
VanImage: typeof import('vant/es')['Image']
|
||||
VanLoading: typeof import('vant/es')['Loading']
|
||||
VanOverlay: typeof import('vant/es')['Overlay']
|
||||
VanPopup: typeof import('vant/es')['Popup']
|
||||
VanSearch: typeof import('vant/es')['Search']
|
||||
VanSwitch: typeof import('vant/es')['Switch']
|
||||
VanTab: typeof import('vant/es')['Tab']
|
||||
VanTabbar: typeof import('vant/es')['Tabbar']
|
||||
VanTabbarItem: typeof import('vant/es')['TabbarItem']
|
||||
|
||||
@@ -12,8 +12,10 @@
|
||||
"@element-plus/icons-vue": "^2.3.1",
|
||||
"axios": "^1.6.7",
|
||||
"element-plus": "^2.6.1",
|
||||
"gsap": "^3.12.7",
|
||||
"pinia": "^2.1.7",
|
||||
"socket.io-client": "^4.8.1",
|
||||
"typeit": "^8.8.7",
|
||||
"vant": "^4.9.17",
|
||||
"vue": "^3.4.21",
|
||||
"vue-router": "^4.3.0"
|
||||
|
||||
@@ -7,4 +7,7 @@ export const userApi = {
|
||||
register: (data: { username: string; password: string; registerCode: string }) => {
|
||||
return request.post<{ token: string }>("/api/user/register", data);
|
||||
},
|
||||
getSponsors: () => {
|
||||
return request.get("/api/sponsors?timestamp=" + Date.now());
|
||||
},
|
||||
};
|
||||
|
||||
@@ -45,12 +45,7 @@
|
||||
|
||||
<!-- GitHub 链接 -->
|
||||
<div class="pc-aside__footer">
|
||||
<a
|
||||
href="https://github.com/jiangrui1994/CloudSaver"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="github-link"
|
||||
>
|
||||
<a :href="PROJECT_GITHUB" target="_blank" rel="noopener noreferrer" class="github-link">
|
||||
<svg
|
||||
height="20"
|
||||
aria-hidden="true"
|
||||
@@ -76,6 +71,7 @@ import { computed } from "vue";
|
||||
import { useRouter, useRoute } from "vue-router";
|
||||
import { Search, Film, Setting, Link } from "@element-plus/icons-vue";
|
||||
import logo from "@/assets/images/logo.png";
|
||||
import { PROJECT_GITHUB } from "@/constants/project";
|
||||
import pkg from "../../package.json";
|
||||
|
||||
// 类型定义
|
||||
@@ -134,6 +130,12 @@ const menuList: MenuItem[] = [
|
||||
router: "/setting",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
index: "4",
|
||||
title: "鸣谢",
|
||||
icon: Link,
|
||||
router: "/thanks",
|
||||
},
|
||||
];
|
||||
|
||||
// 计算当前激活的菜单
|
||||
|
||||
2
frontend/src/constants/project.ts
Normal file
2
frontend/src/constants/project.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export const PROJECT_NAME = "Cloudsaver";
|
||||
export const PROJECT_GITHUB = "https://github.com/jiangrui1994/cloudsaver";
|
||||
@@ -21,6 +21,11 @@ const routes: RouteRecordRaw[] = [
|
||||
name: "setting",
|
||||
component: () => import("@/views/mobile/Setting.vue"),
|
||||
},
|
||||
{
|
||||
path: "/thanks",
|
||||
name: "thanks",
|
||||
redirect: "/resource",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -21,6 +21,11 @@ const routes: RouteRecordRaw[] = [
|
||||
name: "setting",
|
||||
component: () => import("@/views/Setting.vue"),
|
||||
},
|
||||
{
|
||||
path: "/thanks",
|
||||
name: "thanks",
|
||||
component: () => import("@/views/Thanks.vue"),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
927
frontend/src/views/Thanks.vue
Normal file
927
frontend/src/views/Thanks.vue
Normal file
@@ -0,0 +1,927 @@
|
||||
<template>
|
||||
<div ref="containerRef" class="thanks-container">
|
||||
<div ref="titleRef" class="title">感谢Ta们对项目的赞赏</div>
|
||||
|
||||
<!-- 添加说明文字 -->
|
||||
<div class="description">
|
||||
<p>感谢每一位支持者的信任与鼓励</p>
|
||||
<p>正是你们的支持让这个项目能够持续发展</p>
|
||||
</div>
|
||||
|
||||
<div ref="sponsorsContainer" class="sponsors-container">
|
||||
<div
|
||||
v-for="(sponsor, index) in randomizedSponsors"
|
||||
:key="sponsor.name"
|
||||
ref="avatarRefs"
|
||||
class="sponsor-avatar"
|
||||
@mouseenter="handleMouseEnter(index)"
|
||||
@mouseleave="handleMouseLeave"
|
||||
>
|
||||
<div
|
||||
ref="avatarWrapperRefs"
|
||||
class="avatar-wrapper"
|
||||
:class="{
|
||||
active: activeIndex === index,
|
||||
'has-link': sponsor.link,
|
||||
}"
|
||||
@click="handleAvatarClick(sponsor.link)"
|
||||
>
|
||||
<div class="avatar-inner">
|
||||
<div class="avatar-overlay"></div>
|
||||
<img :src="sponsor.avatar" :alt="sponsor.name" class="avatar-img" />
|
||||
<div class="name-tag">
|
||||
{{ sponsor.name }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="activeIndex === index && sponsor.message" class="dialog-box">
|
||||
<div class="dialog-content">
|
||||
<div :id="`typeIt-${index}`" class="type-it-container"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 添加赞赏按钮 -->
|
||||
<a
|
||||
:href="PROJECT_GITHUB + '?tab=readme-ov-file#支持项目'"
|
||||
target="_blank"
|
||||
class="sponsor-button"
|
||||
@mouseenter="handleButtonHover"
|
||||
@mouseleave="handleButtonLeave"
|
||||
>
|
||||
<div class="button-content">
|
||||
<svg class="heart-icon" viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"
|
||||
/>
|
||||
</svg>
|
||||
<span>赞赏支持</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, nextTick, computed, onBeforeUnmount } from "vue";
|
||||
import TypeIt from "typeit";
|
||||
import { userApi } from "@/api/user";
|
||||
import gsap from "gsap";
|
||||
import { PROJECT_GITHUB } from "@/constants/project";
|
||||
// 赞助者数据
|
||||
const sponsors = ref([]);
|
||||
|
||||
const getSponsors = async () => {
|
||||
const res = await userApi.getSponsors();
|
||||
sponsors.value = res.data;
|
||||
};
|
||||
|
||||
// 随机排序赞助者
|
||||
const randomizedSponsors = computed(() => {
|
||||
// 有sort的按照sort排序并排在前面,没有的按照随机排序
|
||||
const sortedSponsors = [...sponsors.value]
|
||||
.filter((item) => item.sort)
|
||||
.sort((a, b) => a.sort - b.sort);
|
||||
const randomSponsors = [...sponsors.value]
|
||||
.filter((item) => !item.sort)
|
||||
.sort(() => Math.random() - 0.5);
|
||||
return [...sortedSponsors, ...randomSponsors];
|
||||
});
|
||||
|
||||
const containerRef = ref(null);
|
||||
const sponsorsContainer = ref(null);
|
||||
const activeIndex = ref(null);
|
||||
const avatarRefs = ref([]);
|
||||
const avatarWrapperRefs = ref([]);
|
||||
let typeItInstance = null;
|
||||
const activeCenter = ref({ x: 0, y: 0 });
|
||||
const titleRef = ref(null);
|
||||
|
||||
// 添加头像动画时间轴的引用
|
||||
const avatarTimelines = ref([]);
|
||||
|
||||
// 使用 requestAnimationFrame 优化动画更新
|
||||
let rafId = null;
|
||||
|
||||
// 添加一个变量来跟踪当前激活的头像
|
||||
let currentHoverIndex = null;
|
||||
|
||||
onMounted(async () => {
|
||||
await getSponsors();
|
||||
// 初始化每个头像的动画时间轴
|
||||
avatarWrapperRefs.value.forEach((wrapper, index) => {
|
||||
const tl = gsap.timeline({
|
||||
repeat: -1,
|
||||
defaults: { ease: "power1.inOut" },
|
||||
paused: true,
|
||||
});
|
||||
|
||||
// 创建浮动动画 - 使用相对值而不是绝对值
|
||||
tl.to(wrapper, {
|
||||
yPercent: -10, // 使用百分比
|
||||
rotation: 2,
|
||||
duration: 1.5,
|
||||
overwrite: "auto", // 添加覆盖设置
|
||||
})
|
||||
.to(wrapper, {
|
||||
yPercent: 0,
|
||||
rotation: 0,
|
||||
duration: 1.5,
|
||||
overwrite: "auto",
|
||||
})
|
||||
.to(wrapper, {
|
||||
yPercent: 10,
|
||||
rotation: -2,
|
||||
duration: 1.5,
|
||||
overwrite: "auto",
|
||||
})
|
||||
.to(wrapper, {
|
||||
yPercent: 0,
|
||||
rotation: 0,
|
||||
duration: 1.5,
|
||||
overwrite: "auto",
|
||||
});
|
||||
|
||||
// 存储时间轴引用
|
||||
avatarTimelines.value[index] = tl;
|
||||
|
||||
// 设置随机的起始时间并播放
|
||||
tl.progress(Math.random()).play();
|
||||
});
|
||||
|
||||
// 修改页面入场动画
|
||||
const tl = gsap.timeline({
|
||||
defaults: { ease: "power3.out" },
|
||||
});
|
||||
|
||||
// 同时执行所有元素的动画
|
||||
tl.from([titleRef.value, sponsorsContainer.value, ...avatarWrapperRefs.value], {
|
||||
y: -20,
|
||||
opacity: 0,
|
||||
duration: 0.6,
|
||||
stagger: {
|
||||
amount: 0.3, // 所有元素在0.3秒内完成错开动画
|
||||
from: "start",
|
||||
},
|
||||
ease: "back.out(1.2)",
|
||||
onComplete: () => {
|
||||
// 动画完成后启动浮动动画
|
||||
avatarTimelines.value.forEach((timeline) => {
|
||||
if (timeline) {
|
||||
timeline.progress(Math.random()).play();
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
// 背景圆圈动画
|
||||
gsap.to(".gradient-circle", {
|
||||
rotation: 360,
|
||||
duration: 20,
|
||||
repeat: -1,
|
||||
ease: "none",
|
||||
stagger: {
|
||||
each: 5,
|
||||
from: "random",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
// 修改鼠标移入处理函数
|
||||
const handleMouseEnter = (() => {
|
||||
let timeout;
|
||||
return async (index) => {
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
currentHoverIndex = index;
|
||||
activeIndex.value = index;
|
||||
|
||||
timeout = setTimeout(async () => {
|
||||
// 确保这是最新的hover状态
|
||||
if (currentHoverIndex !== index) return;
|
||||
|
||||
const activeAvatar = avatarWrapperRefs.value[index];
|
||||
if (activeAvatar) {
|
||||
const rect = activeAvatar.getBoundingClientRect();
|
||||
activeCenter.value = {
|
||||
x: rect.left + rect.width / 2,
|
||||
y: rect.top + rect.height / 2,
|
||||
};
|
||||
}
|
||||
|
||||
// 暂停所有浮动动画
|
||||
avatarTimelines.value.forEach((timeline) => {
|
||||
if (timeline) {
|
||||
timeline.pause();
|
||||
}
|
||||
});
|
||||
|
||||
updateAvatarsEffect(index);
|
||||
|
||||
await nextTick();
|
||||
|
||||
try {
|
||||
// 初始化打字效果
|
||||
if (typeItInstance) {
|
||||
typeItInstance.destroy();
|
||||
typeItInstance = null;
|
||||
}
|
||||
|
||||
const typeItElement = document.getElementById(`typeIt-${index}`);
|
||||
if (typeItElement) {
|
||||
typeItInstance = new TypeIt(typeItElement, {
|
||||
strings: randomizedSponsors.value[index].message,
|
||||
speed: 20,
|
||||
waitUntilVisible: true,
|
||||
}).go();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("TypeIt初始化错误:", error);
|
||||
}
|
||||
}, 16);
|
||||
};
|
||||
})();
|
||||
|
||||
// 更新所有头像效果
|
||||
const updateAvatarsEffect = (activeIndex) => {
|
||||
if (!avatarWrapperRefs.value || activeCenter.value.x === 0) return;
|
||||
|
||||
if (rafId) {
|
||||
cancelAnimationFrame(rafId);
|
||||
}
|
||||
|
||||
rafId = requestAnimationFrame(() => {
|
||||
avatarWrapperRefs.value.forEach((wrapper, index) => {
|
||||
const inner = wrapper.querySelector(".avatar-inner");
|
||||
const avatarContainer = wrapper.closest(".sponsor-avatar");
|
||||
|
||||
if (index === activeIndex) {
|
||||
gsap.to(inner, {
|
||||
scale: 1.2,
|
||||
y: -15,
|
||||
zIndex: 10,
|
||||
duration: 0.5,
|
||||
ease: "back.out(1.7)",
|
||||
force3D: true,
|
||||
});
|
||||
|
||||
// 增强激活头像的阴影
|
||||
gsap.to(avatarContainer, {
|
||||
filter: "drop-shadow(0 20px 30px rgba(0, 0, 0, 0.25))",
|
||||
duration: 0.5,
|
||||
});
|
||||
|
||||
const activeOverlay = wrapper.querySelector(".avatar-overlay");
|
||||
gsap.to(activeOverlay, {
|
||||
opacity: 0,
|
||||
duration: 0.3,
|
||||
onComplete: () => {
|
||||
activeOverlay.style.background = "none";
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取当前头像的位置
|
||||
const rect = wrapper.getBoundingClientRect();
|
||||
const centerX = rect.left + rect.width / 2;
|
||||
const centerY = rect.top + rect.height / 2;
|
||||
const deltaX = activeCenter.value.x - centerX;
|
||||
const deltaY = activeCenter.value.y - centerY;
|
||||
const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
||||
|
||||
// 计算效果强度
|
||||
const maxDistance = 800;
|
||||
const strength = Math.max(0, 1 - distance / maxDistance);
|
||||
const attractionCurve = Math.pow(strength, 1.2);
|
||||
|
||||
// 计算阴影偏移和强度
|
||||
const shadowOffsetX = (deltaX / distance) * 15 * strength;
|
||||
const shadowOffsetY = Math.max(6, (deltaY / distance) * 20 * strength + 6);
|
||||
const shadowBlur = 12 + 18 * strength;
|
||||
const shadowOpacity = 0.15 + 0.1 * strength;
|
||||
|
||||
// 更新阴影效果
|
||||
gsap.to(avatarContainer, {
|
||||
filter: `drop-shadow(${shadowOffsetX}px ${shadowOffsetY}px ${shadowBlur}px rgba(0, 0, 0, ${shadowOpacity}))`,
|
||||
duration: 0.5,
|
||||
});
|
||||
|
||||
// 更新变换效果
|
||||
gsap.to(inner, {
|
||||
rotation: 0,
|
||||
scale: 1 + 0.05 * strength,
|
||||
x: (deltaX / distance) * 30 * attractionCurve,
|
||||
y: (deltaY / distance) * 30 * attractionCurve,
|
||||
rotationX: -Math.atan2(deltaY, distance) * (180 / Math.PI) * strength,
|
||||
rotationY: Math.atan2(deltaX, distance) * (180 / Math.PI) * strength,
|
||||
duration: 0.5,
|
||||
ease: "power2.out",
|
||||
force3D: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// 修改鼠标移出处理函数
|
||||
const handleMouseLeave = () => {
|
||||
currentHoverIndex = null;
|
||||
activeIndex.value = null;
|
||||
activeCenter.value = { x: 0, y: 0 };
|
||||
|
||||
if (!avatarWrapperRefs.value) return;
|
||||
|
||||
// 立即停止所有正在进行的动画
|
||||
avatarWrapperRefs.value.forEach((wrapper) => {
|
||||
const inner = wrapper.querySelector(".avatar-inner");
|
||||
gsap.killTweensOf(inner);
|
||||
});
|
||||
|
||||
// 重置所有头像状态
|
||||
resetAllAvatars();
|
||||
};
|
||||
|
||||
// 添加重置所有头像的函数
|
||||
const resetAllAvatars = () => {
|
||||
if (!avatarWrapperRefs.value) return;
|
||||
|
||||
avatarWrapperRefs.value.forEach((wrapper, index) => {
|
||||
const inner = wrapper.querySelector(".avatar-inner");
|
||||
const avatarContainer = wrapper.closest(".sponsor-avatar");
|
||||
|
||||
// 重置变换状态
|
||||
gsap.set(inner, {
|
||||
scale: 1,
|
||||
x: 0,
|
||||
y: 0,
|
||||
rotation: 0,
|
||||
rotationX: 0,
|
||||
rotationY: 0,
|
||||
yPercent: 0,
|
||||
});
|
||||
|
||||
// 恢复默认阴影
|
||||
gsap.to(avatarContainer, {
|
||||
filter: "drop-shadow(0 6px 12px rgba(0, 0, 0, 0.15))",
|
||||
duration: 0.3,
|
||||
});
|
||||
|
||||
// 重置蒙层
|
||||
const overlayElement = wrapper.querySelector(".avatar-overlay");
|
||||
gsap.to(overlayElement, {
|
||||
opacity: 1,
|
||||
duration: 0.3,
|
||||
onComplete: () => {
|
||||
overlayElement.style.background = `
|
||||
linear-gradient(
|
||||
135deg,
|
||||
rgba(255, 255, 255, 0.2) 0%,
|
||||
rgba(255, 255, 255, 0) 50%,
|
||||
rgba(0, 0, 0, 0.1) 100%
|
||||
)
|
||||
`;
|
||||
},
|
||||
});
|
||||
|
||||
// 重新创建并启动浮动动画
|
||||
const tl = gsap.timeline({
|
||||
repeat: -1,
|
||||
defaults: { ease: "power1.inOut" },
|
||||
});
|
||||
|
||||
tl.to(inner, {
|
||||
yPercent: -10,
|
||||
rotation: 2,
|
||||
duration: 1.5,
|
||||
overwrite: "auto",
|
||||
})
|
||||
.to(inner, {
|
||||
yPercent: 0,
|
||||
rotation: 0,
|
||||
duration: 1.5,
|
||||
overwrite: "auto",
|
||||
})
|
||||
.to(inner, {
|
||||
yPercent: 10,
|
||||
rotation: -2,
|
||||
duration: 1.5,
|
||||
overwrite: "auto",
|
||||
})
|
||||
.to(inner, {
|
||||
yPercent: 0,
|
||||
rotation: 0,
|
||||
duration: 1.5,
|
||||
overwrite: "auto",
|
||||
});
|
||||
|
||||
avatarTimelines.value[index] = tl;
|
||||
tl.progress(Math.random()).play();
|
||||
});
|
||||
};
|
||||
|
||||
// 添加窗口失焦事件处理
|
||||
window.addEventListener("blur", handleMouseLeave);
|
||||
|
||||
// 添加点击处理函数
|
||||
const handleAvatarClick = (link) => {
|
||||
if (link) {
|
||||
window.open(link, "_blank");
|
||||
}
|
||||
};
|
||||
|
||||
// 组件卸载时清理
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener("blur", handleMouseLeave);
|
||||
// 清理动画时间轴
|
||||
avatarTimelines.value.forEach((timeline) => {
|
||||
if (timeline) {
|
||||
timeline.kill();
|
||||
}
|
||||
});
|
||||
avatarTimelines.value = [];
|
||||
|
||||
if (typeItInstance) {
|
||||
typeItInstance.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
// 添加按钮悬浮效果
|
||||
const handleButtonHover = () => {
|
||||
gsap.to(".sponsor-button", {
|
||||
scale: 1.05,
|
||||
duration: 0.3,
|
||||
ease: "power2.out",
|
||||
});
|
||||
};
|
||||
|
||||
const handleButtonLeave = () => {
|
||||
gsap.to(".sponsor-button", {
|
||||
scale: 1,
|
||||
duration: 0.3,
|
||||
ease: "power2.out",
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.thanks-container {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
height: calc(100vh - 100px);
|
||||
overflow: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 40px 20px;
|
||||
background: linear-gradient(135deg, #f6f8fd 0%, #f1f4f9 100%);
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
transform: translateZ(0);
|
||||
will-change: transform;
|
||||
backface-visibility: hidden;
|
||||
}
|
||||
|
||||
.gradient-circle {
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
filter: blur(40px);
|
||||
opacity: 0.5;
|
||||
will-change: transform;
|
||||
backface-visibility: hidden;
|
||||
transform: translateZ(0);
|
||||
}
|
||||
|
||||
.circle-1 {
|
||||
width: 600px;
|
||||
height: 600px;
|
||||
background: linear-gradient(45deg, rgba(142, 68, 173, 0.2), rgba(91, 177, 235, 0.2));
|
||||
top: -200px;
|
||||
left: -200px;
|
||||
}
|
||||
|
||||
.circle-2 {
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
background: linear-gradient(45deg, rgba(91, 177, 235, 0.2), rgba(142, 68, 173, 0.2));
|
||||
bottom: -150px;
|
||||
right: -150px;
|
||||
}
|
||||
|
||||
.circle-3 {
|
||||
width: 400px;
|
||||
height: 400px;
|
||||
background: linear-gradient(45deg, rgba(241, 196, 15, 0.1), rgba(142, 68, 173, 0.1));
|
||||
top: 40%;
|
||||
left: 30%;
|
||||
}
|
||||
|
||||
/* 装饰层 */
|
||||
.decoration-layer {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.floating-dot {
|
||||
position: absolute;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
background: rgba(142, 68, 173, 0.2);
|
||||
border-radius: 50%;
|
||||
animation: floatingDot 8s ease-in-out infinite;
|
||||
animation-delay: var(--delay);
|
||||
will-change: transform;
|
||||
backface-visibility: hidden;
|
||||
transform: translateZ(0);
|
||||
}
|
||||
|
||||
@keyframes floatingDot {
|
||||
0%,
|
||||
100% {
|
||||
transform: translate(0, 0);
|
||||
}
|
||||
25% {
|
||||
transform: translate(100px, 50px);
|
||||
}
|
||||
50% {
|
||||
transform: translate(50px, 100px);
|
||||
}
|
||||
75% {
|
||||
transform: translate(-50px, 50px);
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-bottom: 20px;
|
||||
font-size: 50px;
|
||||
color: #2c3e50;
|
||||
text-align: center;
|
||||
font-weight: 700;
|
||||
background: linear-gradient(45deg, #8e44ad, #3498db);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);
|
||||
letter-spacing: 1px;
|
||||
will-change: transform, opacity;
|
||||
backface-visibility: hidden;
|
||||
transform: translateZ(0);
|
||||
}
|
||||
|
||||
.sponsors-container {
|
||||
width: 70%;
|
||||
max-width: 1200px;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
||||
grid-gap: 40px;
|
||||
justify-content: center;
|
||||
padding: 20px;
|
||||
will-change: transform, opacity;
|
||||
backface-visibility: hidden;
|
||||
transform: translateZ(0);
|
||||
opacity: 1; /* 确保容器默认可见 */
|
||||
}
|
||||
|
||||
.sponsor-avatar {
|
||||
position: relative;
|
||||
transform-style: preserve-3d;
|
||||
transition: transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding-bottom: 20px;
|
||||
filter: drop-shadow(0 6px 12px rgba(0, 0, 0, 0.15));
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.avatar-wrapper {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.avatar-inner {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
border: 4px solid #ffffff;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||
transition:
|
||||
transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1),
|
||||
filter 0.3s ease;
|
||||
cursor: pointer;
|
||||
will-change: transform;
|
||||
backface-visibility: hidden;
|
||||
transform: translateZ(0);
|
||||
position: relative;
|
||||
isolation: isolate;
|
||||
transform-style: preserve-3d;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.avatar-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(255, 255, 255, 0.2) 0%,
|
||||
rgba(255, 255, 255, 0) 50%,
|
||||
rgba(0, 0, 0, 0.1) 100%
|
||||
);
|
||||
opacity: 1;
|
||||
transition: all 0.3s ease;
|
||||
pointer-events: none;
|
||||
z-index: 3;
|
||||
mix-blend-mode: overlay; /* 添加混合模式增强效果 */
|
||||
}
|
||||
|
||||
.avatar-wrapper.active {
|
||||
transform: scale(1.2) translateY(-10px);
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.avatar-wrapper.has-link {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.avatar-wrapper.has-link::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: -4px;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(45deg, #ff3366, #ff6b6b, #4ecdc4, #45b7d1, #96e6a1);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
z-index: -1;
|
||||
filter: blur(8px);
|
||||
}
|
||||
|
||||
.avatar-wrapper.has-link:hover::before {
|
||||
opacity: 0.8;
|
||||
animation: borderGlow 2s linear infinite;
|
||||
}
|
||||
|
||||
.glow-effect {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: 50%;
|
||||
background: transparent;
|
||||
border: 2px solid transparent;
|
||||
transition: all 0.3s ease;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.avatar-wrapper.has-link:hover .glow-effect {
|
||||
border-color: rgba(255, 255, 255, 0.5);
|
||||
box-shadow:
|
||||
0 0 20px rgba(255, 255, 255, 0.3),
|
||||
inset 0 0 20px rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
@keyframes borderGlow {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* 确保激活状态下的发光效果仍然可见 */
|
||||
.avatar-wrapper.active.has-link::before {
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.avatar-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
object-fit: cover;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.dialog-box {
|
||||
position: absolute;
|
||||
top: -120px; /* 稍微上调对话框位置 */
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(8px);
|
||||
padding: 16px 20px;
|
||||
border-radius: 16px;
|
||||
box-shadow:
|
||||
0 4px 24px -1px rgba(0, 0, 0, 0.1),
|
||||
0 2px 8px -1px rgba(0, 0, 0, 0.06),
|
||||
inset 0 0 0 1px rgba(255, 255, 255, 0.5),
|
||||
0 0 40px rgba(142, 68, 173, 0.05);
|
||||
min-width: 180px;
|
||||
z-index: 111;
|
||||
opacity: 0;
|
||||
animation: dialogFadeIn 0.4s cubic-bezier(0.16, 1, 0.3, 1) forwards;
|
||||
border: 1px solid rgba(0, 0, 0, 0.05);
|
||||
will-change: transform, opacity;
|
||||
backface-visibility: hidden;
|
||||
transform: translateZ(0);
|
||||
}
|
||||
|
||||
.dialog-content {
|
||||
position: relative;
|
||||
font-size: 15px;
|
||||
line-height: 1.6;
|
||||
color: #2c3e50;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
text-shadow: 0 1px 1px rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
/* 修改引号装饰的样式 */
|
||||
.dialog-content::before,
|
||||
.dialog-content::after {
|
||||
content: '"';
|
||||
position: absolute;
|
||||
font-size: 28px;
|
||||
color: #8e44ad;
|
||||
opacity: 0.15;
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
.dialog-content::before {
|
||||
left: -15px;
|
||||
top: -12px;
|
||||
}
|
||||
|
||||
.dialog-content::after {
|
||||
right: -15px;
|
||||
bottom: -24px;
|
||||
}
|
||||
|
||||
/* 优化淡入动画,使其更加流畅 */
|
||||
@keyframes dialogFadeIn {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateX(-50%) translateY(20px) scale(0.95);
|
||||
filter: blur(2px);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateX(-50%) translateY(0) scale(1);
|
||||
filter: blur(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 优化打字效果容器样式 */
|
||||
.type-it-container {
|
||||
min-height: 24px;
|
||||
padding: 4px 8px;
|
||||
position: relative;
|
||||
background: linear-gradient(to bottom, rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0));
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
/* 添加打字光标样式 */
|
||||
.ti-cursor {
|
||||
color: #8e44ad;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.name-tag {
|
||||
position: absolute;
|
||||
bottom: -10px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
text-align: center;
|
||||
color: #2c3e50;
|
||||
opacity: 1;
|
||||
font-weight: 500;
|
||||
font-size: 12px;
|
||||
z-index: 20;
|
||||
background-color: #fff;
|
||||
border-radius: 15px;
|
||||
padding: 0px 10px;
|
||||
white-space: nowrap; /* 防止文字换行 */
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* 添加新的样式 */
|
||||
.description {
|
||||
text-align: center;
|
||||
margin-bottom: 40px;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
max-width: 600px;
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
.description p {
|
||||
margin: 8px 0;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.bottom-text {
|
||||
text-align: center;
|
||||
margin-top: 60px;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.bottom-text p {
|
||||
margin: 8px 0;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.sponsor-button {
|
||||
position: fixed;
|
||||
bottom: 40px;
|
||||
right: 40px;
|
||||
background: linear-gradient(45deg, #ff3366, #ff6b6b);
|
||||
color: white;
|
||||
padding: 12px 24px;
|
||||
border-radius: 30px;
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
box-shadow:
|
||||
0 4px 15px rgba(255, 51, 102, 0.3),
|
||||
0 2px 8px rgba(255, 51, 102, 0.2);
|
||||
transition: all 0.3s ease;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.sponsor-button:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow:
|
||||
0 6px 20px rgba(255, 51, 102, 0.4),
|
||||
0 3px 10px rgba(255, 51, 102, 0.3);
|
||||
}
|
||||
|
||||
.button-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.heart-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
fill: currentColor;
|
||||
animation: heartBeat 1.2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes heartBeat {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
}
|
||||
14% {
|
||||
transform: scale(1.3);
|
||||
}
|
||||
28% {
|
||||
transform: scale(1);
|
||||
}
|
||||
42% {
|
||||
transform: scale(1.3);
|
||||
}
|
||||
70% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* 添加响应式样式 */
|
||||
@media (max-width: 768px) {
|
||||
.sponsor-button {
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
padding: 10px 20px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.description,
|
||||
.bottom-text {
|
||||
padding: 0 20px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 添加悬浮状态的阴影效果 */
|
||||
.sponsor-avatar:hover {
|
||||
filter: drop-shadow(0 8px 12px rgba(0, 0, 0, 0.15));
|
||||
}
|
||||
|
||||
/* 修改激活状态的阴影效果 */
|
||||
.sponsor-avatar:has(.avatar-wrapper.active) {
|
||||
filter: drop-shadow(0 15px 25px rgba(0, 0, 0, 0.2));
|
||||
}
|
||||
</style>
|
||||
23
pnpm-lock.yaml
generated
23
pnpm-lock.yaml
generated
@@ -129,12 +129,18 @@ importers:
|
||||
element-plus:
|
||||
specifier: ^2.6.1
|
||||
version: 2.9.5(vue@3.5.13(typescript@5.8.2))
|
||||
gsap:
|
||||
specifier: ^3.12.7
|
||||
version: 3.12.7
|
||||
pinia:
|
||||
specifier: ^2.1.7
|
||||
version: 2.3.1(typescript@5.8.2)(vue@3.5.13(typescript@5.8.2))
|
||||
socket.io-client:
|
||||
specifier: ^4.8.1
|
||||
version: 4.8.1
|
||||
typeit:
|
||||
specifier: ^8.8.7
|
||||
version: 8.8.7
|
||||
vant:
|
||||
specifier: ^4.9.17
|
||||
version: 4.9.17(vue@3.5.13(typescript@5.8.2))
|
||||
@@ -1306,6 +1312,9 @@ packages:
|
||||
'@types/validator@13.12.2':
|
||||
resolution: {integrity: sha512-6SlHBzUW8Jhf3liqrGGXyTJSIFe4nqlJ5A5KaMZ2l/vbM3Wh3KSybots/wfWVzNLK4D1NZluDlSQIbIEPx6oyA==}
|
||||
|
||||
'@types/web-animations-js@2.2.16':
|
||||
resolution: {integrity: sha512-ATELeWMFwj8eQiH0KmvsCl1V2lu/qx/CjOBmv4ADSZS5u8r4reMyjCXtxG7khqyiwH3IOMNdrON/Ugn94OUcRA==}
|
||||
|
||||
'@types/web-bluetooth@0.0.16':
|
||||
resolution: {integrity: sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==}
|
||||
|
||||
@@ -2348,6 +2357,9 @@ packages:
|
||||
graphemer@1.4.0:
|
||||
resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
|
||||
|
||||
gsap@3.12.7:
|
||||
resolution: {integrity: sha512-V4GsyVamhmKefvcAKaoy0h6si0xX7ogwBoBSs2CTJwt7luW0oZzC0LhdkyuKV8PJAXr7Yaj8pMjCKD4GJ+eEMg==}
|
||||
|
||||
has-bigints@1.1.0:
|
||||
resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -3800,6 +3812,9 @@ packages:
|
||||
resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
typeit@8.8.7:
|
||||
resolution: {integrity: sha512-sSVpy+cjeFP6Z+fZqiHzUSShg5yYFeJEt/Qut/bX945+Axyq+Yq+GPOuuk+sofoccSv8nNX/ibOOHkbki2mEpg==}
|
||||
|
||||
typescript@5.8.2:
|
||||
resolution: {integrity: sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==}
|
||||
engines: {node: '>=14.17'}
|
||||
@@ -5363,6 +5378,8 @@ snapshots:
|
||||
|
||||
'@types/validator@13.12.2': {}
|
||||
|
||||
'@types/web-animations-js@2.2.16': {}
|
||||
|
||||
'@types/web-bluetooth@0.0.16': {}
|
||||
|
||||
'@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.8.2))(eslint@8.57.1)(typescript@5.8.2)':
|
||||
@@ -6696,6 +6713,8 @@ snapshots:
|
||||
|
||||
graphemer@1.4.0: {}
|
||||
|
||||
gsap@3.12.7: {}
|
||||
|
||||
has-bigints@1.1.0: {}
|
||||
|
||||
has-flag@3.0.0: {}
|
||||
@@ -8289,6 +8308,10 @@ snapshots:
|
||||
possible-typed-array-names: 1.1.0
|
||||
reflect.getprototypeof: 1.0.10
|
||||
|
||||
typeit@8.8.7:
|
||||
dependencies:
|
||||
'@types/web-animations-js': 2.2.16
|
||||
|
||||
typescript@5.8.2: {}
|
||||
|
||||
ufo@1.5.4: {}
|
||||
|
||||
Reference in New Issue
Block a user