mirror of
https://github.com/jiangrui1994/CloudSaver.git
synced 2026-01-09 14:48:47 +08:00
fix:去除悬浮效果
This commit is contained in:
@@ -109,46 +109,6 @@ let currentHoverIndex = null;
|
|||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await getSponsors();
|
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({
|
const tl = gsap.timeline({
|
||||||
@@ -161,33 +121,30 @@ onMounted(async () => {
|
|||||||
opacity: 0,
|
opacity: 0,
|
||||||
duration: 0.6,
|
duration: 0.6,
|
||||||
stagger: {
|
stagger: {
|
||||||
amount: 0.3, // 所有元素在0.3秒内完成错开动画
|
amount: 0.3,
|
||||||
from: "start",
|
from: "start",
|
||||||
},
|
},
|
||||||
ease: "back.out(1.2)",
|
ease: "back.out(1.2)",
|
||||||
onComplete: () => {
|
|
||||||
// 动画完成后启动浮动动画
|
|
||||||
avatarTimelines.value.forEach((timeline) => {
|
|
||||||
if (timeline) {
|
|
||||||
timeline.progress(Math.random()).play();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 背景圆圈动画
|
// 添加可见性变化监听
|
||||||
gsap.to(".gradient-circle", {
|
document.addEventListener("visibilitychange", handleVisibilityChange);
|
||||||
rotation: 360,
|
|
||||||
duration: 20,
|
// 添加窗口失焦事件处理
|
||||||
repeat: -1,
|
window.addEventListener("blur", handleMouseLeave);
|
||||||
ease: "none",
|
|
||||||
stagger: {
|
|
||||||
each: 5,
|
|
||||||
from: "random",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 修改 handleVisibilityChange 函数
|
||||||
|
const handleVisibilityChange = () => {
|
||||||
|
if (document.hidden) {
|
||||||
|
// 页面不可见时清理资源
|
||||||
|
if (typeItInstance) {
|
||||||
|
typeItInstance.destroy();
|
||||||
|
typeItInstance = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 修改鼠标移入处理函数
|
// 修改鼠标移入处理函数
|
||||||
const handleMouseEnter = (() => {
|
const handleMouseEnter = (() => {
|
||||||
let timeout;
|
let timeout;
|
||||||
@@ -262,29 +219,24 @@ const updateAvatarsEffect = (activeIndex) => {
|
|||||||
scale: 1.2,
|
scale: 1.2,
|
||||||
y: -15,
|
y: -15,
|
||||||
zIndex: 10,
|
zIndex: 10,
|
||||||
duration: 0.5,
|
duration: 0.2,
|
||||||
ease: "back.out(1.7)",
|
ease: "back.out(1.5)",
|
||||||
force3D: true,
|
force3D: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
// 增强激活头像的阴影
|
|
||||||
gsap.to(avatarContainer, {
|
gsap.to(avatarContainer, {
|
||||||
filter: "drop-shadow(0 20px 30px rgba(0, 0, 0, 0.25))",
|
filter: "drop-shadow(0 20px 30px rgba(0, 0, 0, 0.25))",
|
||||||
duration: 0.5,
|
duration: 0.2,
|
||||||
});
|
});
|
||||||
|
|
||||||
const activeOverlay = wrapper.querySelector(".avatar-overlay");
|
const activeOverlay = wrapper.querySelector(".avatar-overlay");
|
||||||
gsap.to(activeOverlay, {
|
gsap.to(activeOverlay, {
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
duration: 0.3,
|
duration: 0.15,
|
||||||
onComplete: () => {
|
|
||||||
activeOverlay.style.background = "none";
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取当前头像的位置
|
|
||||||
const rect = wrapper.getBoundingClientRect();
|
const rect = wrapper.getBoundingClientRect();
|
||||||
const centerX = rect.left + rect.width / 2;
|
const centerX = rect.left + rect.width / 2;
|
||||||
const centerY = rect.top + rect.height / 2;
|
const centerY = rect.top + rect.height / 2;
|
||||||
@@ -292,34 +244,41 @@ const updateAvatarsEffect = (activeIndex) => {
|
|||||||
const deltaY = activeCenter.value.y - centerY;
|
const deltaY = activeCenter.value.y - centerY;
|
||||||
const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
|
||||||
|
|
||||||
// 计算效果强度
|
if (distance < 0.1) return;
|
||||||
const maxDistance = 800;
|
|
||||||
const strength = Math.max(0, 1 - distance / maxDistance);
|
|
||||||
const attractionCurve = Math.pow(strength, 1.2);
|
|
||||||
|
|
||||||
// 计算阴影偏移和强度
|
const maxDistance = 400;
|
||||||
|
const strength = Math.max(0, 1 - distance / maxDistance);
|
||||||
|
|
||||||
|
// 计算吸引力效果
|
||||||
|
const attractionStrength = Math.pow(strength, 1.5);
|
||||||
|
const moveX = (deltaX / distance) * 30 * attractionStrength;
|
||||||
|
const moveY = (deltaY / distance) * 30 * attractionStrength;
|
||||||
|
|
||||||
|
// 计算旋转角度
|
||||||
|
const rotateX = -Math.atan2(deltaY, distance) * (180 / Math.PI) * strength;
|
||||||
|
const rotateY = Math.atan2(deltaX, distance) * (180 / Math.PI) * strength;
|
||||||
|
|
||||||
|
// 应用变换效果
|
||||||
|
gsap.to(inner, {
|
||||||
|
scale: 1 + 0.05 * strength,
|
||||||
|
x: moveX,
|
||||||
|
y: moveY,
|
||||||
|
rotationX: rotateX,
|
||||||
|
rotationY: rotateY,
|
||||||
|
duration: 0.2,
|
||||||
|
ease: "power2.out",
|
||||||
|
force3D: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 更新阴影效果
|
||||||
const shadowOffsetX = (deltaX / distance) * 15 * strength;
|
const shadowOffsetX = (deltaX / distance) * 15 * strength;
|
||||||
const shadowOffsetY = Math.max(6, (deltaY / distance) * 20 * strength + 6);
|
const shadowOffsetY = Math.max(6, (deltaY / distance) * 20 * strength + 6);
|
||||||
const shadowBlur = 12 + 18 * strength;
|
const shadowBlur = 12 + 18 * strength;
|
||||||
const shadowOpacity = 0.15 + 0.1 * strength;
|
const shadowOpacity = 0.15 + 0.1 * strength;
|
||||||
|
|
||||||
// 更新阴影效果
|
|
||||||
gsap.to(avatarContainer, {
|
gsap.to(avatarContainer, {
|
||||||
filter: `drop-shadow(${shadowOffsetX}px ${shadowOffsetY}px ${shadowBlur}px rgba(0, 0, 0, ${shadowOpacity}))`,
|
filter: `drop-shadow(${shadowOffsetX}px ${shadowOffsetY}px ${shadowBlur}px rgba(0, 0, 0, ${shadowOpacity}))`,
|
||||||
duration: 0.5,
|
duration: 0.2,
|
||||||
});
|
|
||||||
|
|
||||||
// 更新变换效果
|
|
||||||
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,
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -333,96 +292,47 @@ const handleMouseLeave = () => {
|
|||||||
|
|
||||||
if (!avatarWrapperRefs.value) return;
|
if (!avatarWrapperRefs.value) return;
|
||||||
|
|
||||||
// 立即停止所有正在进行的动画
|
|
||||||
avatarWrapperRefs.value.forEach((wrapper) => {
|
avatarWrapperRefs.value.forEach((wrapper) => {
|
||||||
const inner = wrapper.querySelector(".avatar-inner");
|
const inner = wrapper.querySelector(".avatar-inner");
|
||||||
gsap.killTweensOf(inner);
|
if (inner) {
|
||||||
});
|
gsap.killTweensOf(inner);
|
||||||
|
|
||||||
// 重置所有头像状态
|
gsap.to(inner, {
|
||||||
resetAllAvatars();
|
scale: 1,
|
||||||
};
|
y: 0,
|
||||||
|
x: 0,
|
||||||
// 添加重置所有头像的函数
|
|
||||||
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,
|
rotation: 0,
|
||||||
duration: 1.5,
|
rotationX: 0,
|
||||||
overwrite: "auto",
|
rotationY: 0,
|
||||||
})
|
duration: 0.2,
|
||||||
.to(inner, {
|
ease: "power2.out",
|
||||||
yPercent: 10,
|
|
||||||
rotation: -2,
|
|
||||||
duration: 1.5,
|
|
||||||
overwrite: "auto",
|
|
||||||
})
|
|
||||||
.to(inner, {
|
|
||||||
yPercent: 0,
|
|
||||||
rotation: 0,
|
|
||||||
duration: 1.5,
|
|
||||||
overwrite: "auto",
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
avatarTimelines.value[index] = tl;
|
const avatarContainer = wrapper.closest(".sponsor-avatar");
|
||||||
tl.progress(Math.random()).play();
|
if (avatarContainer) {
|
||||||
|
gsap.killTweensOf(avatarContainer);
|
||||||
|
|
||||||
|
gsap.to(avatarContainer, {
|
||||||
|
filter: "drop-shadow(0 6px 12px rgba(0, 0, 0, 0.15))",
|
||||||
|
duration: 0.2,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const overlayElement = wrapper.querySelector(".avatar-overlay");
|
||||||
|
if (overlayElement) {
|
||||||
|
gsap.to(overlayElement, {
|
||||||
|
opacity: 1,
|
||||||
|
duration: 0.15,
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
|
||||||
|
|
||||||
// 添加窗口失焦事件处理
|
if (typeItInstance) {
|
||||||
window.addEventListener("blur", handleMouseLeave);
|
typeItInstance.destroy();
|
||||||
|
typeItInstance = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 添加点击处理函数
|
// 添加点击处理函数
|
||||||
const handleAvatarClick = (link) => {
|
const handleAvatarClick = (link) => {
|
||||||
@@ -434,16 +344,12 @@ const handleAvatarClick = (link) => {
|
|||||||
// 组件卸载时清理
|
// 组件卸载时清理
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
window.removeEventListener("blur", handleMouseLeave);
|
window.removeEventListener("blur", handleMouseLeave);
|
||||||
// 清理动画时间轴
|
document.removeEventListener("visibilitychange", handleVisibilityChange);
|
||||||
avatarTimelines.value.forEach((timeline) => {
|
|
||||||
if (timeline) {
|
|
||||||
timeline.kill();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
avatarTimelines.value = [];
|
|
||||||
|
|
||||||
|
// 清理打字实例
|
||||||
if (typeItInstance) {
|
if (typeItInstance) {
|
||||||
typeItInstance.destroy();
|
typeItInstance.destroy();
|
||||||
|
typeItInstance = null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -608,18 +514,15 @@ const handleButtonLeave = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.avatar-inner {
|
.avatar-inner {
|
||||||
width: 100%;
|
width: 100% !important;
|
||||||
height: 100%;
|
height: 100% !important;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
border: 4px solid #ffffff;
|
border: 4px solid #ffffff;
|
||||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||||
transition:
|
transition:
|
||||||
transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1),
|
transform 0.2s ease,
|
||||||
filter 0.3s ease;
|
filter 0.2s ease;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
will-change: transform;
|
|
||||||
backface-visibility: hidden;
|
|
||||||
transform: translateZ(0);
|
|
||||||
position: relative;
|
position: relative;
|
||||||
isolation: isolate;
|
isolation: isolate;
|
||||||
transform-style: preserve-3d;
|
transform-style: preserve-3d;
|
||||||
@@ -647,9 +550,8 @@ const handleButtonLeave = () => {
|
|||||||
mix-blend-mode: overlay; /* 添加混合模式增强效果 */
|
mix-blend-mode: overlay; /* 添加混合模式增强效果 */
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatar-wrapper.active {
|
.avatar-wrapper.active .avatar-inner {
|
||||||
transform: scale(1.2) translateY(-10px);
|
transform: scale(1.2) translateY(-10px);
|
||||||
z-index: 10;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatar-wrapper.has-link {
|
.avatar-wrapper.has-link {
|
||||||
@@ -732,7 +634,7 @@ const handleButtonLeave = () => {
|
|||||||
min-width: 180px;
|
min-width: 180px;
|
||||||
z-index: 111;
|
z-index: 111;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
animation: dialogFadeIn 0.4s cubic-bezier(0.16, 1, 0.3, 1) forwards;
|
animation: dialogFadeIn 0.25s cubic-bezier(0.16, 1, 0.3, 1) forwards;
|
||||||
border: 1px solid rgba(0, 0, 0, 0.05);
|
border: 1px solid rgba(0, 0, 0, 0.05);
|
||||||
will-change: transform, opacity;
|
will-change: transform, opacity;
|
||||||
backface-visibility: hidden;
|
backface-visibility: hidden;
|
||||||
@@ -774,8 +676,8 @@ const handleButtonLeave = () => {
|
|||||||
@keyframes dialogFadeIn {
|
@keyframes dialogFadeIn {
|
||||||
0% {
|
0% {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateX(-50%) translateY(20px) scale(0.95);
|
transform: translateX(-50%) translateY(10px) scale(0.98);
|
||||||
filter: blur(2px);
|
filter: blur(1px);
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
|||||||
Reference in New Issue
Block a user