修改样式
This commit is contained in:
@@ -203,8 +203,8 @@ html {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.box_3 {
|
.box_3 {
|
||||||
background-color: rgba(25, 140, 255, 0.1);
|
/* 跟留资/安全一致:Slice_6 本身就是带缺角的"花瓣圆",不再额外垫浅色背景 + 圆角,
|
||||||
border-radius: 18px;
|
否则浅色方块的四个圆角会透出来,被误认为是图标的"四个角" */
|
||||||
height: 1.814rem;
|
height: 1.814rem;
|
||||||
width: 1.814rem;
|
width: 1.814rem;
|
||||||
}
|
}
|
||||||
@@ -277,8 +277,8 @@ html {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.block_2 {
|
.block_2 {
|
||||||
background-color: rgba(255, 165, 39, 0.1);
|
/* 跟留资/安全一致:Slice_7 本身就是带缺角的"花瓣圆",不再额外垫浅色背景 + 圆角,
|
||||||
border-radius: 18px;
|
否则浅色方块的四个圆角会透出来,被误认为是图标的"四个角" */
|
||||||
height: 1.814rem;
|
height: 1.814rem;
|
||||||
width: 1.814rem;
|
width: 1.814rem;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,11 +13,11 @@
|
|||||||
./assets/img/slice-3-black-3x.png 3x
|
./assets/img/slice-3-black-3x.png 3x
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
<span class="text_1 nav-link" data-spy="top" @click="scrollTo('top')">首页</span>
|
<span class="text_1 nav-link" :class="{ 'nav-active': activeSection === 'top' }" data-spy="top" @click="scrollTo('top')">首页</span>
|
||||||
<span class="text_2 nav-link" data-spy="about" @click="scrollTo('about')">WithMind</span>
|
<span class="text_2 nav-link" :class="{ 'nav-active': activeSection === 'about' }" data-spy="about" @click="scrollTo('about')">WithMind</span>
|
||||||
<span class="text_3 nav-link" data-spy="skills" @click="scrollTo('skills')">Skills</span>
|
<span class="text_3 nav-link" :class="{ 'nav-active': activeSection === 'skills' }" data-spy="skills" @click="scrollTo('skills')">Skills</span>
|
||||||
<span class="text_4 nav-link" data-spy="platform" @click="scrollTo('platform')">创作平台</span>
|
<span class="text_4 nav-link" :class="{ 'nav-active': activeSection === 'platform' }" data-spy="platform" @click="scrollTo('platform')">创作平台</span>
|
||||||
<span class="text_5 nav-link" data-spy="gateway" @click="scrollTo('gateway')">模型网关</span>
|
<span class="text_5 nav-link" :class="{ 'nav-active': activeSection === 'gateway' }" data-spy="gateway" @click="scrollTo('gateway')">模型网关</span>
|
||||||
<div class="group_3 flex-col"></div>
|
<div class="group_3 flex-col"></div>
|
||||||
</div>
|
</div>
|
||||||
<img
|
<img
|
||||||
@@ -530,67 +530,110 @@ export default {
|
|||||||
this.initScrollSpy();
|
this.initScrollSpy();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
/* scroll-spy:用 IntersectionObserver 监听每个 section,
|
/* scroll-spy:用 Vue 响应式 data(activeSection) 驱动 nav-link 的 :class 绑定。
|
||||||
进入视口时把对应 nav 链接加 .nav-active 类(蓝色高亮) */
|
关键设计:点击 nav-link 后,锁定 activeSection = id,屏蔽一切自动 update,
|
||||||
|
直到平滑滚动结束再校准。这样彻底避免"点击瞬间设的 activeSection
|
||||||
|
被 IO/scroll 回调里的 compute() 改掉"的竞态。 */
|
||||||
initScrollSpy() {
|
initScrollSpy() {
|
||||||
const sections = ['top', 'about', 'skills', 'platform', 'agent', 'media', 'gateway'];
|
const sectionIds = ['about', 'skills', 'platform', 'agent', 'media', 'gateway'];
|
||||||
const navLinks = document.querySelectorAll('.section_2 [data-spy]');
|
|
||||||
|
|
||||||
const observer = new IntersectionObserver(
|
// 单一 compute:section 顶 ≤ 视口高度的 50%(即"section 顶已过视口中线")就算激活
|
||||||
(entries) => {
|
// 这避开 about 段高度只有 ~144px 的边界问题:
|
||||||
/* 多个 entry 同时触发时,选"离顶部最近"的那一个作为 active */
|
// 点 skills 后,skills 顶 ≈ 260px < 375px(50% vh),platform 顶 > 375 → 选 skills
|
||||||
let best = null;
|
// 而 about 顶 ≈ 116px 也 ≤ 375,但 for 循环从上到下遍历,后一个 skills 覆盖前一个
|
||||||
entries.forEach((entry) => {
|
const compute = () => {
|
||||||
if (entry.isIntersecting) {
|
const scrollY = window.scrollY || window.pageYOffset || 0;
|
||||||
if (!best || entry.boundingClientRect.top < best.boundingClientRect.top) {
|
if (scrollY < 100) return 'top';
|
||||||
best = entry;
|
const vh = window.innerHeight || document.documentElement.clientHeight;
|
||||||
}
|
const trigger = vh * 0.5;
|
||||||
}
|
let activeId = 'top';
|
||||||
});
|
for (const id of sectionIds) {
|
||||||
if (best) {
|
const el = document.getElementById(id);
|
||||||
const id = best.target.id;
|
if (!el) continue;
|
||||||
this.activeSection = id;
|
const top = el.getBoundingClientRect().top;
|
||||||
/* 同步 DOM class(因为 nav-link class 在外部 CSS 不可用,直接 toggle class) */
|
if (top <= trigger) {
|
||||||
navLinks.forEach((link) => {
|
activeId = id;
|
||||||
if (link.getAttribute('data-spy') === id) {
|
} else {
|
||||||
link.classList.add('nav-active');
|
break;
|
||||||
} else {
|
|
||||||
link.classList.remove('nav-active');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
/* 视口上 30% / 下 40% 区域作为"激活区",让 section 滚到屏幕上半部时高亮 */
|
|
||||||
rootMargin: '-30% 0px -40% 0px',
|
|
||||||
threshold: 0
|
|
||||||
}
|
}
|
||||||
);
|
return activeId;
|
||||||
|
};
|
||||||
|
|
||||||
sections.forEach((id) => {
|
const update = () => {
|
||||||
|
// 抑制期(scrollTo 触发的平滑滚动过程中)不响应任何 update
|
||||||
|
if (Date.now() < (this.scrollSuppressUntil || 0)) return;
|
||||||
|
this.activeSection = compute();
|
||||||
|
};
|
||||||
|
|
||||||
|
// IO 兜底:section 进入/退出视口时重算
|
||||||
|
const observer = new IntersectionObserver(() => update(), {
|
||||||
|
rootMargin: '0px 0px -50% 0px',
|
||||||
|
threshold: 0
|
||||||
|
});
|
||||||
|
sectionIds.forEach((id) => {
|
||||||
const el = document.getElementById(id);
|
const el = document.getElementById(id);
|
||||||
if (el) observer.observe(el);
|
if (el) observer.observe(el);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let ticking = false;
|
||||||
|
window.addEventListener('scroll', () => {
|
||||||
|
if (ticking) return;
|
||||||
|
ticking = true;
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
ticking = false;
|
||||||
|
update();
|
||||||
|
});
|
||||||
|
}, { passive: true });
|
||||||
|
|
||||||
|
update();
|
||||||
},
|
},
|
||||||
scrollTo(id) {
|
scrollTo(id) {
|
||||||
|
// 1) 立即同步响应式状态(不等滚动动画)
|
||||||
|
this.activeSection = id;
|
||||||
|
// 2) 抑制一切自动 update 1.2s(覆盖 smooth 滚动动画)
|
||||||
|
const suppressMs = 1200;
|
||||||
|
this.scrollSuppressUntil = Date.now() + suppressMs;
|
||||||
|
// 3) 抑制期结束后,按当前 scrollY 校准一次
|
||||||
|
setTimeout(() => {
|
||||||
|
this.scrollSuppressUntil = 0; // 解除抑制
|
||||||
|
// 此时页面已稳定,scrollY 是最终值,compute() 必然正确
|
||||||
|
this.activeSection = (() => {
|
||||||
|
const sectionIds = ['about', 'skills', 'platform', 'agent', 'media', 'gateway'];
|
||||||
|
const scrollY = window.scrollY || window.pageYOffset || 0;
|
||||||
|
if (scrollY < 100) return 'top';
|
||||||
|
const vh = window.innerHeight || document.documentElement.clientHeight;
|
||||||
|
const trigger = vh * 0.5;
|
||||||
|
let activeId = 'top';
|
||||||
|
for (const sid of sectionIds) {
|
||||||
|
const el = document.getElementById(sid);
|
||||||
|
if (!el) continue;
|
||||||
|
const top = el.getBoundingClientRect().top;
|
||||||
|
if (top <= trigger) {
|
||||||
|
activeId = sid;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return activeId;
|
||||||
|
})();
|
||||||
|
}, suppressMs);
|
||||||
|
// 4) 触发滚动
|
||||||
if (id === 'top') {
|
if (id === 'top') {
|
||||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const el = document.getElementById(id);
|
const el = document.getElementById(id);
|
||||||
if (el) {
|
if (el) {
|
||||||
// 模型网关特殊处理:滚到网关模块底部,避免露出 footer 黑色背景
|
|
||||||
if (id === 'gateway') {
|
if (id === 'gateway') {
|
||||||
// 让网关底部对齐到"视口底部上方 250px"位置,留更大余量,footer 黑色背景完全在视口外
|
|
||||||
const rect = el.getBoundingClientRect();
|
const rect = el.getBoundingClientRect();
|
||||||
const elBottom = rect.bottom + window.pageYOffset;
|
const elBottom = rect.bottom + window.pageYOffset;
|
||||||
const target = elBottom - window.innerHeight + 250;
|
const target = elBottom - window.innerHeight + 250;
|
||||||
window.scrollTo({ top: Math.max(0, target), behavior: 'smooth' });
|
window.scrollTo({ top: Math.max(0, target), behavior: 'smooth' });
|
||||||
return;
|
} else {
|
||||||
|
const top = el.getBoundingClientRect().top + window.pageYOffset - 260;
|
||||||
|
window.scrollTo({ top: top, behavior: 'smooth' });
|
||||||
}
|
}
|
||||||
// 顶部 fixed 导航栏高度 2.134rem(80px),加 180px 余量,共 260px 偏移,完全避开导航遮住
|
|
||||||
const top = el.getBoundingClientRect().top + window.pageYOffset - 260;
|
|
||||||
window.scrollTo({ top: top, behavior: 'smooth' });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user